2013-04-17 4 views
9

У меня есть удобный полезный метод, который принимает код и выплескивает сборку в памяти. (Он использует CSharpCodeProvider, хотя я не думаю, что должно иметь значения.) Эта сборка работает как любой другой с отражением, но при использовании с dynamic ключевого слова, это, кажется, не в состоянии с RuntimeBinderException:Попытка связать динамический метод с динамически созданной сборкой вызывает 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 не умеет справиться с этим? Есть ли что-то, что можно было бы сделать, чтобы исправить этот сценарий?

EDIT:

метод 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; 
} 

ответ

5

сделать свой класс public.

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

Это решение проблемы на моей машине.

+0

Глупые ошибки с моей стороны. 'dynamic' уважает доступность, поэтому он не может выполнять непубличный код с другой сборки, в то время как отражение не имеет отношения к доступности. Благодарю. – MgSam

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: Я пытаюсь опубликовать это как комментарий (так как это workarround не ответ), но не может сделать это потому что она находится на долго ....

Смежные вопросы