C# StructLayout.Explicit 问题
Posted
技术标签:
【中文标题】C# StructLayout.Explicit 问题【英文标题】:C# StructLayout.Explicit Question 【发布时间】:2010-11-14 01:03:16 【问题描述】:我试图理解为什么下面的第二个示例没有问题,但第一个示例给了我下面的异常。在我看来,这两个例子都应该根据描述给出一个例外。谁能赐教?
未处理的异常: System.TypeLoadException:不能 从加载类型“StructTest.OuterType” 程序集'StructTest,版本= 1.0.0.0, 文化=中立,PublicKeyToken=null' 因为它包含一个对象字段 未正确对齐的偏移量 0 或被非对象字段重叠。 在 StructTest.Program.Main(字符串 [] args) 按任意键继续。 . .
示例 1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StructTest
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnerType
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
char[] buffer;
[StructLayout(LayoutKind.Explicit)]
struct OuterType
[FieldOffset(0)]
int someValue;
[FieldOffset(0)]
InnerType someOtherValue;
class Program
static void Main(string[] args)
OuterType t = new OuterType();
System.Console.WriteLine(t);
示例 2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StructTest
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnerType
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
char[] buffer;
[StructLayout(LayoutKind.Explicit)]
struct OuterType
[FieldOffset(4)]
private int someValue;
[FieldOffset(0)]
InnerType someOtherValue;
class Program
static void Main(string[] args)
OuterType t = new OuterType();
System.Console.WriteLine(t);
【问题讨论】:
【参考方案1】:公共语言运行时包含一个验证程序,可确保正在运行的代码(可验证的 IL)不会损坏托管环境中的内存。这可以防止您声明这样一个字段重叠的结构。基本上,您的结构包含两个数据成员。一个整数(4 个字节)和一个本机整数(指针大小)。在您可能正在运行代码的 32 位 CLR 上,char[]
将占用 4 个字节,因此如果您将整数放在距离结构开头不到 4 个字节的位置,您将有重叠的字段。有趣的是,您的两个代码 sn-ps 在 64 位运行时均失败,因为指针大小为 8 个字节。
【讨论】:
我明白了,所以如果我想正确执行此操作,以便在 32 或 64 位机器上正常工作,我需要分别使用 8 和 0 的偏移量,对吗?问题是我真正想要创建的是一个联合,如果我不能使用相同的偏移量,它看起来最终不会是一个。 是的。它适用于偏移量为 8 和 0 的 x64 CLR。请注意,虽然代码 sn-p 有效,但如果您在现实世界的应用程序中执行此操作,您可能会与一些非托管的东西交互,这些东西会期望精确的偏移量。如果你将它设置为 8,它可能会在 32 位机器上失败(不是这个 sn-p 本身,而是你正在与之交互的非托管代码)。 顺便说一下,如果它们是 非托管类型,你可以有重叠的字段,但是 .NET 数组是一个引用类型(它不能根据 C# 规范被视为非托管类型)。因此,您不能将引用类型作为 C# 中的联合成员。如果你真的需要这个,你应该考虑使用指针类型之类的东西作为结构成员而不是数组。 谢谢。我正在研究直接在 OuterType 中使用大小为 100 的固定字符的不安全结构,而不是使用 InnerType。听起来这可能是更好的方法。 规则更加晦涩难懂。引用类型的两个字段(甚至完全不相关)可以重叠,在这种情况下,程序集是有效的,但不可验证(因此它将在 FullTrust 中运行)。所以你仍然可以通过重叠对不兼容类型的引用来搞砸事情。我认为“部分重叠”限制的原因是因为它绝对保证将GC吹出水面。而使用完全引用重叠 GC 就可以了,而且当您有一个鉴别器字段来跟踪类型时,它实际上会很有用(节省强制转换运行时类型检查)。【参考方案2】:我想我会用我用来创建工会的解决方案来回应——这是我的初衷。我使用了一个不安全的结构和一个固定数组,然后使用一个属性与固定数组进行交互。我相信这应该做我想做的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StructTest
[StructLayout(LayoutKind.Explicit)]
unsafe struct OuterType
private const int BUFFER_SIZE = 100;
[FieldOffset(0)]
private int transactionType;
[FieldOffset(0)]
private fixed byte writeBuffer[BUFFER_SIZE];
public int TransactionType
get return transactionType;
set transactionType = value;
public char[] WriteBuffer
set
char[] newBuffer = value;
fixed (byte* b = writeBuffer)
byte* bptr = b;
for (int i = 0; i < newBuffer.Length; i++)
*bptr++ = (byte) newBuffer[i];
get
char[] newBuffer = new char[BUFFER_SIZE];
fixed (byte* b = writeBuffer)
byte* bptr = b;
for (int i = 0; i < newBuffer.Length; i++)
newBuffer[i] = (char) *bptr++;
return newBuffer;
class Program
static void Main(string[] args)
OuterType t = new OuterType();
System.Console.WriteLine(t);
【讨论】:
以上是关于C# StructLayout.Explicit 问题的主要内容,如果未能解决你的问题,请参考以下文章