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;
}
dynamic
respects the accessibility, so it can't execute non-public code from another assembly, while reflection doesn't care one bit about accessibility. Thanks. – Lilley