2010-10-28 32 views
16

Có hai mô tả của các đại biểu: đầu tiên, trong một hội đồng của bên thứ ba:Làm thế nào để chuyển đổi đại biểu cho đại biểu giống hệt nhau?

public delegate void ClickMenuItem (object sender, EventArgs e) 

thứ hai, tiêu chuẩn:

public delegate void EventHandler (object sender, EventArgs e); 

Tôi đang cố gắng để viết một phương pháp mà sẽ nhận được một tham số kiểu EventHandler và sẽ gọi thư viện của bên thứ ba, với tham số ClickMenuItem.

Làm thế nào để chuyển đổi ClickMenuItem thành EventHandler?

+0

bài viết liên quan: http://msdn.microsoft.com/en-us/library/dd233060.aspx – ja72

Trả lời

22

May mắn thay, thật đơn giản. Bạn chỉ có thể viết:

ClickMenuItem clickMenuItem = ...; // Wherever you get this from 
EventHandler handler = new EventHandler(clickMenuItem); 

Và ngược lại:

EventHandler handler = ...; 
ClickMenuItem clickMenuItem = new ClickMenuItem(handler); 

này thậm chí sẽ làm việc trong C# 1.0. Lưu ý rằng nếu sau đó bạn thay đổi giá trị của biến ban đầu, thay đổi đó sẽ không được phản ánh trong biến số "đã chuyển đổi". Ví dụ:

ClickMenuItem click = new ClickMenuItem(SomeMethod); 
EventHandler handler = new EventHandler(click); 
click = null; 

handler(this, EventArgs.Empty); // This will still call SomeMethod 
+0

Suy nghĩ về điều này, điều này sẽ tránh được một mức độ không đồng ý đúng không? Ví dụ 'Delegate' mới sẽ có cùng' Target' và 'Method' như bản gốc? – Ani

+0

@Ani: Tôi không chắc chắn. Tôi nghĩ rằng * nó thực sự duy trì các đại biểu ban đầu, vì vậy nó chỉ là một cách đơn giản hơn để làm phiên bản biểu thức lambda của bạn. Tôi không hoàn toàn chắc chắn mặc dù. –

+0

@Ani Khi nhìn vào mã IL được tạo ra, bạn có thể thấy rằng đại biểu mới là một cuộc gọi đến phương thức 'Invoke' của đại biểu cũ ... –

6

EDIT: Có tùy chọn thứ tư, tức là để tránh tất cả những điều vô lý này và làm những gì Jon Skeet gợi ý trong câu trả lời của anh ấy.

Một cái gì đó như thế này?

public static EventHandler ToEventHandler(this ClickMenuItem clickMenuItem) 
{ 
    if (clickMenuItem == null) 
     return null; 

    return (sender, e) => clickMenuItem(sender, e); 
} 

và ngược lại:

public static ClickMenuItem ToClickMenuItem(this EventHandler eventHandler) 
{ 
    if (eventHandler == null) 
     return null; 

    return (sender, e) => eventHandler(sender, e); 
} 

Lưu ý rằng infers trình biên dịch mà đại biểu-loại để chuyển đổi lamda-biểu thức để.

EDIT: Nếu bạn thích, bạn cũng có thể sử dụng các đại biểu ẩn danh.

EventHandler eventHandler = delegate(object sender, EventArgs e) 
          { 
           clickMenuItem(sender, e); 
          }; 
return eventHandler; // can be inlined, type-inference works fine 

Thay thế thứ ba của khóa học, là tự viết một lớp đóng. Tôi sẽ không thực sự đề nghị điều này, nhưng nó cung cấp cho bạn một ý tưởng về những gì trình biên dịch làm với các phương thức nặc danh. Một cái gì đó như:

public static class ClickMenuItemExtensions 
{ 
    public static EventHandler ToEventHandler(this ClickMenuItem clickMenuItem) 
    { 
     if (clickMenuItem == null) 
      return null; 

     // new EventHandler not required, included only for clarity 
     return new EventHandler(new Closure(clickMenuItem).Invoke); 
    } 

    private sealed class Closure 
    { 
     private readonly ClickMenuItem _clickMenuItem; 

     public Closure(ClickMenuItem clickMenuItem) 
     { 
      _clickMenuItem = clickMenuItem; 
     } 

     public void Invoke(object sender, EventArgs e) 
     { 
      _clickMenuItem(sender, e); 
     } 
    } 
} 
+0

"=>" là gì? Vui lòng cung cấp liên kết tới MSDN. Tôi không thể tìm thấy. – SkyN

+0

@ SkyN: Đó là toán tử "go to", được sử dụng để viết biểu thức lamda. http://msdn.microsoft.com/en-us/library/bb397687.aspx – Ani

+0

Tại sao phải bận tâm với tất cả điều này khi bạn chỉ có thể chuyển đổi trực tiếp, theo câu trả lời của tôi? –

0

Bạn có thể thanh toán Variance in Delegates.

.NET Framework 3.5 và Visual Studio 2008 giới thiệu hỗ trợ phương sai cho chữ ký phương thức khớp với các loại đại biểu trong tất cả các đại biểu trong C# và Visual Basic. Điều này có nghĩa là bạn có thể gán cho các đại biểu không chỉ các phương thức có chữ ký phù hợp, mà còn là các phương thức trả về các kiểu có nguồn gốc (hiệp phương sai) hoặc chấp nhận các tham số có kiểu có nguồn gốc ít hơn (contravariance) hơn loại được chỉ định bởi kiểu ủy nhiệm. Điều này bao gồm cả các đại biểu chung chung và không chung chung.

+0

Thật lạ lùng ... Tôi có thể thề là * thực sự * trong C# 2 , không phải C# 3. –

+1

Ví dụ: xem http://en.csharp-online.net/New_Features_in_CSharp_2.0%E2%80%94Gain_Flexibility_with_Delegate_Covariance_and_Contravariance - có vẻ như MSDN sai ... –

+0

@Jon: * MSDN sai * (0: –

5

Ngoài câu trả lời khác, nếu bạn muốn làm chuyển đổi giữa các loại đại biểu tương thích mà không biết loại tại thời gian biên dịch, bạn có thể làm một cái gì đó như thế:

static Delegate ConvertDelegate(Delegate sourceDelegate, Type targetType) 
{ 
    return Delegate.CreateDelegate(
      targetType, 
      sourceDelegate.Target, 
      sourceDelegate.Method); 
} 

Nó có thể hữu ích nếu bạn cần đăng ký một sự kiện tự động.

1

Câu trả lời của Thomas Levesque không hoạt động tốt đối với một số trường hợp đặc biệt. Đây là một phiên bản cải tiến.

public static Delegate ConvertDelegate(this Delegate src, Type targetType, bool doTypeCheck) 
{ 
    //Is it null or of the same type as the target? 
    if (src == null || src.GetType() == targetType) 
     return src; 
    //Is it multiple cast? 
    return src.GetInvocationList().Count() == 1 
     ? Delegate.CreateDelegate(targetType, src.Target, src.Method, doTypeCheck) 
     : src.GetInvocationList().Aggregate<Delegate, Delegate> 
      (null, (current, d) => Delegate.Combine(current, ConvertDelegate(d, targetType, doTypeCheck))); 
} 

Lợi ích của mã trên là nó vượt qua các bài kiểm tra sau

EventHandler e = (o,e)=>{} 
var a = e.ConvertDelegate(typeof(Action<object, EventArgs>), true); 
Assert.AreEqual(e, e.ConvertDelegate(typeof(EventHandler), true)); 

khi

EventHandler e = (o,e)=>{} 
var a = new Action<object, EventArgs>(e); 
Assert.AreEqual(e, new EventHandler(a)); 

sẽ thất bại.

Các vấn đề liên quan