2013-04-17 15 views
9

Ho un pratico metodo di utilità che prende il codice e sputa un assembly in memoria. (Usa CSharpCodeProvider, anche se io non credo che dovrebbe importa.) Questo gruppo funziona come qualsiasi altro con la riflessione, ma quando viene utilizzato con la parola chiave dynamic, sembra non riuscire con un RuntimeBinderException:Il tentativo di associare un metodo dinamico a un assieme creato in modo dinamico causa una RuntimeBinderException

'oggetto' non contiene una definizione per 'Sound'

Esempio:

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 

qualcuno sa il motivo per cui la DLR, non è in grado di gestire questa situazione? C'è qualcosa che potrebbe essere fatto per risolvere questo scenario?

EDIT:

metodo CreateAssembly:

responsabilità: alcune di queste cose contiene i metodi di estensione, i tipi personalizzati, ecc dovrebbe essere auto-esplicativo però.

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

risposta

5

Fai la tua classe public.

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

Questo risolve il problema sulla mia macchina.

+0

Errore stupido da parte mia. 'dynamic' rispetta l'accessibilità, quindi non può eseguire codice non pubblico da un altro assembly, mentre la riflessione non interessa un po 'dell'accessibilità. Grazie. – MgSam

0

Questo potrebbe essere un workarround.

//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: cerco di postare questo come un commento (in quanto si tratta di un non workarround una risposta), ma non poteva farlo Allucinante è lungo ....

Problemi correlati