리플렉션을 사용하여 정적 메서드 호출


111

네임 스페이스에 다음 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);
}

내 수업을 정적으로 유지하고 싶습니다. 정적 메서드로 비슷한 작업을 어떻게 수행 할 수 있습니까?

간단히 말해서 mySolution.Macros 네임 스페이스에있는 모든 정적 클래스에서 모든 Run 메서드를 호출하고 싶습니다.

답변:


150

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.
Daniel A. White

2
없이 BindingFlags.Static성공적으로 첫 번째 장소에서 방법 ... 얻을 수 없습니다
ErikE

1
메서드가 상위 클래스에있는 경우 BindingFlags.FlattenHierarchy를 추가 할 수 있습니다.
J. Ouwehand 2019-06-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과 다른 경우 Action의 type-cast 및 typeof를 필요한 Action 및 Func 제네릭 유형 중 하나로 바꾸거나 Delegate를 선언하고 사용할 수 있습니다. 내 구현에서는 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);
    }
}

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

2
가능한 예외를 삼키는 것은 일반적으로 나쁜 생각입니다.
D Stanley

진실. 게으르지 만 단순했습니다. 내 대답을 개선했습니다.
dynamichael
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.