代码之家  ›  专栏  ›  技术社区  ›  themiurge

C#中的无用变量用于捕获委托的循环反汇编?

  •  4
  • themiurge  · 技术社区  · 9 年前

    this old question ,我发现了一些奇怪的事情。

    class ThreadTest
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
                new Thread(() => Console.WriteLine(i)).Start();
        }
    }
    

    下面是我在拆卸过程中看到的内容:

    internal class ThreadTest
    {
        private static void Main(string[] args)
        {
            int i;
            int j;
            for (i = 0; i < 10; i = j + 1)
            {
                new Thread(delegate
                {
                    Console.WriteLine(i);
                }).Start();
                j = i;
            }
        }
    }
    

    是什么 j

    .method private hidebysig static 
        void Main (
            string[] args
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 64 (0x40)
        .maxstack 2
        .entrypoint
        .locals init (
            [0] class ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0' 'CS$<>8__locals0',
            [1] int32
        )
    
        IL_0000: newobj instance void ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::.ctor()
        IL_0005: stloc.0
        IL_0006: ldloc.0
        IL_0007: ldc.i4.0
        IL_0008: stfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
        IL_000d: br.s IL_0035
        // loop start (head: IL_0035)
            IL_000f: ldloc.0
            IL_0010: ldftn instance void ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::'<Main>b__0'()
            IL_0016: newobj instance void [mscorlib]System.Threading.ThreadStart::.ctor(object, native int)
            IL_001b: newobj instance void [mscorlib]System.Threading.Thread::.ctor(class [mscorlib]System.Threading.ThreadStart)
            IL_0020: call instance void [mscorlib]System.Threading.Thread::Start()
            IL_0025: ldloc.0
            IL_0026: ldfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
            IL_002b: ldc.i4.1
            IL_002c: add
            IL_002d: stloc.1
            IL_002e: ldloc.0
            IL_002f: ldloc.1
            IL_0030: stfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
    
            IL_0035: ldloc.0
            IL_0036: ldfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
            IL_003b: ldc.i4.s 10
            IL_003d: blt.s IL_000f
        // end loop
    
        IL_003f: ret
    } // end of method ThreadTest::Main
    

    但这是最奇怪的事情。如果我像这样更改原始代码 i++ i = i + 1 :

    class ThreadTest
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i = i + 1)
                new Thread(() => Console.WriteLine(i)).Start();
        }
    }
    

    我明白了:

    internal class ThreadTest
    {
        private static void Main(string[] args)
        {
            int i;
            for (i = 0; i < 10; i++)
            {
                new Thread(delegate
                {
                    Console.WriteLine(i);
                }).Start();
            }
        }
    }
    

    以下是字节码:

    .method private hidebysig static 
        void Main (
            string[] args
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 62 (0x3e)
        .maxstack 3
        .entrypoint
        .locals init (
            [0] class ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0' 'CS$<>8__locals0'
        )
    
        IL_0000: newobj instance void ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::.ctor()
        IL_0005: stloc.0
        IL_0006: ldloc.0
        IL_0007: ldc.i4.0
        IL_0008: stfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
        IL_000d: br.s IL_0033
        // loop start (head: IL_0033)
            IL_000f: ldloc.0
            IL_0010: ldftn instance void ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::'<Main>b__0'()
            IL_0016: newobj instance void [mscorlib]System.Threading.ThreadStart::.ctor(object, native int)
            IL_001b: newobj instance void [mscorlib]System.Threading.Thread::.ctor(class [mscorlib]System.Threading.ThreadStart)
            IL_0020: call instance void [mscorlib]System.Threading.Thread::Start()
            IL_0025: ldloc.0
            IL_0026: ldloc.0
            IL_0027: ldfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
            IL_002c: ldc.i4.1
            IL_002d: add
            IL_002e: stfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
    
            IL_0033: ldloc.0
            IL_0034: ldfld int32 ConsoleApplication2.ThreadTest/'<>c__DisplayClass0_0'::i
            IL_0039: ldc.i4.s 10
            IL_003b: blt.s IL_000f
        // end loop
    
        IL_003d: ret
    } // end of method ThreadTest::Main
    

    编译器为什么添加 j 在第一种情况下?

    2 回复  |  直到 9 年前
        1
  •  3
  •   Peter Duniho    9 年前

    因为在语义上,当你写 i++ i 因此,它可以用作表达式的结果值。

    编译器通过引入一个新变量来实现这一点,在该变量中,新值可以保留到 必要时使用。因此,旧的价值观 一、 j 一、 add 指示 j ,因为实际上没有代码需要该值。但是,暂时 的价值仍然是旧的,如果需要的话可以使用。

    你可能会争辩:

    添加 直接进入 而不是将其存储在 j

    C编译器不负责优化。它的主要工作是将C代码翻译成IL。事实上,我想说这项工作的一部分是 负责优化。

        2
  •  0
  •   wake-0    9 年前

    i++ 不完全是 i = i + 1

    请尝试以下代码:

    int i = 1;
    int x = 5 + i++;
    Console.WriteLine("i:" + i + " x: " + x);
    i = 1;
    int y = 5 + ++i;
    Console.WriteLine("i:" + i + " y: " + y);
    

    输出:

    i:2 x: 6
    i:2 y: 7
    

    How do Prefix (++x) and Postfix (x++) operations work? ).