Rob*_*ino 7 c# debugging visual-studio
为什么这会变成BOOM?
using System;
using System.Linq;
namespace Test
{
internal class Program
{
private static void Main(string[] args)
{
try
{
// 1. Hit F10 to step into debugging.
string[] one = {"1"}; //2. Drag arrow to make this next statement executed
// 3. Hit f5.
Enumerable.Range(1,1)
.Where(x => one.Contains(x.ToString()));
}
catch (Exception exception)
{
Console.Write("BOOM!");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
查看 ILDASM 输出,这里可能有一个解释......
.locals init ([0] class Test.Program/'<>c__DisplayClass1' 'CS$<>8__locals2',
[1] class [mscorlib]System.Exception exception,
[2] string[] CS$0$0000)
IL_0000: nop
.try
{
IL_0001: newobj instance void Test.Program/'<>c__DisplayClass1'::.ctor()
IL_0006: stloc.0
IL_0007: nop
IL_0008: ldloc.0
IL_0009: ldc.i4.1
IL_000a: newarr [mscorlib]System.String
IL_000f: stloc.2
IL_0010: ldloc.2
IL_0011: ldc.i4.0
IL_0012: ldstr "1"
IL_0017: stelem.ref
IL_0018: ldloc.2
IL_0019: stfld string[] Test.Program/'<>c__DisplayClass1'::one
IL_001e: ldc.i4.1
IL_001f: ldc.i4.1
IL_0020: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32,
int32)
IL_0025: ldloc.0
IL_0026: ldftn instance bool Test.Program/'<>c__DisplayClass1'::'<Main>b__0'(int32)
IL_002c: newobj instance void class [mscorlib]System.Func`2<int32,bool>::.ctor(object,
native int)
IL_0031: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>,
class [mscorlib]System.Func`2<!!0,bool>)
IL_0036: pop
IL_0037: nop
IL_0038: leave.s IL_004a
} // end .try
catch [mscorlib]System.Exception
{
Run Code Online (Sandbox Code Playgroud)
当您拖动执行光标时,您将面临损坏调用堆栈的风险。这是因为拖动光标实际上会跳过这些行。在调试器中运行时,按 F10 后,光标会停在例程开始处Main
、尝试之前。如果将光标拖动到数组的创建位置,您将跳过这一有趣的行:
IL_0001: newobj instance void Test.Program/'<>c__DisplayClass1'::.ctor()
它创建了该类的一个实例Program
。稍后将在此处使用该程序类:
IL_0019: stfld string[] Test.Program/'<>c__DisplayClass1'::one
因为您跳过了它,所以没有创建该对象,所以您NullReferenceException
在运行时会得到一个。
为什么人们不能在 VS2012 上重现这个,我不确定。也许编译器正在输出不同的 IL,但这是我使用 VS2013 Ultimate 和 C#4.5 所能想到的。
有趣的是,当你注释掉 try/catch 时,IL 中程序的开头如下所示:
.locals init ([0] class Test.Program/'<>c__DisplayClass1' 'CS$<>8__locals2',
[1] string[] CS$0$0000)
IL_0000: newobj instance void Test.Program/'<>c__DisplayClass1'::.ctor()
IL_0005: stloc.0
Run Code Online (Sandbox Code Playgroud)
您可以看到例程中的第一行创建了Program
对象。为什么编译器决定将该行放在 try/catch 中,这超出了我的理解。
编辑
深入挖掘一下,将您的程序更改为:
private static void Main(string[] args)
{
string[] one;
try
{
// 1. Hit F10 to step into debugging.
one = new string[] { "1" }; //2. Drag arrow to this
// 3. Hit f5.
Enumerable.Range(1, 1)
.Where(x => one.Contains(x.ToString()));
}
catch (Exception exception)
{
Console.Write("BOOM!");
}
}
Run Code Online (Sandbox Code Playgroud)
结果是工作代码。检查 IL,您可以看到实例创建已移至 try 之外:
.locals init ([0] class [mscorlib]System.Exception exception,
[1] class [mscorlib]System.Func`2<int32,bool> 'CS$<>9__CachedAnonymousMethodDelegate1',
[2] class Test.Program/'<>c__DisplayClass2' 'CS$<>8__locals3',
[3] string[] CS$0$0000)
IL_0000: ldnull
IL_0001: stloc.1
IL_0002: newobj instance void Test.Program/'<>c__DisplayClass2'::.ctor()
IL_0007: stloc.2
IL_0008: nop
.try
{
Run Code Online (Sandbox Code Playgroud)
编译器足够好,可以将字符串数组的创建从 try 外部移至 try 内部,因此跳过该行仍然会产生有效对象。该代码有效,所以我猜测确实NullReferenceException
是该类的实例Program
。
归档时间: |
|
查看次数: |
238 次 |
最近记录: |