2014-08-29 21 views
10

Bài viết này nêu rõ You Can’t Unsubscribe from an Event Using a Lambda Expression.Tại sao tôi không thể hủy đăng ký khỏi Sự kiện bằng Biểu thức Lambda?

Ví dụ: bạn có thể đăng ký như sau:

d.Barked += (s, e) => Console.WriteLine("Bark: {0}", e); 

nhưng bạn không thể bỏ đăng ký như thế này:

d.Barked -= (s, e) => Console.WriteLine("Bark: {0}", e); 

Tại sao? Sự khác biệt giữa điều này và hủy đăng ký từ một đại biểu là gì, ví dụ:

EventHandler<string> handler = (s, e) => Console.WriteLine("Bark: {0}", e); 
d.Barked += handler; 

// ... 

d.Barked -= handler; 
+0

http://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c-sharp –

+0

Có, điều này phác thảo giống như những gì trong bài viết nhưng nó không giải thích tại sao. – DaveDev

+0

@TimSchmelter không may trình duyệt của tôi chỉ có thể xem trong quá khứ. – DaveDev

Trả lời

32

Tất cả đều được coi là: khi hai đại biểu được coi là giống nhau cho mục đích cộng/trừ đại biểu. Khi bạn hủy đăng ký, về cơ bản nó sử dụng logic từ Delegate.Remove, xem xét hai đại biểu tương đương nếu cả hai .Target và trận đấu .Method (ít nhất, đối với trường hợp đơn giản của một đại biểu với một phương pháp đích duy nhất; . Vì vậy: .Method.Target trên lambda là gì (giả sử chúng tôi đang biên dịch nó thành một số đại biểu và không hiển thị biểu thức)?

Trình biên dịch thực sự có rất nhiều tự do ở đây, nhưng những gì xảy ra là:

  • nếu lambda bao gồm một đóng cửa hơn một tham số hoặc biến, trình biên dịch tạo ra một phương pháp (các phương pháp) trên một lớp trình biên dịch tạo ra đại diện cho bối cảnh chụp (cũng có thể bao gồm mã thông báo this); mục tiêu là tham chiếu đến trường hợp chụp ngữ cảnh này (được xác định bởi phạm vi chụp)
  • nếu lambda không bao gồm đóng trên một tham số hoặc biến, nhưng sử dụng trạng thái mỗi trường hợp qua this (ẩn hoặc rõ ràng), trình biên dịch tạo ra một phương thức cá thể (phương thức ) trên loại hiện tại; các mục tiêu là trường hợp hiện tại (this)
  • nếu không trình biên dịch tạo ra một phương pháp tĩnh (phương pháp ), và mục tiêu là null (tình cờ, trong trường hợp này nó cũng bao gồm một lĩnh vực thuận tiện để cache một đơn đại biểu dụ tĩnh - vì vậy trong kịch bản này, chỉ có một đại biểu được từng được tạo ra mỗi lambda)

gì nó không làm, tuy nhiên, là so sánh rất nhiều lambdas với các cơ quan tìm kiếm tương tự để giảm bất kỳ . Vì vậy, những gì tôi nhận được khi tôi biên dịch mã của bạn là hai phương pháp tĩnh:

[CompilerGenerated] 
private static void <Main>b__0(object s, string e) 
{ 
    Console.WriteLine("Bark: {0}", e); 
} 

[CompilerGenerated] 
private static void <Main>b__2(object s, string e) 
{ 
    Console.WriteLine("Bark: {0}", e); 
} 

(các Main đây chỉ là bởi vì trong giàn khoan thử nghiệm của tôi những lambdas là bên trong phương pháp Main - nhưng cuối cùng trình biên dịch có thể chọn bất kỳ phát thành âm thanh tên nó chọn ở đây)

Phương pháp đầu tiên được sử dụng bởi lambda đầu tiên; phương pháp thứ hai được sử dụng bởi lambda thứ hai.Vì vậy, cuối cùng, lý do nó không hoạt động là vì .Method không khớp.

Trong thường xuyên C# điều khoản, nó sẽ giống như thực hiện:

obj.SomeEvent += MethodOne; 
obj.SomeEvent -= MethodTwo; 

nơi MethodOneMethodTwo có cùng mã bên trong chúng; nó không hủy đăng ký bất cứ điều gì.

Nó có thể là đẹp nếu trình biên dịch phát hiện này, nhưng nó là không phải, và như vậy nó là an toàn hơn mà nó không đắc cử tới - nó có thể có nghĩa là trình biên dịch khác nhau bắt đầu sản xuất rất các kết quả khác nhau.

Là một lưu ý phụ; nó có thể rất khó hiểu nếu nó đã làm cố gắng để de-dup, bởi vì bạn cũng sẽ có vấn đề của bối cảnh chụp - nó sẽ là trường hợp nó "làm việc" trong một số trường hợp và không phải những người khác - - có lẽ là kịch bản tồi tệ nhất có thể.

+0

Thật là trớ trêu, tôi nghĩ, tôi vẫn là người upvoter duy nhất ở đây. (Nó gần như bằng nhau mỉa mai rằng - trong tâm trí của tôi - điều này trả lời "làm thế nào" thêm aptly quá; Cảm ơn bạn đã sửa mô hình tinh thần của tôi loại bỏ sự kiện.) – sehe

+0

Giải thích tuyệt vời! Đã một thời gian kể từ khi tôi tìm thấy một khoảng trống trong sự hiểu biết C# của tôi, nhưng hôm nay tôi có một cái nhìn sâu sắc mới. Tôi cần hiểu điều này vì tôi muốn sử dụng giá trị tham chiếu của một phương thức ẩn danh (Loại hành động) để xác định xem nó đã được đăng ký như một cuộc gọi lại chưa. Bây giờ, tôi biết rằng nó sẽ chỉ hoạt động nếu không có đóng cửa và có thể sẽ gây ra các vấn đề bất ngờ. –

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