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.

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

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

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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.