C#: 明确声明“unsafe”/编译器选项的好处

8
我理解指针,以及在 C# 代码中极少使用它们的必要性。我的问题是:为什么在代码块中必须明确声明“unsafe”?另外,为什么必须更改编译器选项才能允许“unsafe”代码?
最终答案:CLR(或语言规范)中的什么使我们不能像在 C 和 C++ 中一样随意使用指针(而不必键入“unsafe”并更改编译器选项)?
澄清一下:我知道“unsafe”和“safe”代码是什么。问题在于为什么我们必须做这么多额外的工作(好吧,并不是那么多),才能使用这些特性。
9个回答

9

这里有一篇采访C#创造者Anders Hejlsberg的文章,涉及到了这里所述的主题。基本上就是像@Marc Gravell所说的:类型安全优先,通过显式声明来实现不安全。

所以回答你的问题:CLR中没有任何东西会阻止它;这是一种语言习惯,旨在让你在处理类型时戴上安全手套。如果你想脱掉手套,那是你的选择,但你必须主动选择脱掉手套。

编辑:

为了澄清:我知道什么是“不安全”的和“安全”的代码。问题只是为什么我们必须做所有这些额外的工作(好吧,不是那么多额外的工作),才能使用这些功能。

作为我链接的采访中提到的,这是一个明确的设计决策。C#本质上是Java的进化,而在Java中,根本没有指针。但设计人员希望允许指针;然而,由于C#通常会吸引Java开发人员,他们认为最好的做法是默认行为类似于Java,即没有指针,同时仍然允许通过显式声明使用指针。
因此,“额外的工作”是故意的,以迫使您在执行操作之前考虑您正在做什么。通过明确,它至少迫使您考虑:“为什么要这样做?当引用类型足够时,我是否真正需要一个指针?”

7

这主要与可验证性有关。通过声明unsafe,手套脱落——系统无法保证您的代码不会失控。在大多数情况下,保持安全区域是非常理想的。

这在部分信任(插件等)中更加明显,但在常规代码中仍然很有价值。


所以基本上,这是微软试图防止我们犯与C++中更容易犯的相同错误? - Inisheer
1
来自微软:进行低级API调用、使用指针算术或执行其他不良操作的C#代码必须放置在标记为unsafe的块内。 - VBNight
从微软来看,有些情况下,不安全的代码可以通过删除数组边界检查来增加应用程序的性能。 - VBNight
http://msdn.microsoft.com/en-us/library/ms228628(VS.80).aspx http://msdn.microsoft.com/en-us/library/t2yzs44b(VS.80).aspx - VBNight
如果您不托管“.Length”,则数组边界检查仍会在JIT上发生。 - Marc Gravell
@Marc Gravell:我想你的意思是“提升”。 :p - Randolpho

3
实际上,CLR对于/unsafe开关或关键字没有任何要求。事实上,在CLR下运行的C++/CLI(C++语言)没有这样的/unsafe开关,指针可以自由使用。
因此,我会重新表述你的问题为“为什么C#在使用指针之前需要使用/unsafe?”答案如其他答案所述:为了帮助用户做出有意识的决定,失去在CLR中不低于完全信任模式下运行的能力。C++几乎总是需要在CLR上具有完全信任,而C#可以在调用需要完全信任的代码或使用指针时进行设置。

没错,我之前重新表述了“要点”部分。我经常使用C++/CLI,这也是我询问关于C#的问题的原因。 - Inisheer

2

当使用不安全代码块时,它会使代码无法验证。这需要特定的权限才能执行,如果您不想在输出中允许它(特别是在共享源环境中),则编译器中有一个开关可以禁用它。


2

从相反的角度考虑:因为未标记为不安全,您可以推断默认情况下大多数代码都是“安全”的。那么什么是“安全”?对于 .Net 代码而言,这包括(但可能不限于):

  • 垃圾收集器可以像往常一样工作。
  • 对特定类型的引用引用该类型的对象(或 null)。
  • 代码保证符合 .Net 的信任/安全要求。
  • 已经数学证明,代码不会直接触及其自身 AppDomain 之外的内存。这似乎微不足道,但想象一下如果应用程序中有多个 AppDomains,则程序员可以自信地将它们视为逻辑上独立的。

使用指针时,您有机会破坏其中任何一个保证。因此,标记代码为不安全会放弃这些保护措施。


2
简而言之,.NET希望您表明您的意图。
当然,编译器可以推断出需要使用“unsafe”标记。 但是设计人员希望这是一个刻意的决定。
对我来说,这类似于C#中的许多语法要求:
- 不能没有break的switch case - 没有访问级别“段”(例如C ++中的“public:”标记) - 不使用“var”定义类字段
模式是,您不应该移动或更改一件事情并无意中影响另一件事情。 在合理范围内,他们想防止您“自乱阵脚”。
这里有很多很好的、有信息价值的答案——也许这更符合你的问题。

变量不应该在您的列表中。允许在类级别上进行类型推断将需要彻底重新设计编译器,可能是不可能的。 - Jonathan Allen

1

这样很明显就能知道哪些代码需要提升权限才能在 Web 服务中运行。


1
培养良好的习惯和安全性。每当您在程序集中使用不安全块时,堆栈将要求NativeCode权限。这当然可以隐式完成,但我们是否可以完全删除private关键字呢?我认为强制开发人员在使用之前明确要求不安全代码是很好的做法。

1

安全代码和不安全代码之间最显著的区别在于,不安全代码无法被 .net 的垃圾回收器访问。自动 GC 是 .net 语言中非常重要的一部分,当你超越其边界时,你会改变很多关于你的代码可以假定的东西。

特别是指针允许在堆上创建没有 GC 引用的对象。这导致另一个优秀的理由要求将代码标记为“不安全”。当你意识到有内存泄漏时,它使得缩小泄漏来源变得容易。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接