Panggil metode statis dengan refleksi

111

Saya memiliki beberapa kelas statis di namespace mySolution.Macrosseperti

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

Jadi pertanyaan saya adalah bagaimana mungkin memanggil metode-metode itu dengan bantuan refleksi?

Jika metode di mana TIDAK menjadi statis maka saya dapat melakukan sesuatu seperti:

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

Saya ingin kelas saya tetap statis. Bagaimana saya dapat melakukan sesuatu yang serupa dengan metode statis?

Singkatnya saya ingin memanggil semua metode Run dari semua kelas statis yang ada di namespace mySolution.Macros.

Tono Nam
sumber

Jawaban:

150

Sebagai dokumentasi untuk MethodInfo.Invoke menyatakan, argumen pertama diabaikan untuk metode statis sehingga Anda bisa meneruskan null.

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

Seperti yang ditunjukkan oleh komentar, Anda mungkin ingin memastikan bahwa metode ini statis saat memanggil GetMethod:

tempClass.GetMethod("Run", BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
Lee
sumber
4
Anda mungkin ingin meneruskan beberapa flag binding ke GetMethod.
Daniel A. White
2
Tanpa BindingFlags.StaticAnda mungkin tidak berhasil mendapatkan metode di tempat pertama ...
ErikE
1
Anda mungkin ingin menambahkan BindingFlags.FlattenHierarchy jika metode berada dalam kelas leluhur.
J. Ouwehand
20

Anda dapat benar-benar, sangat, sangat mengoptimalkan kode Anda dengan membayar harga pembuatan delegasi hanya sekali (tidak perlu membuat instance kelas untuk memanggil metode statis). Saya telah melakukan sesuatu yang sangat mirip, dan saya hanya men-cache delegasi ke metode "Run" dengan bantuan kelas helper :-). Ini terlihat seperti ini:

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

Jauh lebih cepat dengan cara ini.

Jika tanda tangan metode Anda berbeda dari Action, Anda dapat mengganti type-cast dan typeof dari Action ke salah satu jenis generik Action dan Func yang diperlukan, atau mendeklarasikan Delegasi Anda dan menggunakannya. Implementasi saya sendiri menggunakan Func untuk mencetak objek dengan cantik:

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
sumber
0

Saya lebih suka kesederhanaan ...

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

Pemakaian...

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

Tetapi jika Anda mencari sesuatu yang sedikit lebih kuat, termasuk penanganan pengecualian ...

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

Penggunaannya hampir sama ...

_InvokeNamespaceClassesStaticMethod("mySolution.Macros", "Run", false);
dynamichael
sumber
2
menelan segala kemungkinan pengecualian biasanya merupakan ide yang buruk.
D Stanley
Benar. Itu malas tapi sederhana. Saya telah memperbaiki jawaban saya.
dynamichael