如果要查看实际异常,则需要64位机器。我已经创建了一些可以解决问题的虚拟类。
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class InnerType
{
char make;
char model;
UInt16 series;
}
[StructLayout(LayoutKind.Explicit)]
public class OutterType
{
[FieldOffset(0)]
char blah;
[FieldOffset(1)]
char blah2;
[FieldOffset(2)]
UInt16 blah3;
[FieldOffset(4)]
InnerType details;
}
class Program
{
static void Main(string[] args)
{
var t = new OutterType();
Console.ReadLine();
}
}
如果我在64 clr上运行它,我会收到一个类型加载异常,
System.TypeLoadException was unhandled
Message="Could not load type 'Sample.OutterType' from assembly 'Sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field."
如果我将目标cpu强制为32,它可以正常工作。
此外,如果我将InnerType从类更改为结构,它也可以。有人可以解释发生了什么或我做错了什么?
感谢
答案 0 :(得分:20)
关于重叠类型的部分在这里会产生误导。问题是.Net引用类型必须始终在指针大小边界上对齐。您的联合在x86中工作,因为字段偏移量是4个字节,这是32位系统的指针大小,但在x64上失败,因为它必须偏移8的倍数。如果将偏移量设置为3,则会发生同样的情况5在x86平台上。
编辑:对于怀疑者 - 我在互联网上找不到现成的参考资料,但请查看Expert .NET 2.0 IL Assembler By Serge Lidin第175页。答案 1 :(得分:3)
我还注意到你将char数据类型打包成1个字节。 .NET中的Char类型大小为2个字节。我无法验证这是否是实际问题,但我会仔细检查。
答案 2 :(得分:0)
如果您希望将结构放在其自身为Layoutind.Explict的其他结构中,您应该使用显式的Size值(以字节为单位),如果您希望它们在不同的位模式下工作(或者在具有不同包装要求的机器上) 你所说的是“按顺序排列,不要内部打包,但最后要使用尽可能多的空间”。 如果您没有指定Size,则运行时可以自由添加所需的空间。
它通常拒绝让结构和对象类型重叠的原因是GC例程必须可以自由遍历实时对象图。在执行此操作时,它无法知道联合(重叠)字段作为对象引用或作为原始位(例如int或float)是否有意义。由于必须遍历所有活动对象引用才能正常运行,因此它最终将遍历“随机”位,这些位可能指向堆中的任何位置(或者在其中),就好像它们是您在知道它之前的引用一样一般保护错误。
由于32/64引用将根据运行时占用32位或64位,因此必须使用Explict,只有带引用的引用引用和具有值类型的值类型,确保引用类型与两个目标平台的边界对齐它们不同(注意:运行时依赖,见下文)并执行以下操作之一:
关于对齐的注意事项: 道歉 我在未对齐的引用字段上出错 - 编译器删除了类型加载,除非我对结构执行了一些操作。
[StructLayout(LayoutKind.Explicit)]
public struct Foo
{
[FieldOffset(0)]
public byte padding;
[FieldOffset(1)]
public string InvalidReference;
}
public static void RunSnippet()
{
Foo foo;
foo.padding = 0;
foo.ValidReference = "blah";
// Console.WriteLine(foo); // uncomment this to fail
}
相关详细信息在ECMA规范http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf中,请参阅第16.6.2节,其中规定了原始大小值的对齐,包括&amp ;.它指出,如果需要,存在未对齐的前缀指令。
然而,在单声道(OSX intel和Win32 intel 32位)上面的代码可以工作。运行时不会尊重布局并且默默地“纠正”事物,或者它允许任意对齐(历史上它们在这方面不如MS运行时灵活,这是令人惊讶的)。 mono生成的CLI中间形式不包含任何.unaligned指令前缀,因此它似乎不符合规范。
那教会我只检查单声道。
答案 3 :(得分:0)
我在同样的问题上挣扎,并且讨厌我在MSDN上找不到关于这个主题的明确参考。在阅读了这里的答案之后,我开始专注于.NET中的x86和x64差异,并发现以下内容:Migrating 32-bit Managed Code to 64-bit。在这里,他们清楚地指出指针在x86上是4个字节,在x64上是8个字节。希望它对其他人有所帮助。
在Stack Overflow上有很多相关的问题。我将添加其中两个,其中提到其他有趣的东西。
答案 4 :(得分:-2)
由于它不符合CLS(参见此处:http://msdn.microsoft.com/en-us/library/system.uint16.aspx)
,因此Uint16可能出现问题。