Appeler la méthode statique avec réflexion

111

J'ai plusieurs classes statiques dans l'espace de noms mySolution.Macrostelles que

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

Ma question est donc de savoir comment il sera possible d'appeler ces méthodes à l'aide de la réflexion?

Si les méthodes ne sont PAS statiques, je pourrais faire quelque chose comme:

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

J'aimerai garder mes classes statiques. Comment pourrai-je faire quelque chose de similaire avec des méthodes statiques?

En bref, j'aimerais appeler toutes les méthodes Run de toutes les classes statiques qui se trouvent dans l'espace de noms mySolution.Macros.

Tono Nam
la source

Réponses:

150

Comme l'indique la documentation de MethodInfo.Invoke , le premier argument est ignoré pour les méthodes statiques afin que vous puissiez simplement passer null.

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

Comme l'indique le commentaire, vous pouvez vous assurer que la méthode est statique lors de l'appel GetMethod:

tempClass.GetMethod("Run", BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
Lee
la source
4
vous voudrez peut-être passer des indicateurs de liaison à GetMethod.
Daniel A. White
2
Sans BindingFlags.Staticvous risquez de ne pas réussir à obtenir la méthode en premier lieu ...
ErikE
1
Vous souhaiterez peut-être ajouter BindingFlags.FlattenHierarchy si la méthode réside dans une classe ancêtre.
J.Ouwehand
20

Vous pouvez vraiment, vraiment, vraiment optimiser beaucoup votre code en payant le prix de la création du délégué une seule fois (il n'est pas non plus nécessaire d'instancier la classe pour appeler une méthode statique). J'ai fait quelque chose de très similaire, et je mets juste en cache un délégué à la méthode "Run" à l'aide d'une classe d'assistance :-). Cela ressemble à ceci:

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

C'est BEAUCOUP plus rapide de cette façon.

Si votre signature de méthode est différente de Action, vous pouvez remplacer les types-cast et typeof de Action par l'un des types génériques Action et Func nécessaires, ou déclarer votre Delegate et l'utiliser. Ma propre implémentation utilise Func pour imprimer de jolis objets:

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
la source
0

Je préfère la simplicité ...

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

Usage...

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

Mais au cas où vous cherchez quelque chose d'un peu plus robuste, y compris la gestion des exceptions ...

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

L'utilisation est à peu près la même ...

_InvokeNamespaceClassesStaticMethod("mySolution.Macros", "Run", false);
dynamichael
la source
2
avaler toute exception possible est généralement une mauvaise idée.
D Stanley
Vrai. C'était paresseux mais simple. J'ai amélioré ma réponse.
dynamichael