This question already has an answer here:
CLR System.NullReferenceException when forcing 'Set Next Statement' into 'if' block

(1个答案)


5年前关闭。




为什么这会增加?
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!");
            }
        }
    }
}

最佳答案

查看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
  {

拖动执行光标时,存在损坏调用堆栈的风险。这是因为拖动光标实际上会跳过这些行。在调试器中运行时,在按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

您可以在例程的第一行看到它创建了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!");
        }
    }

产生工作代码。检查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
  {

编译器足够好,可以将字符串数组的创建从try外部移到try内部,因此跳过该行仍会生成有效对象。该代码有效,因此我猜测NullReferenceException确实是Program类的实例。

关于c# - 在Visual Studio中更改下一行执行时的意外后果,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30808551/

10-13 08:22