为什么这段代码没有unsafe关键字?

在回答他自己有争议的问题时 , Mash已经说明你不需要“unsafe”关键字直接读写任何.NET对象实例的字节。 您可以声明以下类型:

[StructLayout(LayoutKind.Explicit)] struct MemoryAccess { [FieldOffset(0)] public object Object; [FieldOffset(0)] public TopBytes Bytes; } class TopBytes { public byte b0; public byte b1; public byte b2; public byte b3; public byte b4; public byte b5; public byte b6; public byte b7; public byte b8; public byte b9; public byte b10; public byte b11; public byte b12; public byte b13; public byte b14; public byte b15; } 

然后你可以做一些事情,比如改变一个“不可变”的字符串。 以下代码在我的机器上打印“bar”:

  string foo = "foo"; MemoryAccess mem = new MemoryAccess(); mem.Object = foo; mem.Bytes.b8 = (byte)'b'; mem.Bytes.b10 = (byte)'a'; mem.Bytes.b12 = (byte)'r'; Console.WriteLine(foo); 

您还可以通过使用相同的技术破坏对象引用来触发AccessViolationException 。

问题:我认为(在纯托管C#代码中) unsafe关键字是必要的,可以做这样的事情。 为什么这里没有必要? 这是否意味着纯粹的托管“安全”代码根本不安全?

好吧,这是令人讨厌的……使用工会的危险。 这可能有用,但不是一个好主意 – 我想我会将它与reflection(你可以做大多数事情)进行比较。 我有兴趣看看它是否在受限制的访问环境中工作 – 如果是这样,它可能代表一个更大的问题……


我刚刚测试它没有“完全信任”标志,运行时拒绝它:

无法从程序集“ConsoleApplication4,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null”加载类型“MemoryAccess”,因为对象在偏移量0处重叠且程序集必须是可validation的。

要拥有这面旗帜,你已经需要高度信任 – 所以你已经可以做更多讨厌的事了。 字符串是一个略有不同的情况,因为它们不是正常的.NET对象 – 但是还有其他一些方法可以改变它们 – 但“联合”方法是一个有趣的方法。 对于另一种hacky方式(有足够的信任):

 string orig = "abc ", copy = orig; typeof(string).GetMethod("AppendInPlace", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(int) }, null) .Invoke(orig, new object[] { "def", 3 }); Console.WriteLine(copy); // note we didn't touch "copy", so we have // mutated the same reference 

哎呀,我已经fixed unsafe了。 这是一个更正版本:

示例代码不需要使用unsafe关键字进行标记的原因是它不包含指针 (请参阅下面引用为什么这被认为是不安全的)。 你是完全正确的:“安全”可能更好地被称为“运行时友好”。 有关此主题的更多信息,请参阅Don Box和Chris Sells Essential .NET

引用MSDN,

在公共语言运行库(CLR)中,不安全的代码称为无法validation的代码。 C#中的不安全代码不一定是危险的; 它只是CLR无法validation其安全性的代码。 因此,只有在完全受信任的程序集中,CLR才会执行不安全的代码。 如果使用不安全的代码,则您有责任确保代码不会引入安全风险或指针错误。

固定和不安全之间的区别在于固定阻止CLR在内存中移动东西,因此运行时之外的东西可以安全地访问它们,而不安全则恰恰相反的问题:CLR可以保证正确的解决方案dotnet引用,它不能为指针这样做。 您可能会回想起有关引用如何不是指针的各种Microsoft,这就是为什么它们会对一个微妙的区别做出如此大惊小怪的原因。

你仍然选择退出’托管’位。 有一个潜在的假设,如果你能做到这一点,那么你就知道自己在做什么。