2010-04-18 29 views

回答

6

引發事件,將調用其事件 處理

即開始了錯。可能有沒有事件處理程序。或許多。你不知道。這與直接調用方法的主要區別在於。在你喜歡的設計模式書中查找「觀察者模式」。

+0

就是這樣!謝謝。 – dotnetdev 2010-04-18 17:04:51

2

引發事件(或調用,使用從你的鏈接術語)意味着你發送事件給所有消費者。例如,窗口可以在用鼠標點擊時引發事件。

消費事件意味着您正在接收並處理髮送它的人的事件。例如,您可能想知道何時通過鼠標點擊窗口。

如果你只有一個消費者,那麼你可以完成的只是直接提供的回調類似的東西:

// '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(); 
    } 
} 

事件機制通過阻止消費者從直接設置委託值清除這了一點。相反,它使消費者向+=運營商註冊。當第一個消費者註冊時,代表被設置,當第二個消費者註冊時,他們的兩個回叫通過Delegate.Combine鏈接在一起。

+0

「如果你只有一個消費者,那麼你可以在原則上使用,而不是事件的委託」,爲什麼這是在事件選擇代表一個標準,甚至?許多人可以向代表註冊! – 2010-04-18 02:08:19

+1

這篇文章是不正確的。 「事件」只是一種修飾詞,而不是一種類型。一個'事件'實際上是一個'代表',有一些額外的限制。不同的是,一個事件不會讓你直接設置它。 IE:'myDelegate = someOtherDelegate;'會給你一個錯誤。一個事件限制訪問,所以你只能使用'+ ='和' - ='運算符。代表也可以有一個調用列表。只要你願意,你可以''將盡可能多的'委託人'代入'代表'。就像你可以參加一個活動一樣。 – Joel 2010-04-18 03:02:41

+0

你是對的。試圖修復破碎。 – Eric 2010-04-18 03:20:32

1

有什麼用 事件機制,直接調用 其他方法之間的差異(例如,如果一個條件在方法A()符合 ,調用B())?

商業邏輯明智兩者之間沒有區別。我的意思是,你可以在每個方面完成相同的任務。這只是一種不同的方式。真正的區別在於您爲處理其他模塊的通知所做的工作量。

隨着事件的發展,你基本上會說:「嘿,發生了一些代碼,在發生這種事情時已經註冊通知了,讓他們知道。哪些通知的模塊不是我關心的,因爲我我假設(在運行時)需要知道的所有模塊都已設置爲通知。「

通過直接調用每個方法,您決定要告訴這個(或這些)模塊,只有這些,發生了某些事情。你正在斷言,無論這些模塊的狀態如何不重要,他們都需要知道這個事件發生了。

兩者對於不同的情況都是正確的。事件通知更加動態。不同的模塊可以註冊並取消註冊通知。直接方法調用更加靜態。一些對象(或模塊等)絕對會被通知(除非有例外情況)發生了一些事情,但只有這些纔會被通知。

0

除了上面的multiple/no用戶場景外,事件也用於減少代碼耦合 - 例如,在編譯時,方法A()不需要知道有關方法B()的任何內容。這可以更好地分離關注點和較脆弱的代碼。

在野外,您更有可能看到框架和UI代碼中使用的事件,而在應用程序的域邏輯中,開發人員通常使用諸如Separated InterfaceDependency Injection之類的東西來解耦代碼。最近,在各領域中關於在領域邏輯中使用事件的討論有了更多的討論,這種巧妙命名爲Domain Events的方法。

+1

雖然事件通常用於減少代碼耦合,但我認爲值得注意的是有時候情況並非如此。我偶爾會看到程序員使用事件而不是直接函數調用,因爲他們認爲它更好地分離關注點,即使事件顯然只用於一個位置來調用一個函數。你最終有兩個耦合的對象看起來像一個偶然觀察者的分離對象。 – tsiki 2013-07-11 11:36:55

18

的區別是這樣的:「如果有人傾聽和關心,這件事情就這樣發生了」

方法調用 =「做這個具體的事情」

事件提高 =

它是分離關注點和可重用性的核心。如果點擊它,則按鈕不是可重用組件,而是調用特定的方法。但如果它簡單地向程序「宣佈」它被點擊了,並且感興趣的各方負責訂閱它自己,它是無限可重用的。

這是如何完成(通過委託)的基本技術實現是無關緊要的。

2

對於任何對事件調用感興趣的人,我已經做出了這個簡單的基準。 它顯示了直接調用方法,通過接口調用它,通過委託和通過事件(其中附加了一個處理程序)之間的區別。

在每種情況下,該方法以相應的方式調用1 000 000 000次。這裏是(也許令人驚訝的)結果:

代表電話:23 240毫秒 - 最快

事件電話:23 295毫秒

直接撥打電話:23 396毫秒

界面撥打電話:23 716 ms - 最慢

這些測量是在.NET4.0中使用C#進行版本構建完成的。

的代碼是在這裏:

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; 
    } 
}