9

我有一个方便的实用程序方法,它获取代码并吐出一个内存中的程序集。(它使用CSharpCodeProvider,尽管我认为这无关紧要。)这个程序集的工作方式与其他任何带有反射的程序集一样,但是当与dynamic关键字一起使用时,它似乎失败了RuntimeBinderException

“对象”不包含“声音”的定义

例子:

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;
}
4

2 回答 2

5

做你的课public

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

这解决了我机器上的问题。

于 2013-04-17T17:31:23.143 回答
0

这可能是一种解决方法。

//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:我尝试将此作为评论发布(因为它是一种解决方法而不是答案)但不能这样做,因为它太长了......

于 2013-04-17T17:28:53.117 回答