Подтвердить что ты не робот

Почему события не могут использоваться одинаково в производных классах, как в базовом классе в С#?

В следующем коде я хочу расширить поведение класса, выведя/подклассифицируя его и воспользуемся событием базового класса:

public class A
{
    public event EventHandler SomeEvent;

    public void someMethod()
    {
        if(SomeEvent != null) SomeEvent(this, someArgs);
    }
}

public class B : A
{
    public void someOtherMethod()
    {
        if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible?
//Error: The event 'SomeEvent' can only appear on the left hand side of += or -= 
//(except when used from within the type 'A')
    }
}

Почему это не возможно?

И какое общее решение для такого рода ситуаций?

4b9b3361

Ответ 1

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

Для объяснения причин, по которым читайте ответ, или спецификация С#, который описывает, как компилятор автоматически создает частное поле.

Вот одна возможная работа.

public class A
{
    public event EventHandler SomeEvent;

    public void someMethod()
    {
        OnSomeEvent();
    }

    protected void OnSomeEvent()
    {
        EventHandler handler = SomeEvent;
        if(handler != null)
            handler(this, someArgs);
    }
}

public class B : A
{
    public void someOtherMethod()
    {
        OnSomeEvent();
    }
}

Изменить: Обновлен код, основанный на Руководство по дизайну рамок раздела 5.4 и напоминания другими.

Ответ 2

Другие объяснили, как обойти эту проблему, но не почему она подходит.

Когда вы объявляете публичное полевое событие, компилятор создает публичное событие и частное поле. В пределах одного класса (или вложенных классов) вы можете получить прямо на поле, например. для вызова всех обработчиков. Из других классов вы видите только событие, которое разрешает подписку и отмену подписки.

Ответ 3

Тодд ответ правильный. Часто вы увидите, что это реализовано на всей платформе .NET как методы OnXXX(EventArgs):

public class Foo
{
    public event EventHandler Click;

    protected virtual void OnClick(EventArgs e)
    {
        var click = Click;
        if (click != null)
            click(this, e);
    }
}

Я настоятельно рекомендую вам рассмотреть шаблон EventArgs<T>/EventHandler<T>, прежде чем вы сможете сделать все CustomEventArgs/CustomEventHandler для создания событий.

Ответ 4

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

События в С# публично представлены парой методов add_SomeEvent и remove_SomeEvent, поэтому вы можете подписаться на событие вне класса, но не поднять его.

Ответ 5

Я бы ответил, что вам не нужно это делать.

С# прекрасно применяет Только тип, объявляющий/публикующий событие, должен запускать/поднимать его. Если базовый класс, которому доверяют деривации, иметь возможность поднять свои события, создатель будет выставлять защищенные методы для этого. Если они не существуют, это хороший намек на то, что вы, вероятно, не должны этого делать.

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

public class GoodVigilante
{
  public event EventHandler LaunchMissiles;

  public void Evaluate()
  {
    Action a = DetermineCourseOfAction(); // method that evaluates every possible
// non-violent solution before resorting to 'Unleashing the fury'

    if (null != a) 
    { a.Do(); }
    else
    {  if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); }
  }

  virtual protected string WhatsTheTime()
  {  return DateTime.Now.ToString();  }
  ....   
}
public class TriggerHappy : GoodVigilante
{
  protected override string WhatsTheTime()
  {
    if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty);
  }

}

// client code
GoodVigilante a = new GoodVigilante();
a.LaunchMissiles += new EventHandler(FireAway);
GoodVigilante b = new TriggerHappy();             // rogue/imposter
b.LaunchMissiles += new EventHandler(FireAway);

private void FireAway(object sender, EventArgs e)
{
  // nuke 'em
}

Ответ 6

Оберните его защищенным виртуальным методом On...:

public class BaseClass
{
    public event EventHandler<MyArgs> SomeEvent;

    protected virtual void OnSomeEvent()
    {
        if(SomeEvent!= null)
            SomeEvent(this, new MyArgs(...) );
    }
}

Затем переопределите это в производном классе

public class DerivedClass : BaseClass
{
    protected override void OnSomeEvent()
    {
        //do something

        base.OnSomeEvent();
    }
}

Вы установите этот шаблон на всем протяжении .Net - все формы и веб-элементы управления следуют за ним.

Не используйте префикс Raise... - это не соответствует стандартам MS и может вызвать путаницу в другом месте.