2013-05-03 4 views
32

Ich brauche eine Methode, die eine MethodInfo Instanz übernimmt, die eine nicht-generische statische Methode mit beliebiger Signatur darstellt und einen an diese Methode gebundenen Delegaten zurückgibt, der später mit der Methode Delegate.DynamicInvoke aufgerufen werden kann. Meine erste naive Versuch sah wie folgt aus:Wie erstelle ich einen Delegaten aus einer MethodInfo, wenn die Methodensignatur nicht vorher bekannt sein kann?

using System; 
using System.Reflection; 

class Program 
{ 
    static void Main() 
    { 
     var method = CreateDelegate(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)})); 
     method.DynamicInvoke("Hello world"); 
    } 

    static Delegate CreateDelegate(MethodInfo method) 
    { 
     if (method == null) 
     { 
      throw new ArgumentNullException("method"); 
     } 

     if (!method.IsStatic) 
     { 
      throw new ArgumentNullException("method", "The provided method is not static."); 
     } 

     if (method.ContainsGenericParameters) 
     { 
      throw new ArgumentException("The provided method contains unassigned generic type parameters."); 
     } 

     return method.CreateDelegate(typeof(Delegate)); // This does not work: System.ArgumentException: Type must derive from Delegate. 
    } 
} 

Ich hoffte, dass die MethodInfo.CreateDelegate Methode den Typ richtig delegieren herausfinden konnte selbst. Nun, offensichtlich kann es nicht. Wie erstelle ich eine Instanz von System.Type, die einen Delegaten mit einer Signatur darstellt, die der angegebenen MethodInfo Instanz entspricht?

+1

Warum wollen Sie versuchen, einen Delegierten erstellen und DynamicInvoke verwenden? DynamicInvoke ist viel langsamer als MethodInfo.Invoke. –

+0

@ Nawfal Nö. Ein Duplikat erfordert, dass die hier gestellte Frage in der von Ihnen genannten Frage beantwortet werden kann. Der Fragesteller möchte 'MethodInfo.CreateDelegate()' verwenden können, wenn der Typ, der die Methodensignatur darstellt, nicht bekannt ist. In der anderen Frage ist dies bereits bekannt als "MyDelegate" und ist daher für das Problem dieses Fragestellers nicht hilfreich. – einsteinsci

+0

Wer zum Teufel löscht meine Kommentare? Nicht das erste Mal! Sorry @einsteinsci Ich kann den Thread, den ich hier gepostet habe, nicht als Duplikat finden, also kann ich nicht inspizieren. Wenn Sie schreiben könnten, würde ich mich freuen. – nawfal

Antwort

29

können Sie System.Linq.Expressions.Expression.GetDelegateType Methode verwenden:

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

class Program 
{ 
    static void Main() 
    { 
     var writeLine = CreateDelegate(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })); 
     writeLine.DynamicInvoke("Hello world"); 

     var readLine = CreateDelegate(typeof(Console).GetMethod("ReadLine", Type.EmptyTypes)); 
     writeLine.DynamicInvoke(readLine.DynamicInvoke()); 
    } 

    static Delegate CreateDelegate(MethodInfo method) 
    { 
     if (method == null) 
     { 
      throw new ArgumentNullException("method"); 
     } 

     if (!method.IsStatic) 
     { 
      throw new ArgumentException("The provided method must be static.", "method"); 
     } 

     if (method.IsGenericMethod) 
     { 
      throw new ArgumentException("The provided method must not be generic.", "method"); 
     } 

     return method.CreateDelegate(Expression.GetDelegateType(
      (from parameter in method.GetParameters() select parameter.ParameterType) 
      .Concat(new[] { method.ReturnType }) 
      .ToArray())); 
    } 
} 

Es ist wahrscheinlich eine Copy-Paste-Fehler in der zweiten Prüfung für !method.IsStatic - Sie nicht ArgumentNullException es verwenden soll. Und es ist ein guter Stil, einen Parameternamen als Argument für ArgumentException bereitzustellen.

Verwenden Sie method.IsGenericMethod, wenn Sie alle generischen Methoden ablehnen möchten, und method.ContainsGenericParameters, wenn Sie nur generische Methoden mit unsubstituierten Typparametern ablehnen möchten.

3

Sie möchten System.Linq.Expressions

... 
using System.Linq.Expressions; 
... 

static Delegate CreateMethod(MethodInfo method) 
{ 
    if (method == null) 
    { 
     throw new ArgumentNullException("method"); 
    } 

    if (!method.IsStatic) 
    { 
     throw new ArgumentException("The provided method must be static.", "method"); 
    } 

    if (method.IsGenericMethod) 
    { 
     throw new ArgumentException("The provided method must not be generic.", "method"); 
    } 

    var parameters = method.GetParameters() 
          .Select(p => Expression.Parameter(p.ParameterType, p.Name)) 
          .ToArray(); 
    var call = Expression.Call(null, method, parameters); 
    return Expression.Lambda(call, parameters).Compile(); 
} 

und verwenden Sie es später wie folgt

var method = CreateMethod(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)})); 
method.DynamicInvoke("Test Test"); 
+3

Diese Lösung hat einen erheblichen Overhead: Sie erstellt einen Ausdrucksbaum, führt einen Ausdrucksbaum-Compiler aus, generiert eine dynamische Methode und erstellt einen Delegaten für diese Methode. Dann durchlaufen alle nachfolgenden Aufrufe an den Delegaten diese unnötige dynamische Proxy-Methode. Es ist viel besser, einen Delegaten zu erstellen, der direkt an die bereitgestellte "MethodInfo" -Instanz gebunden ist. –

+0

@OksanaGimmel Der ganze Prozess wird nur gemacht, um den Delegierten zu bekommen. Sobald Sie die Delegiertenreferenz haben, ist es nur eine Frage des Aufrufs. – nawfal

+0

@nwafal, Obwohl dies optimal als einmalige Initialisierung pro CLR-Host- oder AppDomain-Inkarnation erfolgt, beeinträchtigt dies Oksanas Kommentar nicht, da der Fragesteller nicht angibt, wie oft der Delegat anschließend aufgerufen wird. versus wie viele unterschiedliche Delegaten dieses Typs eine bestimmte Sitzung benötigt. Beachten Sie, dass selbst im besten Fall von Single-Init/Multiple-Use, wenn dies die einzige Verwendung von 'Linq.Expressions' in der App ist, ein erheblicher Treffer für das Auflösen und Laden überschüssiger Bibliotheken und vermutlich auch für den Benutzer entsteht in der schlimmsten Zeit, während Ihres Boot-up. –

Verwandte Themen