我们需要经常与本地代码进行互操作,在这种情况下,使用不需要编组的非安全结构体会快得多。但是,当结构包含非原始类型的固定大小缓冲区时,我们无法这样做。 为什么C#编译器要求固定大小的缓冲区只能是原始类型?为什么不能将固定大小的缓冲区设置为结构体,例如:
[StructLayout(LayoutKind.Sequential)]
struct SomeType
{
int Number1;
int Number2;
}
我们需要经常与本地代码进行互操作,在这种情况下,使用不需要编组的非安全结构体会快得多。但是,当结构包含非原始类型的固定大小缓冲区时,我们无法这样做。 为什么C#编译器要求固定大小的缓冲区只能是原始类型?为什么不能将固定大小的缓冲区设置为结构体,例如:
[StructLayout(LayoutKind.Sequential)]
struct SomeType
{
int Number1;
int Number2;
}
在C#中,固定大小的缓冲区是通过一个名为“opaque classes”的CLI特性实现的。Ecma-335的第I.12.1.6.3节对它们进行了描述:
"无法获取数据成员的信息"和"不得使用ldfld/stfld"是关键。第二条规则禁止结构体,因为需要使用ldfld和stfld来访问它们的成员。C#编译器无法提供替代方案,结构体的布局是运行时实现细节。Decimal和Nullable<>也不行,因为它们也是结构体。IntPtr也不行,因为其大小取决于进程的位数,这使得C#编译器很难生成用于访问缓冲区的ldind/stind操作码的地址。引用类型的引用也不行,因为GC需要能够找到它们,但根据第一条规则,它们无法被找回。枚举类型的大小取决于其基础类型,听起来像是可以解决的问题,但不确定为什么要跳过它。一些语言提供多字节数据结构,其内容通过地址算术和间接操作直接操作。为支持此功能,CLI允许创建具有指定大小但没有关于其数据成员的任何信息的值类型。这些“不透明类”的实例与任何其他类的实例处理方式完全相同,但不应使用ldfld、stfld、ldflda、ldsfld和stsfld指令来访问它们的内容。
因此,只剩下C#语言规范中提到的类型:sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double或bool。只有具有明确定义大小的简单类型。
IntPtr
已经过时”是真的吗? struct S { IntPtr p1, p2, p3; }
是完全不受管理的,您可以很好地使用 sizeof(S)
。 同样,对于 struct P { void* p1, p2, p3; }
和 sizeof(P)
等等... 没有理由禁止这些(它们也没有被禁止)。 您的回答对我来说毫无意义。 - user541686来自MSDN:
在C#中,您可以使用fixed语句在数据结构中创建具有固定大小数组的缓冲区。当您使用现有代码时,例如使用其他语言编写的代码、预先存在的DLL或COM项目时,这非常有用。固定数组可以采用任何允许正常结构成员的属性或修饰符。唯一的限制是,数组类型必须为 bool、byte、char、short、int、long、sbyte、ushort、uint、ulong、float 或 double。
我引用Hans Passant先生的话,解释为什么需要使用unsafe
关键字来定义固定缓冲区。您可以查看Why is a fixed size buffers (arrays) must be unsafe?了解更多信息。
因为“固定缓冲区”不是一个真正的数组。它是一种自定义值类型,是我所知道的在C#语言中生成一种自定义值类型的唯一方法。CLR无法验证数组的索引是否以安全的方式进行。该代码也无法验证。最明显的证明是:
using System;
class Program {
static unsafe void Main(string[] args) {
var buf = new Buffer72();
Console.WriteLine(buf.bs[8]);
Console.ReadLine();
}
}
public struct Buffer72 {
public unsafe fixed byte bs[7];
}
我理解你的观点...另一方面,我认为这可能是Microsoft保留的某种前向兼容性。你的代码被编译成MSIL,并且它的布局在内存中是特定.NET框架和操作系统的业务。
我可以想象到会有新的Intel CPU问世,它要求将变量按照每8字节布局以获得最佳性能。在那种情况下,未来可能需要在某个未来的.NET框架6和Windows 9中以不同方式布局这些结构体。在这种情况下,你的示例代码将对Microsoft构成压力,不要改变将来的内存布局,也不要将.NET框架加速到现代硬件。
这只是猜测...
你尝试过设置FieldOffset吗?参考C++中的C#联合