本文介绍了.NET行为LayoutKind.explicit的字段本身就是一个结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用[StructLayout(LayoutKind.Explicit)]构建一个结构(SA),该结构的字段是另一个struct(SB).

I tried building a struct (SA) using [StructLayout(LayoutKind.Explicit)], which had a field which is another struct (SB).

第一:令我惊讶的是,我被允许声明没有[StructLayout(LayoutKind.Explicit)]的其他结构,而在SA中,所有字段必须都具有[FieldOffset(0)],或者编译器将大喊.这没有多大意义.

First: I was surprised I was allowed to declare that other struct without [StructLayout(LayoutKind.Explicit)], whereas in SA, all fields must have [FieldOffset(0)], or the compiler will shout. It doesn't make much sense.

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

第二:似乎SB中的所有引用(object)字段都移到了SB的前面.

Second: it seems that all reference (object) fields in SB are moved to the front of SB.

  • 这种行为在任何地方都有描述吗?
  • 它是否取决于实现?
  • 是否在任何依赖于实现的地方定义了它? :)

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

Note: I'm not intending to use this in production code. I ask this question mainly out of curiosity.

// No object fields in SB
// Gives the following layout (deduced from experimentation with the C# debugger):

// | f0 | f4 and i | f8 and j | f12 and k | f16 |

[StructLayout(LayoutKind.Explicit)]
struct SA {
    [FieldOffset(0)] int f0;
    [FieldOffset(4)] SB sb;
    [FieldOffset(4)] int f4;
    [FieldOffset(8)] int f8;
    [FieldOffset(12)] int f12;
    [FieldOffset(16)] int f16;
}
struct SB { int i; int j; int k; }

// One object field in SB
// Gives the following layout:

// | f0 | f4 and o1 | f8 and i | f12 and j | f16 and k |

// If I add an `object` field after `j` in `SB`, i *have* to convert
// `f4` to `object`, otherwise I get a `TypeLoadException`.
// No other field will do.

[StructLayout(LayoutKind.Explicit)]
struct SA {
    [FieldOffset(0)] int f0;
    [FieldOffset(4)] SB sb;
    [FieldOffset(4)] object f4;
    [FieldOffset(8)] int f8;
    [FieldOffset(12)] int f12;
    [FieldOffset(16)] int f16;
}
struct SB { int i; int j; object o1; int k; }

// Two `object` fields in `SB`
// Gives the following layout:

// | f0 | f4 and o1 | f8 and o2 | f12 and i | f16 and j | k |

// If I add another `object` field after the first one in `SB`, i *have* to convert
// `f8` to `object`, otherwise I get a `TypeLoadException`.
// No other field will do.

[StructLayout(LayoutKind.Explicit)]
struct SA {
    [FieldOffset(0)] int f0;
    [FieldOffset(4)] SB sb;
    [FieldOffset(4)] object f4;
    [FieldOffset(8)] object f8;
    [FieldOffset(12)] int f12;
    [FieldOffset(16)] int f16;
}
struct SB { int i; int j; object o1; object o2; int k; }

推荐答案

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

No, nothing wrong with it. Fields are allowed to overlap, this is why LayoutKind.Explicit exists in the first place. It allows declaring the equivalent of a union in unmanaged code, not otherwise supported in C#. You cannot suddenly stop using [FieldOffset] in a structure declaration, the runtime insist that you use it on all members of the struct. Not technically necessary but a simple requirement that avoids wrong assumptions.

是的,这很正常. CLR以无证件和无法发现的方式布置对象.它使用的确切规则未记录在案,并可能随时更改.对于不同的抖动也不会重复.直到将对象编组,Marshal.StructureToPtr()调用或通过pinvoke编组器隐式布局后,布局才变得可预测.这是唯一的确切布局问题.我在此答案中写了这种行为的基本原理.

Yes, this is normal. The CLR lays out objects in an undocumented and undiscoverable way. The exact rules it uses are not documented and subject to change. It also won't repeat for different jitters. Layout doesn't become predictable until the object is marshaled, Marshal.StructureToPtr() call or implicitly by the pinvoke marshaller. Which is the only time the exact layout matters. I wrote about the rationale for this behavior in this answer.

这篇关于.NET行为LayoutKind.explicit的字段本身就是一个结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 14:59