делегат, соглашение о мероприятии, которое я не понимаю - PullRequest
3 голосов
/ 29 декабря 2011

Я посмотрел на этот пример из книги C# в словах (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"?!? Не могу я просто прикрепить событие из этого класса с помощью метода PriceChanged в тестовом классе и пропустить этот метод?!?

Ответы [ 3 ]

6 голосов
/ 29 декабря 2011

Вам нужна нулевая проверка, так как событие будет нулевым, пока кто-нибудь не подпишется на него. Если вы поднимете его напрямую, и он будет нулевым, будет сгенерировано исключение.

Этот метод используется для повышения события, а не для подписки на него. Вы можете легко подписаться на событие из другого класса:

yourObject.PriceChanged += someMethodWithTheAppropriateSignature;

Однако, когда вы хотите, чтобы событие "огонь", класс должен вызвать событие. Параметр this предоставляет аргумент sender в EventHandler<T>. По соглашению делегаты, используемые для событий, имеют два параметра, первый - object sender, который должен быть объектом, вызвавшим событие. Вторым является EventArgs или подкласс EventArgs, который предоставляет информацию, относящуюся к этому событию. Этот метод используется для правильной проверки на нулевое значение и вызова события с соответствующей информацией.

В этом случае ваше событие объявляется как:

public event EventHandler<PriceChangedEventArgs> PriceChanged;

EventHandler<PriceChangedEventArgs> - делегат с подписью:

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

Это означает, что событие должно быть вызвано двумя параметрами - объектом (отправителем или «этим») и экземпляром PriceChangedEventArgs.

При этом, это соглашение не является на самом деле "лучшим" способом поднять событие. На самом деле было бы лучше использовать:

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

Это защищает вас в многопоточных сценариях, поскольку вполне возможно, что одна подписка фактически отменит подписку между проверкой на ноль и повышением, если у вас работает несколько потоков.

4 голосов
/ 29 декабря 2011

Это удобство для вызова события.

Вам необходимо проверить, что событие имеет подписчиков, и обычно в качестве отправителя события передается this.

Поскольку один и тот же обработчик можно использовать для нескольких событий, передача экземпляра отправителя является единственным способом надежной отмены подписки на событие после его запуска.

Я думаю, что предпочтительный способ вызова - сначала присвоить переменную, чтобы PriceChanged не стал нулевым после проверки, но перед вызовом:

var handler = PriceChanged;
if(handler != null) handler(this, e);
3 голосов
/ 29 декабря 2011

Нулевые проверки используются, поскольку список делегатов (события) не пуст, а null, если нет подписчиков.

Однако, он не безопасен для потоков.Так что это может взорваться вам в лицо, если вы начнете использовать BackgroundWorker или любую другую многопоточную технику.

Я предлагаю вместо этого использовать пустой делегат:

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

Так какэто позволяет вам просто написать:

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

Это потокобезопасно, и код легче читать.

почему я хочу задать ему параметр "this"?!?

Один и тот же обработчик событий может использоваться несколькими генераторами событий.Отправитель сообщает, для чего предназначен вызов.Вы должны всегда отправлять правильный генератор событий, как ожидается, и вы нарушите принцип открытия / закрытия, если вы не

Зачем мне нужен этот метод?

Нет, если вы не скопируете код в противном случае (например, создаете класс EventArgs)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...