2011-12-29 99 views
3

我在書中簡而言之 (http://www.albahari.com/nutshell/ch04.aspx委託,事件慣例,我不明白

using System; 

public class PriceChangedEventArgs : EventArgs 
{ 
    public readonly decimal LastPrice; 
    public readonly decimal NewPrice; 

    public PriceChangedEventArgs (decimal lastPrice, decimal newPrice) 
    { 
    LastPrice = lastPrice; NewPrice = newPrice; 
    } 
} 

public class Stock 
{ 
    string symbol; 
    decimal price; 

    public Stock (string symbol) {this.symbol = symbol;} 

    public event EventHandler<PriceChangedEventArgs> PriceChanged; 

    ****protected virtual void OnPriceChanged (PriceChangedEventArgs e) 
    { 
    if (PriceChanged != null) PriceChanged (this, e); 
    }**** 

    public decimal Price 
    { 
    get { return price; } 
    set 
    { 
     if (price == value) return; 
     OnPriceChanged (new PriceChangedEventArgs (price, value)); 
     price = value; 
    } 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
    Stock stock = new Stock ("THPW"); 
    stock.Price = 27.10M; 
    // register with the PriceChanged event  
    stock.PriceChanged += stock_PriceChanged; 
    stock.Price = 31.59M; 
    } 

    static void stock_PriceChanged (object sender, PriceChangedEventArgs e) 
    { 
    if ((e.NewPrice - e.LastPrice)/e.LastPrice > 0.1M) 
     Console.WriteLine ("Alert, 10% stock price increase!"); 
    } 
} 

我不明白,這就是爲什麼使用這個慣例看這個例子從C# ..

****protected virtual void OnPriceChanged (PriceChangedEventArgs e) 
    { 
    if (PriceChanged != null) PriceChanged (this, e); 
    }**** 

爲什麼我需要這種方法,爲什麼我會關心給它「this」參數?!?不能,我只是將該類中的事件與測試類中的方法PriceChanged直接相連並跳過該方法?!?

+0

你怎麼會重視嗎?該方法正在附加。您不必使用該方法,只需使用其中的代碼,即可代替所需的位置。 – 2011-12-29 19:34:17

回答

3

null檢查,因爲一個(事件)委託列表不如果沒有訂戶,則爲空,但是null

然而,這不是線程安全的。因此,如果您開始使用BackgroundWorker或任何其他多線程技術,它可能會炸燬你的臉。

我建議你使用,而不是一個空的委託:

public event EventHandler<PriceChangedEventArgs> PriceChanged = delegate {}; 

因爲它可以讓你只寫:

protected virtual void OnPriceChanged (PriceChangedEventArgs e) 
{ 
    PriceChanged (this, e); 
} 

這是線程安全的代碼更易於閱讀。

爲什麼我在乎給它「this」參數?!?

相同的事件處理程序可能由多個事件發電機使用。發件人告訴哪個生成調用。你應該總是發送正確的事件發生器,因爲它是預期的,你會打破/關閉原則,如果你不打

爲什麼我需要該方法?

你不這樣做,除非你本來的重複代碼(如生成EventArgs類)

+0

好的建議謝謝 – 2011-12-29 19:44:03

+0

@DmitryMakovetskiyd:讀我的更新。 – jgauffin 2011-12-29 19:47:06

6

您需要進行空檢查,因爲在某人訂閱它之前,事件將爲空。如果直接提升它並且它爲空,則會拋出異常。

此方法用於提高的事件,而不是訂閱它。您可以從另一個類容易訂閱事件:

yourObject.PriceChanged += someMethodWithTheAppropriateSignature; 

但是,如果你想擁有的事件「火」,類需要提高的事件。 「this」參數在EventHandler<T>中提供sender參數。按照慣例,用於事件的代表有兩個參數,第一個是object sender,它應該是引發事件的對象。第二個是EventArgsEventArgs的子類,它提供特定於該事件的信息。該方法用於正確檢查null並使用適當的信息引發事件。

在這種情況下,被宣告您的活動爲:

public event EventHandler<PriceChangedEventArgs> PriceChanged; 

EventHandler<PriceChangedEventArgs>是有簽名的委託:

public delegate void EventHandler<T>(object sender, T args) where T : EventArgs 

這意味着事件必須與兩個參數提高 - 一個對象(發送者或「this」)以及一個PriceChangedEventArgs的實例。

這就是說,這個約定實際上並不是提升事件的「最佳」方式。它實際上將是更好的使用:

protected virtual void OnPriceChanged (PriceChangedEventArgs e) 
{ 
    var eventHandler = this.PriceChanged; 
    if (eventHandler != null) 
     eventHandler(this, e); 
} 

這可以保護你在多線程的情況,因爲它可能是一個單一的訂閱實際上可能你的空檢查和培養之間退訂,如果你有多個線程運行。

+0

你的代碼不是線程安全的。任何使用託管資源(或任何其他有狀態資源)的類可能已經放置在您的列表副本和實際調用之間。示例(1. listcopy,2.取消訂閱/處置,3.調用) – jgauffin 2011-12-29 19:40:36

+0

是否存在該約定的原因...爲什麼我需要傳遞發件人參數? – 2011-12-29 19:42:42

+0

@DmitryMakovetskiyd:多個事件生成器可能會使用相同的事件處理程序。發件人告訴哪個生成調用。您應該始終按照預期發送正確的事件生成器,如果不這樣做,您將打破開放/關閉原則。 – jgauffin 2011-12-29 19:44:34

4

這是調用該事件的便利。

您確實需要檢查事件是否有訂閱者,並且通常會將this作爲事件的發件人。

因爲同一個處理程序可以用於多個事件,所以傳遞發件人實例是您可以可靠地從事件中取消訂閱的唯一方法。

我想調用的首選方法是分配給一個變量第一,以免PriceChanged成爲檢查後無效,但在調用之前:使用

var handler = PriceChanged; 
if(handler != null) handler(this, e);