我正在做一个大型项目,即使有 1000 个自动化测试和 100% 的代码覆盖率中的 10 个,我们也会收到大量错误。我们得到的错误中大约 95% 是 NullReferenceExceptions。
有没有办法在编译时强制执行空值检查?
除此之外,有没有办法在单元测试中自动执行空检查,而不必自己为空案例编写测试?
我正在做一个大型项目,即使有 1000 个自动化测试和 100% 的代码覆盖率中的 10 个,我们也会收到大量错误。我们得到的错误中大约 95% 是 NullReferenceExceptions。
有没有办法在编译时强制执行空值检查?
除此之外,有没有办法在单元测试中自动执行空检查,而不必自己为空案例编写测试?
您应该查看Code Contracts。静态检查器仅适用于高端 VS 版本,但这基本上就是您所追求的。
网上有很多资源,<plug>
你也可以阅读 C# 深度第 2 版中关于代码契约章节的预发布版本 -免费下载第 15 章。</plug>
(关于最新最好的代码合同版本,这一章有点过时了,但没什么大不了的。)
100% 的代码覆盖率没有任何意义。
这是一种虚假的安全感。
您唯一要衡量的是您正在执行所有代码行。
不是:
例如,如果您处理火灾的程序包含 1 个步骤“跑出建筑物”,那么即使这种情况发生在 100% 的情况下,也许更好的程序是“警告消防部门,尝试停止火,然后如果一切都失败了,就会熄灭”。
如果您没有专门进入并添加代码,无论是代码协定 (.NET 4.0) 还是特定的 IF 语句 (<4.0),C# 中都没有任何内置功能可以帮助您解决此问题。
C# 8 引入了不可为空的引用类型。
可以修改 .Net 项目以启用Nullable选项:
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
编译器将能够区分
string
和string?
NonNullableClass
和NullableClass?
这不是技术解决方案,而是社会解决方案。当引用类型被外部代码以任何方式修改(另一个方法调用等)时,只需在您的环境中使访问引用类型而不检查 null 成为不可接受的。单元测试并不能取代良好的老式代码审查。
有没有办法在编译时强制执行空值检查?
没有。编译器无法确定运行时引用变量是否指向 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;
1)我认为,Resharper可以建议您检查代码中的一些关键位置。例如,它建议添加 [null reference check code],如果您允许,则添加它。
试试看。当然,如果您需要,它将增加您的经验。
2)在开发应用程序的早期阶段在您的代码中使用“快速失败”模式(或断言、断言)
防御性编程只能让你走这么远......也许更好地捕捉异常并像其他任何事情一样处理它。
我可能错了,但我认为 FxCop 有一条规则建议您在代码中添加空引用检查。您可以尝试通过该工具运行您的程序集,看看它要说什么。
查看Gendarme,它可以在构建后与您的测试一起运行(如果您愿意,可以在它们之前运行)并且有一些与null
检查相关的规则。您也可以相当简单地编写自己的。
C# 3 不可能实现这些。你必须使用像 Spec# 这样的东西……我认为 C#4 可能内置了一些内容,但我不确定。
您不能在编译时进行空值检查,因为在编译时对象只是类型,并且只有在运行时类型才会转换为具有具体值的实例......这里为空。
也许您应该看一下 TFS 的自定义代码分析签入策略
.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。