2010-04-18 23 views
8

Tăng sự kiện, sẽ gọi trình xử lý sự kiện của nó. ví dụ: http://msdn.microsoft.com/en-us/library/aa645739%28VS.71%29.aspxTăng sự kiện và gọi phương thức trực tiếp khác biệt

Sự khác biệt giữa việc sử dụng cơ chế sự kiện và cuộc gọi trực tiếp đến các phương pháp khác (ví dụ: nếu điều kiện được đáp ứng trong phương pháp A(), gọi B())?

Và sự khác biệt giữa sự kiện tiêu thụ và tăng cường là gì?

Cảm ơn

Trả lời

6

Nâng cao một sự kiện, sẽ gọi cho sự kiện của mình handler

Đó bắt đầu sai. Có thể có không trình xử lý sự kiện. Hoặc nhiều. Bạn không biết. Đó là sự khác biệt lớn khi gọi phương thức trực tiếp. Tra cứu "mẫu quan sát" trong cuốn sách mẫu thiết kế yêu thích của bạn.

+0

Đó là nó! Cảm ơn. – dotnetdev

2

Nâng cao một sự kiện (hoặc Gọi, sử dụng thuật ngữ từ liên kết của bạn) có nghĩa là bạn đang gửi sự kiện cho tất cả người tiêu dùng. Ví dụ, một cửa sổ có thể nâng cao một sự kiện khi nó được nhấp bằng chuột.

Tiêu thụ sự kiện có nghĩa là bạn đang nhận và xử lý sự kiện từ bất kỳ ai đã gửi sự kiện. Ví dụ, bạn có thể muốn biết khi nào cửa sổ được nhấp chuột.

Nếu bạn chỉ có một người tiêu dùng, sau đó bạn có thể thực hiện một cái gì đó tương tự bằng cách chỉ cung cấp một callback trực tiếp:

// 'Event' type: 
delegate void DelMyEvent(); 
// consumer: 
class Consumer 
{ 
    Producer _theProducer; 
    void RegisterForNotification() 
    { 
     _theProducer.OnMyEvent = new DelMyEvent(OnMyEvent); 
    } 
    void OnMyEvent() { } 
} 
// producer: 
class Producer 
{ 
    public DelMyEvent OnMyEvent; 
    void SendNotification() 
    { 
     if(OnMyEvent != null) OnMyEvent(); 
    } 
} 

Cơ chế sự kiện làm sạch này lên một chút bằng cách ngăn chặn người tiêu dùng từ thiết lập các giá trị đại biểu trực tiếp. Thay vào đó nó làm cho người tiêu dùng đăng ký chính nó với nhà điều hành +=. Khi người tiêu dùng đầu tiên đăng ký, đại biểu được thiết lập và khi người tiêu dùng thứ hai đăng ký, hai lần gọi lại của họ bị xích lại với nhau bởi Delegate.Combine.

+0

"Nếu bạn chỉ có một người tiêu dùng, sau đó bạn có thể về nguyên tắc sử dụng một đại biểu thay vì một sự kiện", tại sao đây là một tiêu chí để lựa chọn đại biểu trong sự kiện, thậm chí? nhiều người có thể đăng ký cho các đại biểu! –

+1

Bài đăng này không chính xác. Một 'sự kiện' chỉ đơn thuần là một công cụ sửa đổi, không phải là một loại. Một 'sự kiện' thực chất là một' đại biểu' với một vài hạn chế bổ sung. Sự khác biệt là một sự kiện sẽ không cho phép bạn trực tiếp thiết lập nó. IE: 'myDelegate = someOtherDelegate;' sẽ cung cấp cho bạn một lỗi. Sự kiện hạn chế quyền truy cập để bạn chỉ có thể sử dụng toán tử '+ =' và '- ='. Một đại biểu cũng có thể có một danh sách các cuộc xâm lược để thực hiện. Bạn có thể '+ =' càng nhiều 'đại biểu' thành một' đại biểu' như bạn muốn. Cũng giống như bạn có thể vào một sự kiện. – Joel

+0

Đúng vậy. Đã cố khắc phục sự cố. – Eric

1

sự khác biệt giữa việc sử dụng cơ chế sự kiện và các cuộc gọi trực tiếp với các phương pháp khác là gì (ví dụ như nếu một điều kiện được gặp trong phương pháp A(), gọi B())?

Logic nghiệp vụ khôn ngoan không có sự khác biệt giữa hai loại. Ý tôi là điều này có nghĩa là bạn có thể thực hiện cùng một nhiệm vụ theo cách đó. Nó chỉ là một cách khác nhau để đi về nó. Sự khác biệt thực sự là số lượng công việc bạn phải làm để xử lý thông báo của các mô-đun khác.

Với việc tăng sự kiện, bạn chủ yếu nói "Xin chào, có điều gì đó đã xảy ra với bất kỳ đoạn mã nào đã đăng ký để được thông báo khi điều này xảy ra, hãy cho họ biết. Mô-đun nào được thông báo không phải là mối quan tâm của tôi giả định rằng (lúc chạy) tất cả các mô-đun cần biết là thiết lập để thông báo. "

Khi gọi trực tiếp từng phương pháp, bạn đang đưa ra quyết định rằng bạn sẽ nói cho (hoặc các mô-đun này), và chỉ những điều này, có điều gì đó đã xảy ra. Bạn đang đưa ra khẳng định rằng bất kể trạng thái của các mô-đun này là gì không quan trọng và họ cần phải biết sự kiện này đã xảy ra.

Cả hai đều chính xác cho các tình huống khác nhau. Thông báo sự kiện năng động hơn. Các mô-đun khác nhau có thể đăng ký và hủy đăng ký thông báo. Các cuộc gọi phương thức trực tiếp là tĩnh hơn.Một tập hợp các đối tượng nhất định (hoặc mô-đun vv) hoàn toàn sẽ được thông báo (ngoại trừ các khóa học ngoại lệ) có điều gì đó đã xảy ra, nhưng chỉ những điều này sẽ được thông báo.

0

Ngoài các trường hợp thuê bao nhiều/không có ở trên, các sự kiện cũng được sử dụng để giảm khớp nối mã - ví dụ phương thức A() không cần biết bất kỳ điều gì về phương pháp B() tại thời gian biên dịch. Điều này cho phép tách biệt các mối quan tâm và mã dễ vỡ hơn.

Trong tự nhiên, bạn có nhiều khả năng để xem các sự kiện được sử dụng trong khuôn khổ và giao diện người dùng mã, trong khi đó trong vòng logic miền của một nhà phát triển ứng dụng thường xuyên hơn sử dụng những thứ như Separated InterfaceDependency Injection để tách mã. Gần đây đã có một cuộc thảo luận nhiều hơn trong các đấu trường khác nhau liên quan đến việc sử dụng các sự kiện trong logic miền, một phương pháp được xảo quyệt có tên là Domain Events.

+1

Mặc dù thường là sự kiện được sử dụng để giảm sự ghép nối mã, tôi nghĩ rằng điều đáng chú ý là đôi khi điều này không đúng. Đôi khi tôi thấy các lập trình viên sử dụng các sự kiện thay vì các cuộc gọi hàm trực tiếp bởi vì họ nghĩ rằng việc tách biệt các mối quan tâm tốt hơn, ngay cả khi sự kiện này chỉ được sử dụng ở một vị trí để gọi một hàm. Bạn kết thúc có hai đối tượng kết hợp mà dường như tách các đối tượng cho một người quan sát bình thường. – tsiki

18

Sự khác biệt là thế này:

Phương pháp gọi = "Hãy làm điều cụ thể này"

tổ chức sự kiện nâng = "Nếu bất cứ ai đang lắng nghe và quan tâm, điều này chỉ xảy ra."

Đây là trung tâm để Tách mối quan tâm và khả năng sử dụng lại. Nút không phải là thành phần có thể sử dụng lại được nếu nhấp vào nó gọi một phương thức cụ thể. Nhưng nếu nó chỉ đơn giản là "thông báo" cho chương trình nó đã được nhấp vào, và các bên quan tâm có trách nhiệm đăng ký bản thân vào đó, nó là vô hạn tái sử dụng.

Việc triển khai kỹ thuật cơ bản về cách thực hiện điều này (thông qua đại biểu) không liên quan.

2

Đối với bất kỳ ai quan tâm đến hiệu suất của cuộc gọi sự kiện, tôi đã thực hiện điểm chuẩn đơn giản này. Nó cho thấy sự khác biệt giữa việc gọi một phương thức trực tiếp, gọi nó qua giao diện, thông qua đại biểu và thông qua sự kiện, trong đó một trình xử lý được đính kèm.

Trong mỗi trường hợp, phương pháp được gọi theo cách tương ứng 1 000 000 000 lần. Dưới đây là (có thể ngạc nhiên) kết quả:

Đại biểu gọi: 23 240 ms - nhanh nhất gọi

tổ chức sự kiện: 23 295 ms

cuộc gọi trực tiếp: 23 396 ms

Giao diện cuộc gọi: 23 716 ms - chậm nhất

Các mesurements được thực hiện trong bản phát hành xây dựng bằng C# trong .NET4.0.

Mã này là ở đây:

class Program 
{ 
    static void Main(string[] args) 
    { 
     TestClass.RunTest(); 
     Console.ReadLine(); 
    } 
} 

interface ITestClass 
{ 
    void TestMethod(object sender, TestEventArgs eventErgs); 
} 

class TestClass : ITestClass 
{ 
    #region Events 

    event EventHandler<TestEventArgs> TestEvent; 

    #endregion 

    #region Constructor 

    public TestClass() 
    { 
     TestEvent += TestMethod; 
    } 

    #endregion 

    #region Public Methods 

    public static void RunTest() 
    { 
     int testCount = 1000000000; //1 000 000 000 

     string format = "{0:### ### ### ##0}"; 

     #region Direct Call 

     Console.WriteLine("Direct call"); 
     TestClass testClass = new TestClass(); 

     testClass.TestMethod(testClass, new TestEventArgs(3)); 

     Stopwatch stopwatch = Stopwatch.StartNew(); 
     for (int i = 0; i < testCount; ++i) 
     { 
      testClass.TestMethod(testClass, new TestEventArgs(3)); 
     } 
     stopwatch.Stop(); 
     Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds)); 
     Console.WriteLine(); 

     #endregion 

     #region Interface Call 

     Console.WriteLine("Interface call"); 
     ITestClass itestClass = new TestClass(); 
     itestClass.TestMethod(testClass, new TestEventArgs(3)); 

     stopwatch = Stopwatch.StartNew(); 
     for (int i = 0; i < testCount; ++i) 
     { 
      itestClass.TestMethod(testClass, new TestEventArgs(3)); 
     } 
     stopwatch.Stop(); 
     Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds)); 
     Console.WriteLine(); 

     #endregion 

     #region Delegate Call 

     Console.WriteLine("Delegate call"); 
     TestClass delegateTestClass = new TestClass(); 
     Action<object, TestEventArgs> delegateMethod = delegateTestClass.TestMethod; 
     delegateMethod(testClass, new TestEventArgs(3)); 

     stopwatch = Stopwatch.StartNew(); 
     for (int i = 0; i < testCount; ++i) 
     { 
      delegateMethod(testClass, new TestEventArgs(3)); 
     } 
     stopwatch.Stop(); 
     Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds)); 
     Console.WriteLine(); 

     #endregion 

     #region Event Call 

     Console.WriteLine("Event call"); 
     TestClass eventTestClast = new TestClass(); 
     eventTestClast.TestEvent(testClass, new TestEventArgs(3)); 

     stopwatch = Stopwatch.StartNew(); 
     for (int i = 0; i < testCount; ++i) 
     { 
      eventTestClast.TestEvent(testClass, new TestEventArgs(3)); 
     } 
     stopwatch.Stop(); 
     Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds)); 
     Console.WriteLine(); 

     #endregion 
    } 

    #endregion 

    #region ITestClass Members 

    public void TestMethod(object sender, TestEventArgs e) 
    { 
     e.Result = e.Value * 3; 
    } 

    #endregion 
} 

class TestEventArgs : EventArgs 
{ 
    public int Value { get; private set; } 

    public int Result { get; set; } 

    public TestEventArgs(int value) 
    { 
     Value = value; 
    } 
} 
Các vấn đề liên quan