views:

409

answers:

2

Hi,

I'm trying to read my compiled C# code.

this is my code:

using(OleDbCommand insertCommand = new OleDbCommand("...", connection))
{
   // do super stuff
}

But!

We all know that a using gets translated to this:

{
    OleDbCommand insertCommand = new OleDbCommand("...", connection)
    try
    {
        //do super stuff
    }
    finally
    {
        if(insertCommand != null)
            ((IDisposable)insertCommand).Dispose();
    }
}

(since OleDbCommand is a reference type).

But when I decompile my assembly (compiled with .NET 2.0) I get this in Resharper:

try
{
    insertCommand = new OleDbCommand("", connection);
Label_0017:
    try
    {
       //do super stuff
    }
    finally
    {
    Label_0111:
        if ((insertCommand == null) != null)
        {
            goto Label_0122;
        }
        insertCommand.Dispose();
    Label_0122:;
    }

I'm talking about this line: if ((insertCommand == null) != null).

Let's say insertCommand IS null. Then the first part returns true. (true != null) returns true. So Then the disposing is still skipped? Weird, very weird.

If I paste this in Visual Studio, Resharper already warns me: Expression is always true...

Thanks!

-Kristof

A: 

when you decompile code, you are not guaranteed to get the original code back. When .net code is compile in to IL, it is optimized. Sometimes you will see some crazy when an application translates the IL back into C#. This doesn't mean the code doesn't work, it is just how the application (resharper in this case) translated the IL.

If you are worried about it, I would look directly at the IL to see what it was compiled into.

Side Note: decompiled IL into C# or VB.net is not guaranteed to compile. :)

Another product to try is Reflector

Tony
I already use Reflector :)
Snake
Reflector definitely contains lots of bugs. I reported one myself, and never got even a reply. http://www.red-gate.com/MessageBoard/viewtopic.php?t=8858
Mark Byers
+11  A: 

The decompiler has a bug. This line

if ((insertCommand == null) != null) 

should have been decompiled to

if ((insertCommand == null) != false)

which, though needlessly verbose, is at least correct code.

The decompiler probably does this unnecessarily verbose version because the C# compiler often chooses to emit

if (x)
   Y();
Z();

as if you'd written

if (!x)
    goto L;
Y();
L: Z();

Since the code generated for both programs is the same, the decompiler doesn't always know which one is the more sensible code to display.

The reason for the unexpected "!= false" is because when we generate IL that tests whether something is true, the fastest and most compact code we can generate is to test whether it is not false. False is represented as zero in IL, and there's a cheap instruction for "is this thing zero?"

Eric Lippert
Is it legitimate to call this a bug in the decompiler? It is possible the user wrote code which looked like the 2nd case, so the compiler has no real way to know which one to pick. Or are you proposing the compiler should favor avoiding gotos in this type of situation since most users don't use them.
Brian
@Brian: The compiler has no way of avoiding gotos; *an if statement is a goto*. An if statement is just a pleasant way of writing a conditional goto; don't fool yourself into thinking that it is anything other than that. That said, a good heuristic for a decompiler is to decompile to the code the user more likely wrote when it is ambiguous. The bug is that the decompilation of a comparison of an integer to zero is being generated as a comparison to null, which is not legal C#. It should be generated as a comparison to false.
Eric Lippert
@Eric: Sorry, I meant to say ask if you were proposing the *de*compiler should favor avoiding gotos. Which isn't the issue, as you point out.
Brian