我尝试使用SA
构建结构([StructLayout(LayoutKind.Explicit)]
),其中的字段是另一个struct
(SB
)。
首先:我很惊讶我被允许声明没有[StructLayout(LayoutKind.Explicit)]
的其他结构,而在SA
中,所有字段必须拥有{ {1}},或者编译器会喊出来。这没什么意义。
第二次:似乎[FieldOffset(0)]
中的所有引用(object
)字段都移到了SB
的前面。
SB
注意:我不打算在生产代码中使用它。我主要是出于好奇而问这个问题。
:)
答案 0 :(得分:3)
这是编译器警告/错误的漏洞吗?
不,没有错。允许字段重叠,这就是为什么LayoutKind.Explicit首先存在的原因。它允许在非托管代码中声明等效的 union ,而C#中则不支持。你不能突然停止在结构声明中使用[FieldOffset],编译器坚持你在结构的所有成员上使用它。不是技术上必要的,而是一个避免错误假设的简单要求。
似乎SB中的所有参考(对象)字段都已移动
是的,这很正常。 CLR以无记录和不可发现的方式布置对象。它使用的确切规则没有记录,可能会有变化。它也不会重复不同的紧张情绪。在对象被封送后,布局不会变得可预测,Marshal.StructureToPtr()调用或由pinvoke编组器隐式调用。这是确切布局重要的唯一时间。我在this answer中写了关于这种行为的基本原理。
答案 1 :(得分:1)
第一个问题的答案是否定的,编译器的错误报告中没有漏洞或错误。如果您开始进行显式布局,编译器将假设您知道自己在做什么(在限制范围内 - 见下文)。你告诉它将一个结构覆盖在另一个结构之上。编译器不会(也不应该)关心你所覆盖的结构也没有明确规定。
如果编译器 非常关心,那么您将无法覆盖任何未明确布局的类型,这意味着在一般情况下您无法进行联合。例如,考虑尝试覆盖DateTime
和long
:
[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?。