2

For a simple Method with no local variables like the following

public static int Test1(short i, long j)
{
    j = i + j;

    switch (j)
    {
    case 1:
        j = 2;
        break;
    default:
        j = 11;
        break;
    }

    return j;
}

The count of MethodInfo.GetMethodBody().LocalVariables.Count = 2 WHY? Add another switch statement and the count becomes 3 WHY?

public static int Test1(short i, long j)
{
    j = i + j;

    switch (j)
    {
    case 1:
        j = 2;
        break;
    default:
        j = 11;
        break;
    }

    switch (i)
    {
    case 1:
        j = 2;
        break;
    default:
        j = 11;
        break;
    }

    return j;
}

No local variables are defined. So why 2 and 3. Also if another switch statement with j keeps the count at 2.

svick
  • 236,525
  • 50
  • 385
  • 514
Paul Ash
  • 21
  • 2
  • 1
    Use [ILdasm](http://msdn.microsoft.com/en-us/library/f7dy01k1.aspx) or any other IL disassembler and find out what the variables are used for. – dtb Mar 17 '12 at 00:35
  • This doesn't even compile due to the implicit cast of `long` (`j`) to `int` (return value). – Joshua Honig Mar 17 '12 at 00:37
  • Bit of a compiler flaw, it is adding local variables that it doesn't actually use. These kind of locals are otherwise common. – Hans Passant Mar 17 '12 at 01:41

1 Answers1

2

The fact that C# compiler generates local variables that are not in your C# source code is to be expected, I think. That's because the IL stack is not always a great place to store some temporary values, because you can only access its top.

And this applies especially for debug builds, because those are optimized for debugging, not for performance or memory footprint. I have no idea how do those locals help the debugger, or whether they help at all, but I'm assuming they do have their point.

Specifically, your method actually won't compile, as jmh_gr pointed out, because you can't implicitly cast long to int. If I change the type of j to int, it produces code like this when using debug configuration (the decompiled by using Reflector, with optimizations disabled):

public static int Test1(short i, int j)
{
    int CS$1$0000;
    int CS$4$0001;
    j = i + j;
    CS$4$0001 = j;
    if (CS$4$0001 != 1)
    {
        goto Label_0013;
    }
    j = 2;
    goto Label_0019;
Label_0013:
    j = 11;
Label_0019:
    CS$1$0000 = j;
Label_001D:
    return CS$1$0000;
}

So, you see, the method actually has two locals, and both are used. When using the release configurations, the generated IL has only one local variable and it looks like this:

public static int Test1(short i, int j)
{
    int CS$0$0000;
    j = i + j;
    CS$0$0000 = j;
    if (CS$0$0000 != 1)
    {
        goto Label_0010;
    }
    j = 2;
    goto Label_0014;
Label_0010:
    j = 11;
Label_0014:
    return j;
}

It looks like the local shouldn't be necessary, but maybe there is a good reason for it. And of course, what really matters for performance is the JIT compiled assembly, not the IL code.

svick
  • 236,525
  • 50
  • 385
  • 514
  • OK there is a method to the madness and the method body is not mad. Here is my logical deduction so far. In Debug mode the count of Local variables is not predictable. In release mode it is a puppy who is slightly well behaved. Instance methods will always have the object itself as the first argument. Some times(???) switch statements generates an extra local variable. – Paul Ash Mar 22 '12 at 18:11
  • 1
    I don't think it's that simple. The compiler can generate any code it likes, as long as it does what the source code says. And the translation into IL gets even more complicated when you consider things like lambdas, iterator blocks or `dynamic`. Also, even a normal `switch` can be sometimes compiled into a hash table. So, the only way to know for sure what code does a certain version of the compiler produce, you have to actually compile the code. – svick Mar 22 '12 at 18:23