Вызов статического метода с отражением

112

У меня есть несколько статических классов в пространстве имен, mySolution.Macrosтаких как

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

Итак, мой вопрос: как можно будет вызывать эти методы с помощью отражения?

Если методы НЕ должны быть статическими, я мог бы сделать что-то вроде:

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

Я хочу, чтобы мои классы оставались статичными. Как я смогу сделать что-то подобное со статическими методами?

Короче говоря, я хотел бы вызвать все методы Run из всех статических классов, которые находятся в пространстве имен mySolution.Macros.

Тоно Нам
источник

Ответы:

151

Как указано в документации для MethodInfo.Invoke , первый аргумент игнорируется для статических методов, поэтому вы можете просто передать null.

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

Как указано в комментарии, вы можете убедиться, что метод статичен при вызове GetMethod:

tempClass.GetMethod("Run", BindingFlags.Public | BindingFlags.Static).Invoke(null, null);
подветренный
источник
4
вы можете захотеть передать некоторые флаги привязки GetMethod.
Дэниел А. Уайт
2
Без BindingFlags.Staticвас невозможно успешно получить метод ...
ErikE
1
Вы можете добавить BindingFlags.FlattenHierarchy, если метод находится в классе-предке.
J. Ouwehand 08
20

Вы действительно, действительно, действительно могли бы значительно оптимизировать свой код, заплатив цену за создание делегата только один раз (также нет необходимости создавать экземпляр класса для вызова статического метода). Я сделал что-то очень похожее, и я просто кэширую делегата метода "Run" с помощью вспомогательного класса :-). Выглядит это так:

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

Так происходит НАМНОГО быстрее.

Если подпись вашего метода отличается от Action, вы можете заменить приведение типов и typeof из Action на любой из необходимых универсальных типов Action и Func или объявить свой делегат и использовать его. Моя собственная реализация использует Func для красивой печати объектов:

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
источник
0

Я предпочитаю простоту ...

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

Использование...

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

Но если вы ищете что-то более надежное, включая обработку исключений ...

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

Использование почти такое же ...

_InvokeNamespaceClassesStaticMethod("mySolution.Macros", "Run", false);
dynamichael
источник
2
проглатывание любого возможного исключения - обычно плохая идея.
D Stanley
Правда. Это было лениво, но просто. Я улучшил свой ответ.
dynamichael