Gọi phương thức tĩnh với phản chiếu


111

Tôi có một số lớp tĩnh trong không gian tên mySolution.Macroschẳng hạn như

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

Vì vậy, câu hỏi của tôi là làm thế nào nó sẽ có thể gọi các phương pháp đó với sự trợ giúp của sự phản chiếu?

Nếu các phương thức KHÔNG phải là tĩnh thì tôi có thể làm điều gì đó như:

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

Tôi muốn giữ cho các lớp học của mình tĩnh. Làm thế nào để tôi có thể làm điều gì đó tương tự với các phương thức tĩnh?

Tóm lại, tôi muốn gọi tất cả các phương thức Run từ tất cả các lớp tĩnh có trong không gian tên mySolution.Macros.

Câu trả lời:


150

Như tài liệu cho MethodInfo.Invoke trạng thái, đối số đầu tiên bị bỏ qua đối với các phương thức tĩnh, do đó bạn có thể chỉ cần truyền null.

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

Như nhận xét đã chỉ ra, bạn có thể muốn đảm bảo phương thức là tĩnh khi gọi GetMethod:

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

4
bạn có thể muốn chuyển một số cờ ràng buộc cho GetMethod.
Daniel A. White

2
Nếu không có, BindingFlags.Staticbạn có thể không nhận được phương pháp thành công ngay từ đầu ...
ErikE

1
Bạn có thể muốn thêm BindingFlags.FlattenHierarchy nếu phương thức nằm trong lớp tổ tiên.
J. Ouwehand

20

Bạn thực sự có thể thực sự tối ưu hóa mã của mình rất nhiều bằng cách trả giá bằng cách tạo ủy quyền chỉ một lần (cũng không cần phải khởi tạo lớp để gọi một phương thức tĩnh). Tôi đã làm một cái gì đó rất giống và tôi chỉ lưu vào bộ nhớ cache một đại biểu cho phương thức "Run" với sự trợ giúp của lớp helper :-). Nó trông như thế này:

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

Nó nhanh hơn RẤT NHIỀU theo cách này.

Nếu chữ ký phương thức của bạn khác với Action, bạn có thể thay thế type-cast và typeof từ Action thành bất kỳ kiểu chung Action và Func nào cần thiết, hoặc khai báo Ủy quyền của bạn và sử dụng nó. Việc triển khai của riêng tôi sử dụng Func để in các đối tượng đẹp:

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

Tôi thích sự đơn giản hơn ...

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

Sử dụng...

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

Nhưng trong trường hợp bạn đang tìm kiếm thứ gì đó mạnh mẽ hơn một chút, bao gồm cả việc xử lý các ngoại lệ ...

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

Cách sử dụng là khá giống nhau ...

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

2
nuốt bất kỳ ngoại lệ nào có thể xảy ra thường là một ý tưởng tồi.
D Stanley

Thật. Nó lười biếng nhưng đơn giản. Tôi đã cải thiện câu trả lời của mình.
dynamichael
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.