LayoutKind.explicit的.NET行为,对于一个本身就是结构的字段

时间:2013-03-13 17:15:50

标签: c# .net struct offset layoutkind.explicit

问题

我尝试使用SA构建结构([StructLayout(LayoutKind.Explicit)]),其中的字段是另一个structSB)。

首先:我很惊讶我被允许声明没有[StructLayout(LayoutKind.Explicit)]的其他结构,而在SA中,所有字段必须拥有{ {1}},或者编译器会喊出来。这没什么意义。

  • 这是编译器警告/错误的漏洞吗?

第二次:似乎[FieldOffset(0)]中的所有引用(object)字段都移到了SB的前面。

  • 这种行为是在任何地方描述的吗?
  • 是否依赖于实现?
  • 它是否定义为依赖于实现的任何地方? SB

注意:我不打算在生产代码中使用它。我主要是出于好奇而问这个问题。

实验

:)

2 个答案:

答案 0 :(得分:3)

  

这是编译器警告/错误的漏洞吗?

不,没有错。允许字段重叠,这就是为什么LayoutKind.Explicit首先存在的原因。它允许在非托管代码中声明等效的 union ,而C#中则不支持。你不能突然停止在结构声明中使用[FieldOffset],编译器坚持你在结构的所有成员上使用它。不是技术上必要的,而是一个避免错误假设的简单要求。

  

似乎SB中的所有参考(对象)字段都已移动

是的,这很正常。 CLR以无记录和不可发现的方式布置对象。它使用的确切规则没有记录,可能会有变化。它也不会重复不同的紧张情绪。在对象被封送后,布局不会变得可预测,Marshal.StructureToPtr()调用或由pinvoke编组器隐式调用。这是确切布局重要的唯一时间。我在this answer中写了关于这种行为的基本原理。

答案 1 :(得分:1)

第一个问题的答案是否定的,编译器的错误报告中没有漏洞或错误。如果您开始进行显式布局,编译器将假设您知道自己在做什么(在限制范围内 - 见下文)。你告诉它将一个结构覆盖在另一个结构之上。编译器不会(也不应该)关心你所覆盖的结构也没有明确规定。

如果编译器 非常关心,那么您将无法覆盖任何未明确布局的类型,这意味着在一般情况下您无法进行联合。例如,考虑尝试覆盖DateTimelong

[StructLayout(LayoutKind.Explicit)]
struct MyUnion
{
    [FieldOffset(0)]
    public bool IsDate;
    [FieldOffset(1)]
    public DateTime dt;
    [FieldOffset(1)]
    public long counter;
}

除非明确列出DateTime,否则无法编译。可能不是你想要的。

只要将引用类型放在明确布局的结构中,您的结果就会......可能不是您所期望的。例如,考虑一下这个简单的一点:

struct MyUnion
{
    [FieldOffset(0)]
    public object o1;
    [FieldOffset(0)]
    public SomeRefType o2;
}

这大大违反了类型安全。如果它编译(很可能),当你尝试使用它时,它会因TypeLoadException而死。

编译器会尽可能防止您违反类型安全。我不知道编译器是否知道如何处理这些属性并布局结构,或者它是否只是通过生成的MSIL将布局信息传递给运行时。可能是后者,考虑到你的第二个例子,编译器允许特定布局,但运行时被TypeLoadException轰炸。

Google搜索[structlayout.explicit引用类型]揭示了一些有趣的讨论。例如,请参阅Overlaying several CLR reference fields with each other in explicit struct?