Chiama il metodo statico con riflessione


111

Ho diverse classi statiche nello spazio dei nomi mySolution.Macroscome

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

Quindi la mia domanda è come sarà possibile chiamare quei metodi con l'aiuto della riflessione?

Se i metodi NON devono essere statici, potrei fare qualcosa del tipo:

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

Mi piacerebbe mantenere le mie classi statiche. Come potrò fare qualcosa di simile con i metodi statici?

In breve, mi piacerebbe chiamare tutti i metodi Run da tutte le classi statiche che si trovano nello spazio dei nomi mySolution.Macros.

Risposte:


150

Come afferma la documentazione per MethodInfo.Invoke , il primo argomento viene ignorato per i metodi statici, quindi puoi semplicemente passare null.

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

Come sottolinea il commento, potresti voler assicurarti che il metodo sia statico quando chiami GetMethod:

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

4
potresti voler passare alcuni flag vincolanti a GetMethod.
Daniel A. White

2
Senza di BindingFlags.Staticte potresti non riuscire a ottenere il metodo in primo luogo ...
ErikE

1
Potresti voler aggiungere BindingFlags.FlattenHierarchy se il metodo risiede in una classe predecessore.
J. Ouwehand

20

Potresti davvero, davvero, davvero ottimizzare molto il tuo codice pagando il prezzo di creare il delegato solo una volta (non c'è nemmeno bisogno di istanziare la classe per chiamare un metodo statico). Ho fatto qualcosa di molto simile e ho memorizzato nella cache un delegato al metodo "Run" con l'aiuto di una classe helper :-). Assomiglia a questo:

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

È MOLTO più veloce in questo modo.

Se la firma del tuo metodo è diversa da Action, potresti sostituire i type-cast e typeof da Action con uno dei tipi generici Action e Func necessari, oppure dichiarare il tuo Delegate e usarlo. La mia implementazione usa Func per stampare piuttosto oggetti:

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

Preferisco la semplicità ...

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

Uso ...

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

Ma nel caso tu stia cercando qualcosa di un po 'più robusto, inclusa la gestione delle eccezioni ...

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'utilizzo è più o meno lo stesso ...

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

2
ingoiare ogni possibile eccezione è di solito una cattiva idea.
D Stanley,

Vero. Era pigro ma semplice. Ho migliorato la mia risposta.
dynamichael
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.