2016-10-16 3 views
1

ich Ironpython bin mit und ich weiß, wie Methoden aus meiner Klasse der Skripts Umfang aussetzen:Ironpython - Verborgene Methoden Reflexion mit

m_scope.SetVariable("log", new Action<string>(Log)); 

public void Log(string a) 
{ 
    Console.WriteLine(a); 
} 

Doch anstatt Aufruf SetVariable jedes Mal wenn ich will, den Prozess beschleunigen mit Reflexion. Also, habe ich ein Attribut ScriptMethodAttribute genannt:

public sealed class ScriptMethodAttribute : Attribute 
{ 
    public string Name { get; private set; } 

    public ScriptMethodAttribute(string name) 
    { 
     Name = name; 
    } 
} 

Auf diese Weise kann ich Methoden in meiner Klasse definieren für das Skript zu verwenden, etwa so:

[ScriptMethod("log")] 
public void Log(string a) 
{ 
    Console.WriteLine(a); 
} 

Jetzt möchte ich SetVariable auf jeden anrufen Methode, die dieses Attribut verwendet, um den Prozess zu beschleunigen. Das scheint jedoch nicht zu funktionieren.

Dies ist eine Hilfsmethode, die eine Liste von Tuple<ScriptMethodAttribute, MethodInfo zurückgibt. in meinem Skript Klassenkonstruktors

public static IEnumerable<Tuple<TAttribute, MethodInfo>> FindMethodsByAttribute<TAttribute>() 
     where TAttribute : Attribute 
{ 
    return (from method in AppDomain.CurrentDomain.GetAssemblies() 
        .Where(assembly => !assembly.GlobalAssemblyCache) 
        .SelectMany(assembly => assembly.GetTypes()) 
        .SelectMany(type => type.GetMethods()) 
       let attribute = Attribute.GetCustomAttribute(method, typeof(TAttribute), false) as TAttribute 
       where attribute != null 
       select new Tuple<TAttribute, MethodInfo>(attribute, method)); 
} 

Dieses befindet:

foreach (var a in Reflector.FindMethodsByAttribute<ScriptMethodAttribute>()) 
{ 
    Action action = (Action)Delegate.CreateDelegate(typeof(Action), this, a.Item2); 

    m_scope.SetVariable(a.Item1.Name, action); 
} 

Ich erhalte die folgende Ausnahme:

System.ArgumentException: Cannot bind to the target method because its signature or security transparency is   not compatible with that of the delegate type. 

Ich vermute, es ist, weil ich die erforderliche enthalten haben Typen im Action-Konstruktor, aber ich weiß nicht, wie man sie aus der Klasse MethodInfo bekommt.

Antwort

0

Um Methoden mit einem Parameter zu verarbeiten, können Sie diesen Code verwenden:

var parameters = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray(); 

var delegateType = typeof(Action<>).MakeGenericType(parameters); 

var action = methodInfo.CreateDelegate(delegateType, this); 

// Now you can call m_scope.SetVariable with action 

Dinge werden komplizierter, wenn Sie mit einer beliebigen Anzahl von Parametern behandeln Methoden wollen, weil Sie eine gewisse Verzweigung auf die Abhängigkeit hinzufügen müssen Anzahl der Elemente im parameters Array. Wenn es zwei Elemente gibt, dann müssen Sie Action<,> anstelle von Action<> verwenden, wenn es drei gibt, müssen Sie Action<,,> verwenden, und so weiter.

A nicht so elegant, aber schnelle Art und Weise ein Array mit jeder Art von Aktion wäre vorzuzubelegen:

private Type[] DelegateTypes = new[] 
{ 
    typeof (Action), 
    typeof (Action<>), 
    typeof (Action<,>), 
    typeof (Action<,,>), 
    typeof (Action<,,,>), 
    typeof (Action<,,,,>), 
    typeof (Action<,,,,,>), 
    typeof (Action<,,,,,,>), 
    typeof (Action<,,,,,,,>), 
    typeof (Action<,,,,,,,,>), 
    typeof (Action<,,,,,,,,,>), 
    typeof (Action<,,,,,,,,,,>), 
    typeof (Action<,,,,,,,,,,,>), 
    typeof (Action<,,,,,,,,,,,,>), 
    typeof (Action<,,,,,,,,,,,,,>), 
    typeof (Action<,,,,,,,,,,,,,,>), 
    typeof (Action<,,,,,,,,,,,,,,,>) 
}; 

Von dort aus ist es nur eine Frage des richtigen Index des Arrays zugreifen, je nach Ihre Anzahl an Parametern:

Type delegateType; 

if (parameters.Length == 0) 
    delegateType = DelegateTypes[0]; 
else 
    delegateType = DelegateTypes[parameters.Length].MakeGenericType(parameters); 
+0

Und was, wenn meine Methode überhaupt keine Parameter benötigt? Wie würde ich dann den Typ bekommen? Gibt es auch eine Möglichkeit, den Typ basierend auf der Anzahl der Parameter zu erhalten, so dass ich diese auf jede gewünschte Anzahl von Parametern erweitern kann und mich nicht auf einen beschränken muss? –

+0

Ok, ich verstehe. Darüber habe ich nachgedacht. Definitiv nicht elegant, aber sehr schlau. –

+0

Wenn ich versuche, eine Aktion hinzuzufügen, die keine Parameter akzeptiert, erhalte ich eine InvalidOperatioNException, die besagt: System.Action ist keine GenericTypeDefinition. MakeGenericType kann nur für einen Typ aufgerufen werden, für den Type.IsGenericTypeDefinition True ist. –