我正在解释来自C#Windows Phone应用程序的异常报告。方法抛出NullReferenceException
。该方法是:
public void OnDelete(object o, EventArgs a)
{
if (MessageBox.Show(Res.IDS_AREYOUSURE, Res.IDS_APPTITLE, MessageBoxButton.OKCancel) == MessageBoxResult.OK)
m_Field.RequestDelete();
}
这与
m_Field
为null一致-根本没有其他可能为null的东西。但这是神秘的部分。异常对象的
GetILOffset()
中的StackFrame
中的StackTrace
返回0x13。如ILDASM所示,该方法的MSIL为:IL_0000: call string App.Res::get_IDS_AREYOUSURE()
IL_0005: call string App.Res::get_IDS_APPTITLE()
IL_000a: ldc.i4.1
IL_000b: call valuetype (...) System.Windows.MessageBox::Show(...)
IL_0010: ldc.i4.1
IL_0011: bne.un.s IL_001e
IL_0013: ldarg.0
IL_0014: ldfld class App.Class2 App.Class1::m_Field
IL_0019: callvirt instance void App.Class2::RequestDelete()
IL_001e: ret
这是我不明白的。如果偏移量确实为0x13,则意味着
ldarg
行会导致异常。但是该命令记录为没有引发任何异常。应该扔的是callvirt
,不是吗?还是相对于方法之外的其他地方的偏移量? ldfld
也可以抛出,但是仅当this
对象为null时;这在C#AFAIK中是不可能的。文档提到调试信息可能会影响偏移量,但这是发布版本。
我正在使用ILDASM检查的DLL正是我作为XAP一部分提供给Windows Phone Store的DLL。
最佳答案
当JIT生成机器代码时,它还会生成MSIL 机器代码映射。当您在生成的代码中获得异常时,运行时将使用映射来标识IL偏移。
作为优化的一部分,允许JIT对机器指令进行重新排序(启用它们时),这可能导致映射变得更加近似和精细。如果提前进行了字段访问(内存访问相对较慢,有时在需要之前就开始加载它是一件好事),则该异常似乎是由较早的IL指令引发的。
我租用了其中一个调试实用程序来执行以下操作:
然后,我在一个虚拟进程上运行该工具,该进程大致执行了您在问题中显示的内容,并获得了以下内容(发行版):
IL_0000: call 0600000B
IL_0005: call 0600000A
IL_000A: ldc.i4.1
IL_000B: call 0A000014
IL_0010: ldc.i4.1
IL_0011: bne.un.s 30
----
IL_0013: ldarg.0
IL_0014: ldfld 04000001
IL_0019: callvirt 06000004
----
IL_001E: ret
如您所见,
ldarg.0
,ldfld
和callvirt
指令都被同一映射覆盖,因此,如果其中任何一个触发异常,它们都将映射回相同的IL偏移(0x13)。关于c# - NullReferenceException与MSIL,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36682208/