如果结构包含 DateTime 字段,为什么 LayoutKind.Sequential 的工作方式不同?
考虑以下代码(必须在启用“不安全”的情况下编译的控制台应用程序):
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;
}
}
现在,如果我运行上面的代码,我会得到类似于以下的输出:
结构的地址 = 40F2CC
第一个地址 = 40F2D4
NotFirst 的地址 = 40F2CC
请注意,First 的地址与结构的地址不同;但是,NotFirst 的地址与结构的地址相同。
现在注释掉结构中的“DateTime WTF”字段,然后再次运行它。 这一次,我得到类似这样的输出:
结构的地址 = 15F2E0
第一个地址 = 15F2E0
NotFirst 的地址 = 15F2E8
现在“First”确实与结构具有相同的地址。
鉴于使用 LayoutKind.Sequential,我发现这种行为令人惊讶。任何人都可以提供解释吗?在与使用 Com DATETIME 类型的 C/C++ 结构进行互操作时,此行为是否会产生任何影响?
[编辑] 注意:我已验证当您使用 Marshal.StructureToPtr() 编码结构时,数据是以正确的顺序编码,“第一个”字段是第一个。这似乎表明它可以与互操作一起正常工作。神秘之处在于内部布局为何会发生变化 - 当然,内部布局从未指定,因此编译器可以随心所欲。
[EDIT2] 从结构声明中删除了“不安全”(它是我正在做的一些测试遗留下来的)。
[EDIT3] 这个问题的原始来源来自 MSDN C# 论坛:
最佳答案
Why does LayoutKind.Sequential work differently if a struct contains a DateTime field?
它与(surprising) fact that DateTime
itself has layout "Auto" (link to SO question by myself)有关.此代码重现了您看到的行为:
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;
}
示例输出:
64-bit: True Layout of OneField: Auto Layout of Composite: Sequential Size of Composite: 24 L: 48F050 M: 48F048 N: 48F058
如果我们从 OneField
中删除该属性,事情就会按预期进行。示例:
64-bit: True Layout of OneField: Sequential Layout of Composite: Sequential Size of Composite: 24 L: 48F048 M: 48F050 N: 48F058
这些示例是使用 x64 平台编译的(因此大小 24,三乘以八,不足为奇),但对于 x86,我们也看到相同的“无序”指针地址。
所以我想我可以得出结论,OneField
的布局(在您的示例中为 DateTime
)会影响包含 OneField 的结构的布局
成员,即使该复合结构本身具有 Sequential
布局。我不确定这是否有问题(甚至是必需的)。
根据 Hans Passant 在另一个线程中的评论,当其中一个成员是 Auto
布局结构时,它不再尝试保持顺序。
关于c# - 如果结构包含 DateTime 字段,为什么 LayoutKind.Sequential 的工作方式不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4132533/