.netdecompilingicsharpcodeilspy

Lambda expression's nested classes missing from syntax tree with ICSharpCode Decompiler


Apparently ICSharpCode Decompiler somehow doesn't decompile inner classes that were created by the C# compiler for a lambda expression. Consider the following example:

In certain cases a lambda expression in C# will be compiled into an inner class with a method containing the lambda's body. E.g. a C# code like this:

class MyClass
{
    public void MyMethod()
    {
        Parallel.For(0, 10, i =>
        {
            ... = 3 * i;
        })
    }
}

Will result in the compiler adding an inner class like below:

class MyClass
{
    public void MyMethod()
    ...

    public class c__DisplayClass2()
    {
        public int i;

        public void b__0()
        {
            ... = 3 * i;
        }
    }
}

(Maybe not exactly like this but you get the idea.)

Now the problem is that when I try to build an AST programmatically from the assembly of MyClass by using ICSharpCode.Decompiler.Ast.AstBuilder these inner classes aren't included in the AST (everything else is fine). I can even see these generated classes among the annotations of MyClass's TypeDecleration: the annotation with the type Mono.Cecil.TypeDefinition correctly lists these inner classes in its NestedTypes property (so they were properly loaded from the assembly but not added to the syntax tree; also other, manually created inner classes are properly decompiled).

Also see this ILSpy issue I opened: https://github.com/icsharpcode/ILSpy/issues/686

Am I missing something obvious here? I also looked at the assembly from ILSpy using the GUI and there the code in question is properly decompiled (though not with an inner class but rather the lambda is reconstructed).


Solution

  • I found out the problem: you need to run astBuilder.RunTransformations(); before working with the syntax tree, that will also re-create delegates.

    Before I did this:

    var assembly = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
    var decompilerContext = new DecompilerContext(assembly.MainModule);
    var astBuilder = new AstBuilder(decompilerContext);
    astBuilder.AddAssembly(assembly);
    
    var syntaxTree = astBuilder.SyntaxTree;
    

    However for the syntax tree to be properly initialized you need this:

    var assembly = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
    var decompilerContext = new DecompilerContext(assembly.MainModule);
    var astBuilder = new AstBuilder(decompilerContext);
    astBuilder.AddAssembly(assembly);
    astBuilder.RunTransformations(); // This is new.
    
    var syntaxTree = astBuilder.SyntaxTree;