新的 .NET 运行时是否会引发更有意义的空引用异常
本文关键字:更有意义 引用 异常 NET 运行时 是否 新的 | 更新日期: 2023-09-27 18:20:36
我们知道 .NET 运行时在引发异常时不是很有用,因为它只显示泛型消息,而不指示任何变量或参数名称。但它能以不同的方式做吗?
例如,在这种情况下:
class foo
{
public void bar() {}
}
foo f = null;
f.var(); // NullReferenceException
但是从 C# 6 开始,如果我们使用 new ?
运算符,编译器能够生成不同的代码,因此它能够检查f
是否为 null;
f?.var();
它不能像使用 ?
时那样使用类似的空检查来包装调用并获取f
的名称并创建类似 exeption 消息吗
其他信息:对象引用"Foo类型f"未设置为对象的实例。
是否有可能将其也用于其他扩展类型并在那里放置有意义的信息,或者无论这意味着什么,它是否太昂贵了?
No.如果可以的话,他们早就做到了。布拉德·亚当斯(Brad Adams(早在2004年就在博客上写过它
。出现 NullReferenceException 是因为像"调用"这样的指令 [EAX+44]"或"Mov EDX, [ESI+24]"导致访问冲突。 我们没有保留足够的信息来形成通信 在特定 EIP 处的特定寄存器为 NULL 和 申请中的特定引用为空的事实。 特别是因为 EIP 可能位于共享帮助程序中,例如写入 屏障例程或阵列帮助程序。 在这些情况下,我们将不得不 执行有限的堆栈遍历以获得有效的 EIP。
改进此错误消息所需的机制是 巨大。 在可预见的未来,您将不得不依赖调试器, 或在FX代码上明确检查并抛出适当的 NullArgumentException.
如您所见,可用于提供有意义的错误消息的信息很少,并且创建机制也需要付出很多努力。你不太可能在 .Net 中看到此功能。
想想你自己试图在整个代码中实现这样的异常。你要做的是在每次使用之前对每个引用类型变量进行空检查,如果变量名称为 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 模式编译也不会这样做。
我希望我能有所帮助。