2013-04-17 15 views
9

Ich habe eine praktische Hilfsmethode, die Code verarbeitet und eine In-Memory-Assembly ausspuckt. (Es verwendet CSharpCodeProvider, obwohl ich nicht glaube, das sollte egal.) Diese Anordnung funktioniert wie jedes andere mit Reflektion, aber wenn sie mit dem dynamic Schlüsselwort verwendet, so scheint es, mit einem RuntimeBinderException zum Scheitern verurteilt:Der Versuch, eine dynamische Methode an einer dynamisch erstellten Assembly zu binden, verursacht eine RuntimeBinderException.

‚Objekt‘ keine Definition für ‚Sound‘ enthält

Beispiel:

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 

Kennt jemand den Grund, warum das DLR nicht in der Lage ist, dies zu umgehen? Gibt es etwas, das getan werden könnte, um dieses Szenario zu beheben?

EDIT:

Create Methode:

Haftungsausschluss: einige dieser Sachen enthält Erweiterungsmethoden, benutzerdefinierte Typen, etc. Es sollte allerdings selbsterklärend sein.

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

Antwort

5

Machen Sie Ihre Klasse public.

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

Das löst das Problem auf meiner Maschine.

+0

Dummer Fehler meinerseits. 'dynamic' respektiert die Barrierefreiheit, so dass nicht-öffentlicher Code von einer anderen Assembly ausgeführt werden kann, während Reflektion sich nicht um Barrierefreiheit kümmert. Vielen Dank. – MgSam

0

Dies könnte ein Workarround sein.

//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: Ich versuche, dies als einen Kommentar zu schreiben (da es sich um eine workarround keine Antwort ist), aber tun konnte sie nicht beacuse es zu lang ist ....