Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attempting to bind a dynamic method on a dynamically-created assembly causes a RuntimeBinderException

I have a handy utility method which takes code and spits out an in-memory assembly. (It uses CSharpCodeProvider, although I don't think that should matter.) This assembly works like any other with reflection, but when used with the dynamic keyword, it seems to fail with a RuntimeBinderException:

'object' does not contain a definition for 'Sound'

Example:

var assembly = createAssembly("class Dog { public string Sound() { return \"woof\"; } }");
var type = assembly.GetType("Dog");
Object dog = Activator.CreateInstance(type);

var method = type.GetMethod("Sound");
var test1Result = method.Invoke(dog, null); //This returns "woof", as you'd expect

dynamic dog2 = dog;
String test2Result = dog2.Sound(); //This throws a RuntimeBinderException

Does anyone know the reason why the DLR is not able to handle this? Is there anything that could be done to fix this scenario?

EDIT:

createAssembly method:

Disclaimer: some of this stuff contains extension methods, custom types, etc. It should be self-explanatory though.

private Assembly createAssembly(String source, IEnumerable<String> assembliesToReference = null)
{
    //Create compiler
    var codeProvider = new CSharpCodeProvider();

    //Set compiler parameters
    var compilerParameters = new CompilerParameters
    {
        GenerateInMemory = true,
        GenerateExecutable = false,
        CompilerOptions = "/optimize",
    };

    //Get the name of the current assembly and everything it references
    if (assembliesToReference == null)
    {
        var executingAssembly = Assembly.GetExecutingAssembly();
        assembliesToReference = executingAssembly
            .AsEnumerable()
            .Concat(
                executingAssembly
                    .GetReferencedAssemblies()
                    .Select(a => Assembly.Load(a))
            )
            .Select(a => a.Location);
     }//End if

    compilerParameters.ReferencedAssemblies.AddRange(assembliesToReference.ToArray());

    //Compile code
    var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, source);

    //Throw errors
    if (compilerResults.Errors.Count != 0)
    {                
        throw new CompilationException(compilerResults.Errors);                
    }

    return compilerResults.CompiledAssembly;
}
like image 878
MgSam Avatar asked Apr 17 '13 16:04

MgSam


1 Answers

Make your class public.

var assembly = createAssembly("public class Dog { public string Sound() ...
                               ^

That solves the problem on my machine.

like image 91
Robert Harvey Avatar answered Sep 19 '22 15:09

Robert Harvey