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

本文关键字:参数 方法 框架 net Guard 为什么 | 更新日期: 2023-09-27 18:15:44

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

if (source == null)
    throw Error.ArgumentNull("source");

,而不是使用更通用的类,如

Guard.IsNotNull(source);

每次都显式地做这个有什么原因吗?或者这只是自框架开发以来一直存在的遗留代码,而新类正在向这个方向发展,或者有任何显式检查的固有优势吗?我能想到的一个原因可能是避免用函数指针重载堆栈。

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

补充马修斯的回答:

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

你可以使用表达式树——像这样:Guard.IsNotNull(() => source);——但是分析这个表达式树会在运行时产生相当大的性能影响,所以这也不是一个选择。

建议的语法只能与静态编织器结合使用。这基本上是一个修改生成IL的后编译器。这就是Code Contracts使用的方法。但这是有代价的,即:

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

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

Contract.Requires(source != null);
Contract.Ensures(Contract.Result<MyType>() != null);

等等,但是目前我们只能在我们自己的代码中这样做,因为它还没有内置到CLR中(它是一个单独的下载)。

代码契约类本身从4版开始就是。net的一部分,但是它们自己并不生成任何检查代码。要做到这一点,我们需要代码契约重写器,它将由c#编译器在生成代码时调用。这是需要单独下载的内容。

所以,是的,我们现在有更好的方法来做到这一点,但它还没有作为CLR的一部分发布,所以CLR目前使用的是你认为的"遗留"方法。

这当然与"用函数指针重载堆栈"无关。

即使有代码契约,我们仍然在做检查。据我所知,没有IL命令检查参数是否为空并抛出,因此必须使用几个IL指令(在所有CLR语言中)完成此类工作。然而,代码契约代码重写器确实生成内联代码来检查代码契约的谓词(例如value != null),而不是调用方法来这样做,因此它非常有效。

. net框架中没有Guard类,所以您建议的替代方案是不可行的。后来对框架的添加确实使用了代码契约,但相当少。并不是所有微软的。net程序员都相信契约是有用的,我也这么认为。

你看到的是微软的工作方式。. net框架中的代码是由公司内许多小团队贡献的。一个典型的团队规模大约是10个程序员。否则,软件开发行业的每个人都知道,大型团队是行不通的。当花在让每个人进行交流上的时间开始超过花在实际编写代码上的时间时,就会出现一个临界质量。

这样的团队也在不断地创建和解散。框架的许多部分不再有活跃的团队来维护它。通常情况下,只有一个人仍然对内部非常了解,可以提供关键的安全更新,并可能在必要时修复错误。这样一个解散的团队编写的代码在很大程度上处于维护模式,只有在绝对必要的时候才会进行更改。不仅因为做小的风格改变没有好处,而且为了减少不知情地增加破坏性改变的可能性。

这是。net框架的一个缺点,有很多内部代码可以在外部可见,即使这些代码位于私有方法中。像例外。程序员使用反射来破解框架的限制。真正微妙的东西,一个很好的例子是在微软内部广泛使用的电子邮件应用程序的错误,由实习生编写。当他们的电脑从。net 1.1升级到。net 2.0时,它崩溃了,导致每个人都没有电子邮件。电子邮件应用程序中的错误是一个潜在的线程竞争,在使用。net 1.1运行时从未触发过。但是在。net 2.0框架代码的时间上有一个很小的变化,

它可能不是。net框架的一部分,但微软开发人员似乎正在接受这个概念(注意使用JetBrains注释而不是代码契约):

https://github.com/aspnet/EntityFramework/blob/master/src/Shared/Check.cs

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;
namespace Microsoft.Data.Entity.Utilities
{
    [DebuggerStepThrough]
    internal static class Check
    {
        [ContractAnnotation("value:null => halt")]
        public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
        {
            NotEmpty(parameterName, "parameterName");
            if (ReferenceEquals(value, null))
            {
                throw new ArgumentNullException(parameterName);
            }
            return value;
        }
...

我唯一能想到的是,如果你有一个Guard类,那么在异常堆栈跟踪中,它看起来好像问题是在Guard中,当它实际上在称为Guard的方法中。您可以通过捕获和重新抛出来解决这个问题,但这样又会在生产代码中出现样板文件。