static void Main(string[] args)
{
int x = 0;
x++;
}
we get:
.method private hidebysig static void Main (
string[] args
) cil managed
{
.entrypoint
.locals init (
[0] int32 x
)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldc.i4.1
IL_0005: add
IL_0006: stloc.0
IL_0007: ret
}
Digging into it, IL_0000 (henceforth line 0) is a no-op command, that is, do nothing. Line 1 pushes a 0 onto the stack. Line 2 pops the top value of the stack (the 0 just pushed) into the variable [0]. Line 3 pushes the value of variable [0] onto the stack. Line 4 pushes a 1 onto the stack. Line 5 adds the top two values in the stack and pushes the results. Line 6 pops the stack value into variable [0].
Not very interesting in and of itself, but what if we change from post-increment to pre-increment. What change would we see? Well, none, because the compiler knows that it doesn't matter in this expression. To see a difference, we need to get a little bit more complex. How about something ridiculous like:
static void Main(string[] args)
{
int x = 0;
int y = 0;
int z = 0;
z = x++ + ++y;
}
.method private hidebysig static void Main (
string[] args
) cil managed
{
.entrypoint
.locals init (
[0] int32 x,
[1] int32 y,
[2] int32 z
)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: ldc.i4.0
IL_0004: stloc.1
IL_0005: ldc.i4.0
IL_0006: stloc.2
IL_0007: ldloc.0
IL_0008: dup
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stloc.0
IL_000c: ldloc.1
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: dup
IL_0010: stloc.1
IL_0011: add
IL_0012: stloc.2
IL_0013: ret
}
A bit more complex, but Telerik JustDecompile does a great job turning it into readable C#:
private static void Main(string[] args)
{
int x = 0;
int y = 0;
int z = 0;
int num = x;
x = num + 1;
int num1 = y + 1;
y = num1;
z = num + num1;
}
Notice the use of temporary variables to represent the pushed stack values?
So, that is it for right now, I want to look at some of the more complex expressions so we will stay with 1.4 next time.