我不理解的委派,事件约定

本文关键字:事件 约定 委派 不理解 | 更新日期: 2023-09-27 18:19:07

我查看了C# in nutshell一书中的这个例子(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!");
  }
}

我不明白的是为什么要用这个惯例…

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

为什么我需要这个方法,为什么我关心给它"this"参数?我能直接在测试类中用pricechange方法附加那个类中的事件,然后跳过那个方法吗?

我不理解的委派,事件约定

您需要null检查,因为在有人订阅之前事件将为null。如果你直接抛出它,而它是空的,将会抛出一个异常。

此方法用于引发事件,而不是订阅它。您可以轻松地从另一个类订阅事件:

yourObject.PriceChanged += someMethodWithTheAppropriateSignature;

然而,当你想让事件"fire"时,类需要引发事件。"this"参数提供EventHandler<T>中的sender参数。按照惯例,用于事件的委托有两个参数,第一个是object sender,它应该是引发事件的对象。第二个是EventArgsEventArgs的子类,它提供特定于该事件的信息。该方法用于正确检查是否为空,并使用适当的信息引发事件。

在这种情况下,你的事件被声明为:

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

这可以在多线程场景中保护您,因为如果您有多个线程操作,那么在null检查和raise之间可能存在单个订阅取消订阅的情况。

这是为了方便调用事件。

您确实需要检查事件是否有订阅者,并且通常将this作为事件的发送方传递。

由于相同的处理程序可以用于多个事件,因此传递发送方的实例是在事件触发后可靠地取消订阅的唯一方法。

我认为调用的首选方式是先分配给一个变量,以免PriceChanged在检查后变为null,但在调用之前:

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

使用空检查,因为(事件)委托列表不是空的,而是null,如果没有订阅者。

然而,它不是线程安全的。因此,如果你开始使用BackgroundWorker或任何其他多线程技术,它可能会在你的脸上爆炸。

我建议你使用一个空的委托:

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

因为它允许你只写:

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

它是线程安全的,代码更容易阅读。

为什么我要给它一个"this"参数?!?

相同的事件处理程序可能被多个事件生成器使用。发送方告知调用针对哪个生成。你应该总是发送正确的事件生成器,因为它是预期的,你会打破打开/关闭原则,如果你不

为什么我需要这个方法?

不需要,除非您需要复制代码(例如生成EventArgs类)