Why doesn't dynamic keyword work with dynamically loaded assemblies?

1.4k views Asked by At

I'm working on a CSharp expression evaluator which can be used as you can see below. This component generates code and compiles it in memory and after that, it loads the generated assembly, creates an instance of the generated class and run it. Results are saved in a dictionary.

My problem is that all run ok until the last line of code where it fails with the exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException 'object' does not contain a definition for 'FirstName'.

However, Visual Studio is able to show me the professional variable content:

Professional = { FirstName = Juan Pablo, 
                LastName = Ibañez, 
                Certifications = <>f__AnonymousType0`3[System.String,System.String,System.String][], 
                SayHi = System.Action }

And this is the code:

    static void Main(string[] args)
    {
        const string data = @"results[""professional""] = 
        new
        {
            FirstName = ""Juan Pablo"",
            LastName = ""Ibañez"",
            Certifications = new[]
            {
                new { Provider=""MSFT"", ExamCode = ""70-536"", Title = ""TS: Microsoft .NET Framework – Application Development Foundation"" },
                new { Provider=""MSFT"", ExamCode = ""70-505"", Title = ""TS: Microsoft .NET Framework – Application Development Foundation"" },
                new { Provider=""MSFT"", ExamCode = ""70-563"", Title = ""TS: Microsoft .NET Framework – Application Development Foundation"" }
            },

            SayHi = new System.Action(()=> System.Console.WriteLine(""Hi""))
        };";

        var eval = CSharpEvaluator.Evaluate(data); // return a disctionary<string, object>
        dynamic professional = eval["professional"];
        Console.WriteLine("First Name: {0}", professional.FirstName);

Any ideas?

Thank you.

1

There are 1 answers

2
Jon Skeet On

EDIT: I think I've got it. Anonymous types are internal, so the type isn't visible to the dynamic binder. Verifying now...

Yup, that's it. Note the exact error message: 'object' does not contain a definition for 'FirstName'".

It's using object because that's the only type you could actually refer to the object as from within your own assembly. This has nothing to do with the assembly being loaded dynamically - it's simply a matter of it being in a different assembly.

EDIT: You can get round this using the [InternalsVisibleTo] attribute, if you can make your "publishing" assembly apply that. Here's an example:

// In Library.dll
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("Test")]

public class Library
{
    public static object Foo()
    {
        return new { ID = 1 };
    }
}

// In Test.exe
using System;

class Test
{
    static void Main()
    {
        dynamic d = Library.Foo();
        Console.WriteLine(d.ID);
    }
}

If you comment out the attribute, Test.exe breaks as per your question. With the attribute, it works fine. The C# compiler authors think of everything :)