Useless variable in C # to parse a delegate loop?

I tried to take a look at the breakdown of the code posted in this old question , and I found something strange.

Here's the source code for clarity:

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

(Of course, the behavior of this program is unexpected, this is not a question here.)

Here is what I saw when looking at the showdown:

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;
        }
    }
}

What is doing there j? Here's the bytecode:

.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

But the strangest thing. If I changed the source code as follows, replacing i++with 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();
    }
}

I get this:

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

This is exactly what I expected.

Here's the bytecode:

.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

Why did the compiler add jto the first script?

Note. I am using VS 2015 Update 3, .NET Framework 4.5.2, compiling in release mode.

+4
2

, i++, i, .

, , , i. , i - , j i. , add j, . i , , .

:

. ? add i , j?

# . - # IL. , , , , , , JIT-, .

, , # IL , .

+3

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

/ (. (++ x) Postfix (x ++)?).

0

Source: https://habr.com/ru/post/1681342/


All Articles