web-dev-qa-db-fra.com

Appelez la méthode statique avec réflexion

J'ai plusieurs classes statiques dans l'espace de noms mySolution.Macros, telles que 

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

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

Si les méthodes n'étaient 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 cours statiques. Comment vais-je pouvoir faire quelque chose de similaire avec des méthodes statiques?

En bref j'aimerai appeler toutes les méthodes Run de toutes les classes statiques contenues dans l'espace de noms mySolution.Macros.

89
Tono Nam

En tant que documentation pour MethodInfo.Invoke states, 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 le souligne le commentaire, vous pouvez vous assurer que la méthode est statique lors de l'appel de GetMethod:

tempClass.GetMethod("Run", BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
119
Lee

Vous pouvez vraiment, vraiment, vraiment optimiser votre code en payant le prix de la création du délégué une seule fois (il n'est pas nécessaire non plus d'instancier la classe pour appeler une méthode statique). J'ai fait quelque chose de très similaire, et je viens de mettre 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 la signature de votre méthode est différente de Action, vous pouvez remplacer les transtypages et le typeof from Action par l'un des types génériques Action et Func nécessaires, ou déclarer votre délégué 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);
    }
}
16
Loudenvier

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 si vous recherchez quelque chose de plus robuste, y compris le traitement 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);
0
dynamichael