为什么.Net框架不使用Guard类(或等效的)作为方法参数

Baz*_*nga 17 .net c#

如果看一下.net框架代码的反编译源,大多数API都会有这样的检查

if (source == null)
    throw Error.ArgumentNull("source");
Run Code Online (Sandbox Code Playgroud)

在方法参数上,而不是使用更通用的类

Guard.IsNotNull(source);
Run Code Online (Sandbox Code Playgroud)

有没有理由每次都做这种说明,或者这只是自框架开发以来的遗留代码,而新的类正朝着这个方向发展,还是有明确的检查有任何固有的优势?我可以想到的一个原因可能是避免使用函数指针重载堆栈.

Dan*_*rth 21

添加到Matthews答案:

您提出的语法Guard.IsNotNull(source);不直接等同于第一个代码段.它只传递参数的而不传递其名称,因此抛出的异常无法报告违规参数的名称.它只知道其中一个参数是null.

您可以使用表达式树 - 如下所示:Guard.IsNotNull(() => source);- 但是分析此表达式树在运行时具有相当大的性能影响,因此这也不是一个选项.

您提出的语法只能与静态编织器一起使用.这基本上是一个改变生成的IL的后编译器.这就是Code Contracts正在使用的方法.但这有其自身的成本,即:

  1. 那个静态编织者首先需要由某人编写
  2. 它增加了构建时间
  3. 编织者还需要修补调试符号
  4. 编辑和继续会导致各种问题

  • 公平地说,无论如何,我发现编辑并继续在最好的时候变得好看; 我很少使用它(无论如何它都不适用于64位程序). (2认同)

Mat*_*son 11

现在我们可以这样做,Code Contracts所以我们可以使用:

Contract.Requires(source != null);
Contract.Ensures(Contract.Result<MyType>() != null);
Run Code Online (Sandbox Code Playgroud)

等等,但目前我们只能在自己的代码中执行此操作,因为它尚未内置到CLR中(它是单独的下载).

自版本4以来,Code Contracts类本身就是.Net的一部分,但它们本身并不生成任何检查代码.为此,我们需要代码契约重写器,它将在生成代码时由C#编译器调用.这是需要单独下载的东西.

所以是的,我们现在有更好的方法来做到这一点,但它尚未作为CLR(尚未)的一部分发布,因此CLR目前正在使用您认为的"遗留"方法.

这肯定与"使用函数指针重载堆栈"无关.

即使使用代码合同,我们仍在进行检查.我知道没有IL命令检查null的参数,如果是,则抛出,所以这样的工作必须使用几个IL指令(在所有CLR语言中)完成.但是,Code Contracts代码重写器确实会生成内联代码来检查Code Contract的谓词(例如value != null),而不是调用方法来执行此操作,因此它非常有效.

  • +1.虽然我之前在项目中使用过CodeContract,但是现在看起来它们似乎比它的价值更麻烦,特别是当你在做ClickOnce时 - 除非你完全禁用它,否则重写器会弄乱哈希值.但是我等着看他们是否可以向前推进一点...... :) (2认同)

Han*_*ant 5

.NET框架中没有Guard类,因此您提出的替代方案是不可行的.后来添加到框架确实使用代码契约,但相当谨慎.并非每个微软的.NET程序员都相信合同是有用的,我确实分享了这种情绪.

您正在看到Microsoft的工作方式..NET框架中的代码由公司内的许多小团队贡献.典型的团队规模约为10名程序员.否则,对软件开发商业中的每个人都知道,大型团队无法工作.有一个临界质量,让每个人进行通信所花费的时间开始压缩实际获得代码所花费的时间.

这些团队也不断创建和解散.框架的许多部分不再拥有维护它的活跃团队.通常只有一个人仍然能够很好地了解内部,以提供关键的安全更新,并且可能在必要时修复错误.这样一个被解散的团队编写的代码非常处于维护模式,只有在绝对必要时才会进行更改.这不仅仅是因为进行轻微的风格改变没有任何好处,而是为了降低在不知不觉中添加突破性变化的可能性.

对于.NET框架来说,这是一个负担,有很多内部设备具有外部可见的诀窍,即使该代码存在于私有方法中.像例外.程序员使用Reflection来破解框架限制.而真正微妙的东西,一个很好的例子是微软内部广泛使用的电子邮件应用程序中的错误,由实习生编写.当他们将他们的机器从.NET 1.1更新到.NET 2.0时,崩溃并让每个人都没有电子邮件.该电子邮件应用程序中的错误是潜在的线程竞争,在使用.NET 1.1时从未触发过.但是,通过.NET 2.0框架代码的时序的微小变化,可以看到它.