新的 .NET 运行时能否抛出更有意义的空引用异常?

Could the new .NET runtime throw more meaningful null reference exceptions?

我们知道 .NET 运行时在抛出异常时不是很有帮助,因为它只显示一般消息,而不指示任何变量或参数名称。但它可以做不同的事情吗?

例如在这种情况下:

class foo
{
    public void bar() {}
}

foo f = null;
f.var(); // NullReferenceException

但是从 C# 6 开始,如果我们使用新的 ? 运算符,编译器能够生成不同的代码,因此它能够检查 f 是否为空;

f?.var(); 

难道它不能像使用 ? 时那样用类似的空检查来包装调用并获取 f 的名称并创建一条异常消息,例如

Additional information: Object reference "f of type Foo" not set to an instance of an object.

是否也可以将它用于其他异常类型并在其中放置有意义的信息,或者它 昂贵吗?

想想你自己试图在你的代码中实现这样的异常。您需要做的是在每次使用之前对每个引用类型变量进行 null 检查,如果它为 null,则抛出一个包含变量名的有意义的异常。

所以它很昂贵而且可能毫无价值,因为如果您的程序集旁边有 PDB 调试数据库,异常详细信息包含确切的行号。

考虑以下代码块:

static void Main(string[] args)
{
    object nullObject = null;
    string nullPointerAccess = nullObject.ToString();
}

这段代码编译器生成的IL如下(我把注释放在生成的IL操作旁边)

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       10 (0xa)
  .maxstack  1
  .locals init ([0] object nullObject) // declare local variable. After this point, the variable has no name. It only has a position.
  IL_0000:  ldnull 
  IL_0001:  stloc.0 // read local variable at position 0 (formerly nullObject)
  IL_0002:  ldloc.0 // load the local variable at position 0 to the stack
  IL_0003:  callvirt   instance string [mscorlib]System.Object::ToString() // call ToString()
  IL_0008:  pop // pop (remove) the return value on top of the stack
  IL_0009:  ret // return
} // end of method Program::Main

您希望编译器将变量名保存在与现有堆栈平行的另一个堆栈中,并在发生空指针异常时访问该堆栈,这还需要保持两个堆栈同步,以便运行时知道正在引用 nullObject ,我认为这不在任何编译器的总体规划中,也不会持续很长时间。

首先,这将使执行程序所需的 cpu 周期增加一倍/三倍。即使是 DEBUG 模式编译也不会这样做。

希望对您有所帮助。

没有。如果可以的话,他们早就做到了。布拉德·亚当斯 has blogged about it back in 2004

The NullReferenceException occurs because an instruction like “call [eax+44]” or “mov edx, [esi+24]” has resulted in an access violation. We don’t retain nearly enough information to form a correspondence between a particular register being NULL at a particular EIP and the fact that a particular reference in the application was null. Especially since the EIP might be in a shared helper like a write barrier routine or an array helper. In those cases, we would have to perform a limited stack walk to get the effective EIP.

The machinery that would be required to improve this error message is huge. For the foreseeable future, you will have to rely on debuggers, or on FX code explicitly checking and throwing an appropriate NullArgumentException.

如您所见,可用于提供有意义的错误消息的信息非常少,而且创建机制也需要付出很多努力。您不太可能在 .Net 中看到此功能。