我们必须与本机代码交互很多,在这种情况下,使用不需要封送的不安全结构会快得多.但是,当结构包含非原始类型的固定大小缓冲区时,我们无法执行此操作.为什么C#编译器要求固定大小的缓冲区只是原始类型?为什么固定大小的缓冲区不能由结构组成,例如:
[StructLayout(LayoutKind.Sequential)]
struct SomeType
{
int Number1;
int Number2;
}
Run Code Online (Sandbox Code Playgroud)
Han*_*ant 17
C#中的固定大小缓冲区使用名为"opaque classes"的CLI功能实现.Ecma-335的第I.12.1.6.3节描述了它们:
一些语言提供多字节数据结构,其内容由地址算术和间接操作直接操作.为了支持此功能,CLI允许使用指定的大小创建值类型,但不包含有关其数据成员的信息.这些"opaque类"的实例的处理方式与任何其他类的实例完全相同,但ldfld,stfld,ldflda,ldsfld和stsfld指令不得用于访问其内容.
"没有关于他们的数据成员的信息"和"不得使用"ldfld/stfld"是一个问题.第二条规则将kibosh放在结构上,你需要ldfld和stfld来访问它们的成员.C#编译器无法提供替代方案,struct的布局是运行时实现细节.也是你不能使用sizeof的原因结构上的运算符.Decimal和Nullable <>都是因为它们也是结构.IntPtr是因为它的大小取决于进程的位数,因此C#编译器很难为用于访问缓冲区的ldind/stind操作码生成地址.引用类型引用已经过时,因为GC需要能够找回它们,而不能通过第一条规则找到它们.枚举类型具有可变大小,取决于其基本类型; 听起来像一个可解决的问题,不完全确定他们为什么跳过它.
其中只剩下C#语言规范中提到的那些:sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double或bool.只是具有明确定义的简单类型.
来自MSDN:
在C#中,您可以使用fixed语句在数据结构中创建具有固定大小数组的缓冲区.当您使用现有代码时,这很有用,例如用其他语言编写的代码,预先存在的DLL或COM项目.固定数组可以采用常规struct成员允许的任何属性或修饰符.唯一的限制是数组类型必须是bool,byte,char,short,int,long,sbyte,ushort,uint,ulong,float或double.
我只是在关于引用汉斯帕桑特先生为什么一个固定的缓冲区必须是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];
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,您可以任意访问堆栈帧.恶意代码可以使用标准缓冲区溢出注入技术来修补函数返回地址并强制您的代码跳转到任意位置.
是的,这是非常不安全的.
西蒙怀特提出了一个有效观点:
我会选择"为编译器添加复杂性".编译器必须检查没有.NET特定功能应用于应用于可枚举项的结构.例如,泛型,接口实现,甚至非原始数组的更深层属性等等.毫无疑问,运行时也会遇到一些互操作问题.
而Ibasa:
"但这已经由编译器完成了." 只是部分.编译器可以执行检查以查看是否管理了类型,但是不会生成用于将结构读/写到固定缓冲区的代码.它可以完成(没有什么能阻止它在CIL级别)它只是没有在C#中实现.
最后,Mehrdad:
我认为这是因为他们不希望您使用固定大小的缓冲区(因为他们希望您使用托管代码).使得与本机代码互操作变得太容易使得您不太可能将.NET用于所有内容,并且他们希望尽可能地提升托管代码.
答案似乎是一个响亮的"它只是没有实现".
我的猜测是,成本和实施时间对他们来说并不值得.开发人员宁愿通过非托管代码推广托管代码.它可能在未来的C#版本中完成,但是当前的CLR缺乏很多所需的复杂性.
另一种选择可能是安全问题.由于固定缓冲区非常容易受到各种问题和安全风险的影响,如果它们在代码中实现得不好,我可以看到为什么不鼓励使用它们而不是C#中的托管代码.为什么要把很多工作放在你想要阻止使用的东西上呢?
| 归档时间: |
|
| 查看次数: |
1419 次 |
| 最近记录: |