2009-07-14 6 views
32

Sau khi googling và hạ cánh trên SO và có đọc this other questionXây dựng một đại biểu từ MethodInfo?

Có thể xây dựng một Đại biểu đúng từ một MethodInfo nếu THỂ BẠN CHƯA BIẾT số điện thoại hoặc các loại thông số tại thời gian biên dịch?

Thông tin thêm về điều này: điều này có thể được thực hiện một cách thanh lịch mà không cần sử dụng Reflection.Emit hoặc type builders?

Đây là một điều tốt cho tôi vì Delegate.CreateDelegate yêu cầu tôi chỉ định đúng loại đại biểu làm tham số đầu tiên hoặc nếu không nó sẽ ném ngoại lệ hoặc gọi phương thức không chính xác.

Tôi đang xây dựng một số bánh răng ninja và điều này sẽ giúp rất nhiều ... Cảm ơn!


Dưới đây là một giải pháp chung:

/// <summary> 
/// Builds a Delegate instance from the supplied MethodInfo object and a target to invoke against. 
/// </summary> 
public static Delegate ToDelegate(MethodInfo mi, object target) 
{ 
    if (mi == null) throw new ArgumentNullException("mi"); 

    Type delegateType; 

    var typeArgs = mi.GetParameters() 
     .Select(p => p.ParameterType) 
     .ToList(); 

    // builds a delegate type 
    if (mi.ReturnType == typeof(void)) { 
     delegateType = Expression.GetActionType(typeArgs.ToArray()); 

    } else { 
     typeArgs.Add(mi.ReturnType); 
     delegateType = Expression.GetFuncType(typeArgs.ToArray()); 
    } 

    // creates a binded delegate if target is supplied 
    var result = (target == null) 
     ? Delegate.CreateDelegate(delegateType, mi) 
     : Delegate.CreateDelegate(delegateType, target, mi); 

    return result; 
} 

Note: Tôi đang xây dựng một ứng dụng Silverlight rằng sẽ thay thế một ứng dụng javascript xây dựng năm-trước trong đó tôi có nhiều Các giao diện Javascript gọi vào cùng một phương thức Silverlight [ScriptableMember].

Tất cả các giao diện JS cũ cần được hỗ trợ cũng như giao diện mới để truy cập các tính năng mới, vì vậy một thứ tự động thiết lập giao diện JS và "đại biểu" cuộc gọi đến phương pháp Silverlight phù hợp sẽ giúp tăng tốc công việc.

Tôi không thể đăng mã ở đây, vì vậy đó là tóm tắt.

Trả lời

22

Thành thật mà nói, nếu bạn không biết loại thời gian biên dịch, sẽ không có nhiều lợi ích khi tạo một Delegate. Bạn không muốn sử dụng DynamicInvoke; nó sẽ chậm như phản xạ. Ngoại lệ chính cho điều này là khi có một loại đại biểu ẩn trong bóng tối, ví dụ khi đăng ký một sự kiện - trong trường hợp này EventInfo làm cho tính năng này khả dụng.

Đối với thông tin, trong .NET 3.5 trên Expression, có:

Expression.GetActionType(params Type[] typeArgs); 
Expression.GetFuncType(params Type[] typeArgs) 

Điều đó có thể giúp đỡ để một mức độ:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 
static class Program { 
    static void Main() { 
     DoStuff("Test1"); 
     DoStuff("Test2"); 
    } 
    static void DoStuff(string methodName) { 
     MethodInfo method = typeof(Program).GetMethod(methodName); 
     List<Type> args = new List<Type>(
      method.GetParameters().Select(p => p.ParameterType)); 
     Type delegateType; 
     if (method.ReturnType == typeof(void)) { 
      delegateType = Expression.GetActionType(args.ToArray()); 
     } else { 
      args.Add(method.ReturnType); 
      delegateType = Expression.GetFuncType(args.ToArray()); 
     } 
     Delegate d = Delegate.CreateDelegate(delegateType, null, method); 
     Console.WriteLine(d); 
    } 
    public static void Test1(int i, DateTime when) { } 
    public static float Test2(string x) { return 0; } 
} 
+1

Tôi đang xây dựng điều này để gắn kết với nhau Silverlight [ScriptableMember] và một giao diện Javascript riêng biệt vì vậy tôi không phải lo lắng về việc giữ chữ ký phương thức đồng bộ ở cả hai vị trí. – chakrit

+0

Wow .... đã giúp ích rất nhiều! Các bạn rock! – chakrit

+0

@ Marc Gravell, tôi không thể gọi đại biểu được tạo trong mã ở trên như d(). Sau khi googling tôi thấy rằng dynamicInvoke có thể được sử dụng để gọi phương thức, mà là rất chậm. xin giúp đỡ. tôi mới đến đại biểu và sự kiện. yêu cầu của tôi là gọi phương thức động, số hoặc loại tham số sẽ được biết khi chạy chỉ – Saranya

7

Nếu bạn không biết số lượng hoặc loại tham số trước, có lẽ điều đó có nghĩa là bạn không biết loại đại biểu bạn muốn tạo?

Nếu đúng như vậy, bạn bị kẹt trong trường hợp hoàn toàn chung chung.

Tuy nhiên, đối với hầu hết chung trường hợp (không có ref thông số/out, vài thông số đủ để sử dụng một trong những loại hiện có), bạn có thể nhận được ngay với một trong những Func hoặc Action đại biểu. (.NET 4.0 có các kiểu số Func/Action cho số lượng lớn tham số, vì vậy thực sự bạn chỉ cần lo lắng về tham số out/ref.) Nếu phương thức có kiểu trả về không sử dụng Func, nếu không hãy sử dụng Action. Tìm ra loại sử dụng dựa trên số lượng tham số, ví dụ:

static readonly Type[] FuncTypes = { typeof(Func), 
    typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), /* etc */ }; 

Sử dụng Type.MakeGenericType sử dụng các loại tham số và kiểu trả về để có được các loại đại biểu phải, sau đó Delegate.CreateDelegate nên làm việc.

Tôi không có thời gian để làm mẫu ngay bây giờ, nhưng hãy cho tôi biết nếu bạn muốn tôi làm việc sau này.

Một câu hỏi: bạn định sử dụng đại biểu này như thế nào? Một cái gì đó khác sẽ cần phải biết làm thế nào để thực hiện nó, chắc chắn ...

+0

ah-ha ... tốt mất trên MakeGenericType + Func ... mà sẽ làm điều đó :-) – chakrit

+4

Để tránh static Type [], xem Expression.GetActionType/Expression.GetFuncType - xem bài viết. Tôi sẽ ** hy vọng ** rằng các phương thức này đã được mở rộng để bao gồm các biến thể .NET 4.0 mới. –

+0

Tôi đã thêm "lý do" vào câu hỏi – chakrit

6

Tại sao mà phức tạp?

public static Delegate CreateDelegate(this MethodInfo method) 
{ 
    return Delegate.CreateDelegate 
    (
     Expression.GetDelegateType 
     (
      method.GetParameters() 
       .Select(p => p.ParameterType) 
       .Concat(new Type[] { method.ReturnType }) 
       .ToArray() 
     ), 
     null, 
     method 
    ); 
} 

[Ghi chú bên: Tôi đã bắt đầu trước phương pháp này "Tạo ...". "Để ..." là gây nhầm lẫn vì nó lừa dối bạn nghĩ rằng đó là một chuyển đổi.]

+0

Phương thức 'Expression.GetDelegateType' thực sự là .NET 4 và SL 4. Câu hỏi của tôi đã được hỏi trước khi phát hành .NET4 và SL4. Dù sao cảm ơn cho câu trả lời. Lưu ý rằng bạn sẽ cần phải ràng buộc một đại biểu đến một đích nếu phương thức là một phương thức cá thể sao cho phần ràng buộc vẫn là bắt buộc. – chakrit

+0

Bằng cách truyền một đối tượng làm tham số thứ hai (được gọi là 'firstArgument' - tôi đang truyền null ở đây), bạn có thể chỉ định đối tượng mà Delegate bị ràng buộc. Hay tôi bỏ lỡ vấn đề? – 0xbadf00d