如果一个结构体包含一个DateTime字段,那么为什么LayoutKind.Sequential的工作方式不同? [英] Why does LayoutKind.Sequential work differently if a struct contains a DateTime field?

查看:110
本文介绍了如果一个结构体包含一个DateTime字段,那么为什么LayoutKind.Sequential的工作方式不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



如果一个结构体包含一个DateTime字段,LayoutKind.Sequential为什么不同?

考虑下面的代码(一个必须用不安全启用的控制台应用程序):

  using System; 
使用System.Runtime.InteropServices;

命名空间ConsoleApplication3
{
静态类程序
{
static void Main()
{
内部测试=新内部();

不安全
{
Console.WriteLine(Address of struct =+((int)& test).ToString(X));
Console.WriteLine(Address of First =+((int)& test.First).ToString(X));
Console.WriteLine(Address of NotFirst =+((int)& test.NotFirst).ToString(X));
}
}
}

[StructLayout(LayoutKind.Sequential)]
public struct Inner
{
public byte First ;
public double NotFirst;
public DateTime WTF;
}
}

现在,如果我运行上面的代码,我会得到输出类似于以下内容:



struct = 40F2CC的地址

First = 40F2D4的地址

NotFirst的地址= 40F2CC



请注意,First的地址与struct的地址不同;然而,NotFirst 的地址与结构的地址相同。



现在注释掉结构中的DateTime WTF字段,并再次运行。
这次,我得到类似的输出:



struct = 15F2E0的地址

First = 15F2E0的地址

NotFirst的地址= 15F2E8



现在,First 具有与结构体相同的地址。



鉴于使用LayoutKind.Sequential,我发现这个行为令人吃惊。任何人都可以提供解释吗?使用Com DATETIME类型与C / C ++结构进行互操作时,这种行为是否有任何后果?



注意:当我使用元帅时,我已经验证过。 StructureToPtr()来组织结构体,数据 以正确的顺序编组,First字段为第一个。这似乎表明它可以正常工作。这个谜是为什么内部布局发生变化的 - 但是当然内部布局也是没有指定的,所以编译器可以做任何事情。



从结构声明(这是从我正在做的一些测试中剩下的)。



这个问题的原始来源是MSDN C#论坛:



http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/fb84bf1d-d9b3-4e91-823e-988257504b30

解决方案


为什么如果struct包含DateTime字段,LayoutKind.Sequential的工作方式会不同?


它与(令人惊奇的)事实相关,即 DateTime 本身有布局自动(我自己链接到SO的问题)。这段代码重现了你看到的行为:

  static class Program 
{
static unsafe void Main
{
Console.WriteLine(64位:{0},Environment.Is64BitProcess);
Console.WriteLine(OneField的布局:{0},typeof(OneField).StructLayoutAttribute.Value);
Console.WriteLine(Layout of Composite:{0},typeof(Composite).StructLayoutAttribute.Value);
Console.WriteLine(Composite of Composite:{0},sizeof(Composite));
var local = default(Composite);
Console.WriteLine(L:{0:X},(long)(&(local.L)));
Console.WriteLine(M:{0:X},(long)(&(local.M)));
Console.WriteLine(N:{0:X},(long)(&(local.N)));
}
}

[StructLayout(LayoutKind.Auto)] //也尝试删除此属性
struct OneField
{
public long X;
}

struct复合//有布局顺序
{
public byte L;
public double M;
public OneField N;
}

样本输出:

 
64位:True
OneField的布局:Auto
复合的布局:顺序
复合的大小:24
L:48F050
M:48F048
N:48F058

如果我们从 OneField ,事情按预期方式行事。示例:

 
64位:True
OneField的布局:顺序
复合的布局:顺序
复合数量:24
L:48F048
M:48F050
N:48F058

例如,使用 x64 平台编译(所以大小为24,三十八,不奇怪),但是与x86我们也看到相同的无序指针地址。



所以我想我可以得出结论,在你的例子中, OneField (分别为 DateTime )对包含 OneField 成员的结构的布局有影响,即使该组合结构本身具有布局顺序。我不知道这是否有问题(甚至需要)。






根据Hans Passant在其他主题中的评论,其中一个成员是 Auto layout struct。


时,它不再尝试保持顺序

Why does LayoutKind.Sequential work differently if a struct contains a DateTime field?

Consider the following code (a console app which must be compiled with "unsafe" enabled):

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication3
{
    static class Program
    {
        static void Main()
        {
            Inner test = new Inner();

            unsafe
            {
                Console.WriteLine("Address of struct   = " + ((int)&test).ToString("X"));
                Console.WriteLine("Address of First    = " + ((int)&test.First).ToString("X"));
                Console.WriteLine("Address of NotFirst = " + ((int)&test.NotFirst).ToString("X"));
            }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct Inner
    {
        public byte First;
        public double NotFirst;
        public DateTime WTF;
    }
}

Now if I run the code above, I get output similar to the following:

Address of struct = 40F2CC
Address of First = 40F2D4
Address of NotFirst = 40F2CC

Note that the address of First is NOT the same as the address of the struct; however, the address of NotFirst is the same as the address of the struct.

Now comment out the "DateTime WTF" field in the struct, and run it again. This time, I get output similar to this:

Address of struct = 15F2E0
Address of First = 15F2E0
Address of NotFirst = 15F2E8

Now "First" does have the same address as the struct.

I find this behaviour surprising given the use of LayoutKind.Sequential. Can anyone provide an explanation? Does this behaviour have any ramifications when doing interop with C/C++ structs that use the Com DATETIME type?

[EDIT] NOTE: I have verified that when you use Marshal.StructureToPtr() to marshal the struct, the data is marshalled in the correct order, with the "First" field being first. This seems to suggest that it will work fine with interop. The mystery is why the internal layout changes - but of course, the internal layout is never specified, so the compiler can do what it likes.

[EDIT2] Removed "unsafe" from struct declaration (it was leftover from some testing I was doing).

[EDIT3] The original source for this question was from the MSDN C# forums:

http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/fb84bf1d-d9b3-4e91-823e-988257504b30

解决方案

Why does LayoutKind.Sequential work differently if a struct contains a DateTime field?

It is related to the (surprising) fact that DateTime itself has layout "Auto" (link to SO question by myself). This code reproduces the behavior you saw:

static class Program
{
    static unsafe void Main()
    {
        Console.WriteLine("64-bit: {0}", Environment.Is64BitProcess);
        Console.WriteLine("Layout of OneField: {0}", typeof(OneField).StructLayoutAttribute.Value);
        Console.WriteLine("Layout of Composite: {0}", typeof(Composite).StructLayoutAttribute.Value);
        Console.WriteLine("Size of Composite: {0}", sizeof(Composite));
        var local = default(Composite);
        Console.WriteLine("L: {0:X}", (long)(&(local.L)));
        Console.WriteLine("M: {0:X}", (long)(&(local.M)));
        Console.WriteLine("N: {0:X}", (long)(&(local.N)));
    }
}

[StructLayout(LayoutKind.Auto)]  // also try removing this attribute
struct OneField
{
    public long X;
}

struct Composite   // has layout Sequential
{
    public byte L;
    public double M;
    public OneField N;
}

Sample output:

64-bit: True
Layout of OneField: Auto
Layout of Composite: Sequential
Size of Composite: 24
L: 48F050
M: 48F048
N: 48F058

If we remove the attribute from OneField, things behave as expected. Example:

64-bit: True
Layout of OneField: Sequential
Layout of Composite: Sequential
Size of Composite: 24
L: 48F048
M: 48F050
N: 48F058

These example are with x64 platform compilation (so the size 24, three times eight, is unsurprising), but also with x86 we see the same "disordered" pointer addresses.

So I guess I can conclude that the layout of OneField (resp. DateTime in your example) has influence on the layout of the struct containing a OneField member even if that composite struct itself has layout Sequential. I am not sure if this is problematic (or even required).


According to comment by Hans Passant in the other thread, it no longer makes an attempt to keep it sequential when one of the members is an Auto layout struct.

这篇关于如果一个结构体包含一个DateTime字段,那么为什么LayoutKind.Sequential的工作方式不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆