【问题标题】:How do I enforce null checking? [duplicate]如何强制执行空值检查? [复制]
【发布时间】:2011-01-21 05:09:00
【问题描述】:

我正在开展一个大型项目,即使有 1000 次中的 10 次自动化测试和 100% 的代码覆盖率,我们仍然会遇到大量错误。我们得到的错误中大约 95% 是 NullReferenceExceptions。

有没有办法在编译时强制执行空值检查?

除此之外,有没有办法在单元测试中自动强制执行空值检查,而不必自己编写空案例的测试?

【问题讨论】:

  • NullReferenceExceptions 是来自测试框架还是来自正在测试的实际代码?
  • 哪个构建服务器?如果 TFS 可能使用代码分析策略规则帮助。
  • 也许在您的样式检查器中添加一条规则以查找{} = nullreturn null;?如果您从未将任何内容设置为 null,那么您唯一需要检查 null 的就是库调用的结果。
  • @Anon:当然还有统一的类字段成员..
  • @Matthew Iselin:异常来自代码,而不是测试框架。我们有一些自动化的端到端系统和集成测试,它似乎运行良好,但我们的 QA 测试人员或现场用户发现了许多空异常。

标签: c# null static-analysis nullreferenceexception


【解决方案1】:

您应该查看Code Contracts。静态检查器仅适用于高端 VS 版本,但这基本上就是您所追求的。

网上有很多资源,<plug> 你也可以阅读 C# 深度第二版的代码契约章节的预发布版本 - download chapter 15 for free</plug>(关于最新最好的代码合同版本,该章节有些过时,但没什么大不了的。)

【讨论】:

  • +1 代码契约肯定会在编译时停止你的空引用。在消除将空值传递给特定方法/类的所有可能性之前,您将无法构建。另请查看与代码合同密切相关的 Pex。
  • @Jon Skeet:我错了,或者代码合同只有在开发人员使用 Requires.Something 时才有效?因此,如果开发人员在检查使用合约时出现错误,它会在编译时通过吗?我认为 Juliet 想在开发时间之后,在测试或构建时检查这一点。
  • @Yoann:嗯,是的,你必须在代码中表达合同。您还打算如何区分 可以 接受 null 和不能接受 null 的 API?但是静态检查器确实在 API 调用者的编译时执行检查。
  • 我还是希望他们能像 Spec# 那样做。代码合同可能是这里最好的解决方案,但它们太……冗长了。
  • @Jon:这就是为什么我建议自定义代码分析规则,但不确定是否可以构建自定义规则检查空引用。
【解决方案2】:

100% 的代码覆盖率毫无意义。

这是一种虚假的安全感。

您唯一要衡量的是您正在执行所有代码行。

不是:

  • 那些代码行是所有应该存在的代码行
  • 这些代码行是否正常运行(您是否在测试所有边缘情况?)

例如,如果您处理火灾的程序包含 1 个步骤“用完建筑物”,那么即使这种情况发生在 100% 的情况下,也许更好的程序是“通知消防部门,试着把火扑灭,如果一切都失败了,就用尽”。

如果您没有专门进入并添加代码,无论是代码协定 (.NET 4.0) 还是特定的 IF 语句 (

【讨论】:

  • 更正:代码覆盖意味着什么,但并不意味着一切。
【解决方案3】:

C#8 引入了Non-nullable reference types

可以修改 .Net 项目以启用 Nullable 选项:

<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>

编译器可以区分

  • stringstring?

  • NonNullableClassNullableClass?

【讨论】:

    【解决方案4】:

    这不是技术解决方案,而是社交解决方案。当引用类型已被外部代码以任何方式修改(另一个方法调用等)时,只需在您的环境中使访问引用类型而不检查 null 成为不可接受的。单元测试并不能取代良好的老式代码审查。

    【讨论】:

    • 这会导致数千行并没有真正增加价值的新代码。如果你得到一个空值并且你不能处理空值,不要检查它,只是崩溃和烧毁。更好的约定是“在任何情况下都不要在生产代码中将空引用传递给其他人(测试代码中的空值可以很好地减少混乱)”
    • @kai - 这太疯狂了。您不仅让生产应用程序崩溃和烧毁,而且您无法控制方法是否在 3rd 方 API 中返回 null。
    • 崩溃(或至少终止当前的操作/请求)比吞下错误或让系统继续处于未知状态要好得多。当然,您不会“让”您的应用程序崩溃,这不应该发生。如果你在某个地方得到一个空值,那么你就有一个错误,你应该修复它,这样你就不会在那里得到空值。当然,应用程序边界、UI 代码和第 3 方集成点是您必须验证内容的地方,但是当您进入域模型和业务逻辑时,空值大多只会造成损害并损害可读性。
    • 我同意你永远不应该在你控制的代码中引入空值,但你不能忽略空值而让错误冒泡。错误越冒泡,错误对您发现错误的任何地方的意义就越小。因此,这两种选择是将所有内容都包装在 try...catch 中,或者测试空值并优雅地处理。
    • 相反,您希望能够在从测试套件运行代码时插入空值。它有助于显示真正重要的和不重要的。就像我说的,当然你必须验证用户输入或你从网络请求中得到的东西等,但我坚持认为,如果你在域模型中得到一个空值并且没有预料到它,你有一个错误并且假装是愚蠢的就像应用程序不工作时一样。将所有内容包装在 try/catch 中或对每个 LoC 进行防御性检查正是您不想做的事情。不过这变成了聊天,所以我出去了。
    【解决方案5】:

    有没有办法在编译时强制执行空值检查?

    不。编译器无法确定运行时引用变量是否指向 null。

    并且排除空产生语句(集合和返回)也是不够的。考虑:

    public class Customer
    {
      public List<Order> Orders {get;set;}
    }
      //now to use it
    Customer c = new Customer;
    Order o = c.Orders.First(); //oops, null ref exception;
    

    【讨论】:

      【解决方案6】:

      防御性编程只能让你走这么远......也许捕获异常并像处理其他任何事情一样处理它会更好。

      【讨论】:

      • 在“处理”异常时,一定要处理它发生的原因。为什么这个参考永远不会被设置?在设置之前是否抛出异常?今天发生在我身上,有必要追查其原因(导致ArgumentNullException 的资源丢失,已被记录并忽略)。
      • 某些事情,特别是 io 操作,你永远无法确定会起作用。如果在某处拔出电缆导致方法返回 null(可能是不好的做法,但您不能总是得到您想要的),那么您不妨将其作为异常捕获。
      【解决方案7】:

      1) 我认为,Resharper 可以建议您检查代码中的一些关键位置。 例如,它建议添加[空引用检查代码],并在允许的情况下添加。

      试试看。当然,如果您需要,它会增加您的体验。

      2) 在开发应用程序的早期阶段在您的代码中使用“Fail Fast”模式(或断言、断言)

      【讨论】:

        【解决方案8】:

        这些都不可能在 C# 3 中实现。您必须使用 Spec# 之类的东西...我认为 C#4 可能内置了其中的一些内容,但我不确定。

        规格#:http://research.microsoft.com/en-us/projects/specsharp

        【讨论】:

          【解决方案9】:

          您不能在编译时进行空值检查,因为在编译时对象只是类型,并且只有在运行时类型才会转换为具有具体值的实例......这里为空。

          【讨论】:

            【解决方案10】:

            【讨论】:

              【解决方案11】:

              .NET 框架希望通过使用 !修饰符。

              public void MyMethod(!string cannotBeNull)
              

              但是很遗憾,我们没有编译时检查。最好的办法是尽量减少外部调用者传递空值的次数,然后对面向公众的方法执行空检查:

              public class ExternalFacing
              {
                public void MyMethod(string arg)
                {
                   if (String.IsNullOrEmpty(arg))
                      throw new ArgumentNullException(arg);
              
                   implementationDependency.DoSomething(arg);
                 }
              }
              
              internal class InternalClass
              {
                  public void DoSomething(string arg)
                  {
                       // shouldn't have to enforce null here.
                  }
              }
              

              然后将适当的单元测试应用于 External 类以预期 ArgumentNullExceptions。

              【讨论】:

              【解决方案12】:

              我可能错了,但我认为 FxCop 有一条规则建议您在代码中添加空引用检查。您可以尝试通过该工具运行您的程序集,看看它会说什么。

              【讨论】:

                【解决方案13】:

                查看Gendarme,它可以在构建后与您的测试一起运行(如果您愿意,可以在它们之前运行)并且有一些与null 检查相关的规则。您也可以相当简单地编写自己的代码。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-12-31
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-02-07
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多