2013-04-17 8 views
9

Mam przydatną metodę użytkową, która pobiera kod i wypluwa zespół w pamięci. (Wykorzystuje CSharpCodeProvider, chociaż nie sądzę, że powinno mieć znaczenia.) Zespół ten działa jak każdy inny z refleksji, ale gdy używana z kluczowych dynamic wydaje się niepowodzeniem z RuntimeBinderException:Próba związania metody dynamicznej na dynamicznie tworzonym złożeniu powoduje, że RuntimeBinderException

„obiektu” nie zawiera definicji „dobrej”

Przykład:

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 

Czy ktoś wie dlaczego DLR nie jest w stanie sobie z tym poradzić? Czy jest coś, co można zrobić, aby naprawić ten scenariusz?

EDIT:

metoda createAssembly:

Uwaga: niektóre z tych rzeczy zawiera metody rozszerzenie, typy niestandardowe itd Należy oczywiste chociaż.

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

Odpowiedz

5

Dokonaj klasę public.

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

Rozwiązuje to problem na moim komputerze.

+0

Głupi błąd z mojej strony. 'dynamic' respektuje dostępność, więc nie może wykonać niepublicznego kodu z innego zespołu, podczas gdy refleksja nie dba o dostępność. Dzięki. – MgSam

0

To może być krycie.

//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: Staram się pisać to jako komentarz (ponieważ jest to nie workarround odpowiedź), ale nie mógł tego zrobić beacuse to na długo ....

Powiązane problemy