Rufen Sie die statische Methode mit Reflexion auf

111

Ich habe mehrere statische Klassen im Namespace mySolution.Macroswie

static class Indent{    
     public static void Run(){
         // implementation
     }
     // other helper methods
}

Meine Frage ist also, wie es möglich sein wird, diese Methoden mit Hilfe der Reflexion aufzurufen.

Wenn die Methoden NICHT statisch sein sollten, könnte ich so etwas tun:

var macroClasses = Assembly.GetExecutingAssembly().GetTypes().Where( x => x.Namespace.ToUpper().Contains("MACRO") );

foreach (var tempClass in macroClasses)
{
   var curInsance = Activator.CreateInstance(tempClass);
   // I know have an instance of a macro and will be able to run it

   // using reflection I will be able to run the method as:
   curInsance.GetType().GetMethod("Run").Invoke(curInsance, null);
}

Ich möchte meine Klassen statisch halten. Wie kann ich mit statischen Methoden etwas Ähnliches tun?

Kurz gesagt, ich möchte alle Run-Methoden aus allen statischen Klassen aufrufen, die sich im Namespace mySolution.Macros befinden.

Tono Nam
quelle

Antworten:

150

Wie in der Dokumentation zu MethodInfo.Invoke angegeben , wird das erste Argument für statische Methoden ignoriert, sodass Sie einfach null übergeben können.

foreach (var tempClass in macroClasses)
{
   // using reflection I will be able to run the method as:
   tempClass.GetMethod("Run").Invoke(null, null);
}

Wie der Kommentar hervorhebt, möchten Sie möglicherweise sicherstellen, dass die Methode beim Aufruf statisch ist GetMethod:

tempClass.GetMethod("Run", BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
Lee
quelle
4
Möglicherweise möchten Sie einige Bindungsflags an übergeben GetMethod.
Daniel A. White
2
Ohne BindingFlags.StaticSie kann die Methode überhaupt nicht erfolgreich bekommen ...
ErikE
1
Möglicherweise möchten Sie BindingFlags.FlattenHierarchy hinzufügen, wenn sich die Methode in einer Vorgängerklasse befindet.
J. Ouwehand
20

Sie können Ihren Code wirklich, wirklich, wirklich optimieren, indem Sie den Preis für das Erstellen des Delegaten nur einmal bezahlen (es ist auch nicht erforderlich, die Klasse zu instanziieren, um eine statische Methode aufzurufen). Ich habe etwas sehr Ähnliches gemacht und nur einen Delegierten mit Hilfe einer Hilfsklasse für die "Run" -Methode zwischengespeichert :-). Es sieht aus wie das:

static class Indent{    
     public static void Run(){
         // implementation
     }
     // other helper methods
}

static class MacroRunner {

    static MacroRunner() {
        BuildMacroRunnerList();
    }

    static void BuildMacroRunnerList() {
        macroRunners = System.Reflection.Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(x => x.Namespace.ToUpper().Contains("MACRO"))
            .Select(t => (Action)Delegate.CreateDelegate(
                typeof(Action), 
                null, 
                t.GetMethod("Run", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)))
            .ToList();
    }

    static List<Action> macroRunners;

    public static void Run() {
        foreach(var run in macroRunners)
            run();
    }
}

Auf diese Weise ist es VIEL schneller.

Wenn sich Ihre Methodensignatur von Action unterscheidet, können Sie die Typumwandlungen und Typeof von Action durch einen der erforderlichen generischen Action- und Func-Typen ersetzen oder Ihren Delegaten deklarieren und verwenden. Meine eigene Implementierung verwendet Func, um Objekte hübsch zu drucken:

static class PrettyPrinter {

    static PrettyPrinter() {
        BuildPrettyPrinterList();
    }

    static void BuildPrettyPrinterList() {
        printers = System.Reflection.Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(x => x.Name.EndsWith("PrettyPrinter"))
            .Select(t => (Func<object, string>)Delegate.CreateDelegate(
                typeof(Func<object, string>), 
                null, 
                t.GetMethod("Print", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)))
            .ToList();
    }

    static List<Func<object, string>> printers;

    public static void Print(object obj) {
        foreach(var printer in printers)
            print(obj);
    }
}
Loudenvier
quelle
0

Ich bevorzuge Einfachheit ...

private void _InvokeNamespaceClassesStaticMethod(string namespaceName, string methodName, params object[] parameters) {
    foreach(var _a in AppDomain.CurrentDomain.GetAssemblies()) {
        foreach(var _t in _a.GetTypes()) {
            try {
                if((_t.Namespace == namespaceName) && _t.IsClass) _t.GetMethod(methodName, (BindingFlags.Static | BindingFlags.Public))?.Invoke(null, parameters);
            } catch { }
        }
    }
}

Verwendung...

    _InvokeNamespaceClassesStaticMethod("mySolution.Macros", "Run");

Aber für den Fall, dass Sie etwas Robusteres suchen, einschließlich der Behandlung von Ausnahmen ...

private InvokeNamespaceClassStaticMethodResult[] _InvokeNamespaceClassStaticMethod(string namespaceName, string methodName, bool throwExceptions, params object[] parameters) {
    var results = new List<InvokeNamespaceClassStaticMethodResult>();
    foreach(var _a in AppDomain.CurrentDomain.GetAssemblies()) {
        foreach(var _t in _a.GetTypes()) {
            if((_t.Namespace == namespaceName) && _t.IsClass) {
                var method_t = _t.GetMethod(methodName, parameters.Select(_ => _.GetType()).ToArray());
                if((method_t != null) && method_t.IsPublic && method_t.IsStatic) {
                    var details_t = new InvokeNamespaceClassStaticMethodResult();
                    details_t.Namespace = _t.Namespace;
                    details_t.Class = _t.Name;
                    details_t.Method = method_t.Name;
                    try {
                        if(method_t.ReturnType == typeof(void)) {
                            method_t.Invoke(null, parameters);
                            details_t.Void = true;
                        } else {
                            details_t.Return = method_t.Invoke(null, parameters);
                        }
                    } catch(Exception ex) {
                        if(throwExceptions) {
                            throw;
                        } else {
                            details_t.Exception = ex;
                        }
                    }
                    results.Add(details_t);
                }
            }
        }
    }
    return results.ToArray();
}

private class InvokeNamespaceClassStaticMethodResult {
    public string Namespace;
    public string Class;
    public string Method;
    public object Return;
    public bool Void;
    public Exception Exception;
}

Die Nutzung ist ziemlich gleich ...

_InvokeNamespaceClassesStaticMethod("mySolution.Macros", "Run", false);
Dynamichael
quelle
2
Das Schlucken einer möglichen Ausnahme ist normalerweise eine schlechte Idee.
D Stanley
Wahr. Es war faul, aber einfach. Ich habe meine Antwort verbessert.
Dynamichael