c# - 如果结构包含 DateTime 字段,为什么 LayoutKind.Sequential 的工作方式不同?

标签 c# datetime marshalling structlayout

如果结构包含 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# 论坛:

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?

它与(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/

相关文章:

c# - ADO 存储过程缺少参数错误...

python - SQLAlchemy 返回字符串而不是日期时间对象

Python pandas 从日期时间数组生成一个月的第一天

java - 具有不同 namespace 的 JAXB 片段

json - 实现实体组件系统的最佳方式是什么

c# - 在类方法中使用通用列表的字段

c# - 为什么这个正则表达式在最后一场比赛中失败

c# - 如何使用 jQuery 将 JSON 数据绑定(bind)到 Asp.net MVC 中的下拉列表

sql - 在 MS SQL/SQL Server 2005 中精确限制日期范围

java - 自定义 map <Object,Object> XmlAdapter