我有一个方便的实用程序方法,它接受代码并输出内存中的程序集。(它使用CSharpCodeProvider
,尽管我认为这应该无关紧要。)这个程序集与反射一样工作,但是当与dynamic
关键字一起使用时,它似乎与RuntimeBinderException
:
"object"不包含"Sound"的定义
的例子:
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
有谁知道DLR不能处理这个的原因吗?有什么办法可以解决这个问题吗?
编辑:createAssembly方法:
免责声明:其中一些内容包含扩展方法,自定义类型等。它应该是不言自明的。
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;
}
让你的类public
。
var assembly = createAssembly("public class Dog { public string Sound() ...
^
解决了我机器上的问题
这可能是一个变通办法。
//instead of this
//dynamic dog2 = dog;
//try this
dynamic dog2 = DynWrapper(dog);
String test2Result = dog2.Sound();//Now this works.
public class DynWrapper : DynamicObject {
private readonly object _target;
public DynWrapper(object target) {
_target = target;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
//for the sake of simplicity I'm not taking arguments into account,
//of course you should in a real app.
var mi = _target.GetType().GetMethod(binder.Name);
if (mi != null) {
result = mi.Invoke(_target, null);
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
PS:我试着把这个作为一个评论(因为它是一个解决方案,而不是一个答案),但不能这样做,因为它是长....