如果要在第 2 步进行空检查,则必须在每个方法调用中添加空检查。
就像现在一样,绝大多数方法不需要检查实例是否为空。相反,他们尝试调用该方法,如果实例为空,则尝试获取方法表来执行此操作会导致无效的内存访问,然后NullReferenceException
由框架捕获并转换为无效的内存访问。如果先验地知道实例不为空,则此处运行的代码没有更多的工作。
只有当优化意味着:
- 该调用已通过内联删除。
- 内联调用不涉及字段访问(无论如何都会导致空引用异常)。
- 内联调用不涉及对同一对象的另一个调用(同上)。
- 该实例不能显示为绝对不为空(或者不用担心)。
在这种情况下,添加了一个字段访问以以NullReferenceException
与调用相同的方式触发。
但是,如果规则需要在评估参数之前进行空检查,则需要为每个调用添加显式检查。在实践中,这意味着您在NullReferenceException
尝试可能导致NullReferenceException
被抛出的事情之前进行了抛出。(他们无法删除将低地址内存访问冲突转变为的逻辑,NullReferenceException
因为它仍然以其他方式出现)。
所以你建议的规则在实践中需要更多的工作。
有关的:
C# 仅在 .NET 开发中已经在内部使用时才添加了禁止在空实例上调用方法的规则,尽管尚未公开发布。
毕竟,通过编译为 CIL 指令call
而不是.NET,在 .NET 中的空实例上调用非虚拟方法通常是完全合法的callvirt
。(就此而言,您可以以非虚拟方式调用虚拟方法,这就是调用的base
工作方式)。只要实例上没有字段访问或虚拟方法调用(这在实践中很少见,但可能发生),这将起作用。
在此之前,规则是只有当方法是虚拟方法时才需要进行空检查。
这与以前的方法相同;callvirt
如果在空引用上调用该方法并捕获内存访问冲突。
当规则更改为(不幸的是,IMO)禁止对空对象的任何调用时,这是通过将编译更改为使用来完成的,callvirt
即使该方法不是虚拟的,因此如果实例为空,则会发生内存访问冲突,结果NullReferenceException
随之而来。