2013-05-24 47 views
8

Điều tôi muốn làm về cơ bản là xóa một hàm khỏi một sự kiện, mà không biết tên của hàm.Làm cách nào để tự xóa mình khỏi trình xử lý sự kiện?

Tôi có một số FileSystemWatcher. Nếu một tập tin được tạo/đổi tên nó sẽ kiểm tra tên của nó. Nếu nó khớp, nó sẽ di chuyển nó đến một vị trí cụ thể. Tuy nhiên, nếu tập tin bị khóa, nó làm cho một lambda gắn vào một sự kiện đánh dấu thời gian, chờ cho đến khi tập tin không bị khóa. Khi không, nó di chuyển tệp và sau đó tự xóa nó khỏi trình xử lý sự kiện. Tôi đã nhìn thấy rất nhiều cách để làm điều này, như giữ dụ, hoặc thực hiện một phương pháp được đặt tên. Tôi không thể làm một trong những người ở đây. Những lựa chọn của tôi là gì?

+0

Có một vài mẫu trên mạng cho phép bạn để đăng ký vào các sự kiện trong một * loosley cùng * chiều. Hãy xem [this] (http://martinfowler.com/eaaDev/EventAggregator.html). Nó mô tả cách đăng ký/hủy đăng ký một trình tổng hợp sự kiện (trong trường hợp này là về màn hình). Trình tổng hợp thực sự tạo ra các tham chiếu giữa các đối tượng. Có rất nhiều cách triển khai khác nhau. Người tôi thích được thực hiện trong [Caliburn.Micro] (http://www.mindscapehq.com/blog/index.php/2012/02/01/caliburn-micro-part-4-the-event-aggregator/) . –

Trả lời

19

Không có phương pháp đơn giản để đạt được điều này.

phương pháp ưa thích:

Tôi không thấy lý do tại sao bạn không thể lưu các đại biểu. Bạn không cần phải lưu dụ như một số trường. Nó có thể là biến cục bộ được xử lý bởi trình xử lý sự kiện ẩn danh của bạn:

EventHandler<TypeOfEventArgs> handler = null; 
handler = (s, e) => 
{ 
    // Do whatever you need to do here 

    // Remove event: 
    foo.Event -= handler; 
} 

foo.Event += handler; 

Tôi không thể nghĩ ra một trường hợp duy nhất mà bạn không thể sử dụng.

Cách tiếp cận thay thế mà không lưu đại biểu:

Tuy nhiên, nếu bạn có kịch bản như vậy, sẽ rất phức tạp.
Bạn cần tìm đại biểu đã được thêm làm người xử lý cho sự kiện. Bởi vì bạn đã không lưu nó, nó là khá khó khăn để có được nó. Không có this để có được một đại biểu của phương pháp hiện đang thực hiện.

Bạn không thể sử dụng GetInvocationList() trong sự kiện, vì việc truy cập sự kiện bên ngoài lớp được xác định trong bị hạn chế thêm và xóa trình xử lý, tức là +=-=.

Không thể tạo đại biểu mới. Trong khi bạn có thể truy cập đối tượng MethodInfo xác định phương thức ẩn danh của bạn, bạn không thể truy cập vào cá thể của lớp mà phương thức được khai báo. Lớp này được trình biên dịch tạo tự động và gọi this bên trong phương thức nặc danh sẽ trả về Ví dụ của lớp mà phương pháp thông thường của bạn được định nghĩa.

Cách duy nhất tôi thấy là hoạt động là tìm trường - nếu có - sự kiện đó sử dụng và gọi GetInvocationList() trên đó. Các mã sau đây chứng tỏ điều này với một lớp giả:

void Main() 
{ 
    var foo = new Foo(); 
    foo.Bar += (s, e) => { 
     Console.WriteLine("Executed"); 

     var self = new StackFrame().GetMethod(); 
     var eventField = foo.GetType() 
          .GetField("Bar", BindingFlags.NonPublic | 
              BindingFlags.Instance); 
     if(eventField == null) 
      return; 
     var eventValue = eventField.GetValue(foo) as EventHandler; 
     if(eventValue == null) 
      return; 
     var eventHandler = eventValue.GetInvocationList() 
            .OfType<EventHandler>() 
            .FirstOrDefault(x => x.Method == self) 
           as EventHandler; 
     if(eventHandler != null) 
      foo.Bar -= eventHandler; 
    }; 

    foo.RaiseBar(); 
    foo.RaiseBar(); 
} 

public class Foo 
{ 
    public event EventHandler Bar; 
    public void RaiseBar() 
    { 
     var handler = Bar; 
     if(handler != null) 
      handler(this, EventArgs.Empty); 
    } 
} 

Xin lưu ý rằng chuỗi "Bar" được truyền cho GetField cần phải là tên chính xác của lĩnh vực được sử dụng bởi sự kiện này. Điều này dẫn đến hai vấn đề:

  1. Trường có thể được đặt tên khác, ví dụ: khi sử dụng triển khai sự kiện rõ ràng. Bạn cần tự tìm ra tên trường.
  2. Có thể không có trường nào cả. Điều này xảy ra nếu sự kiện sử dụng triển khai sự kiện rõ ràng và chỉ ủy quyền cho một sự kiện khác hoặc lưu trữ các đại biểu theo một cách khác.

Kết luận:

Phương pháp thay thế dựa vào chi tiết thực hiện, do đó, không sử dụng nó nếu bạn có thể tránh nó.

+0

Tôi quên đóng cửa! Rất tiếc! Cảm ơn câu trả lời mặc dù, bạn đã đi xa hơn tôi có thể. –

+0

Tôi nghĩ rằng đó là một chút không công bằng mà bạn đã viết một bình luận tiêu cực về câu trả lời của Dark Falcon mà predated của bạn, sau đó đã cho cơ bản cùng một lời khuyên trong cách tiếp cận * ưa thích * của bạn. –

+0

@BenVoigt Vâng, bình luận của anh ấy đúng, về mặt kỹ thuật không phải là câu trả lời, mặc dù nếu có thể thì đó là giải pháp ưu tiên. Cũng lưu ý rằng anh ta không downvote câu trả lời của Dark, chỉ đơn thuần là nhận xét rằng nó không đáp ứng các tiêu chuẩn đã nêu. Một điểm khác biệt chính là Daniel đã đưa ra một giải pháp * * đáp ứng các tiêu chí trong câu hỏi. – Servy

0

bước để loại bỏ xử lý sự kiện với biểu thức lambda:

public partial class Form1 : Form 
{ 
    private dynamic myEventHandler; 
    public Form1() 
    { 
     InitializeComponent(); 
    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 
     myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there")); 
     this.button1.Click += myEventHandler; 
    } 

    private void button1_Click(object sender, EventArgs e, string additionalInfo) 
    { 
     MessageBox.Show(additionalInfo); 
     button1.Click -= myEventHandler; 
    } 
}