C #: Возбуждение унаследованного события


144

У меня есть базовый класс, который содержит следующие события:

public event EventHandler Loading;
public event EventHandler Finished;

В классе, который наследуется от этого базового класса, я пытаюсь вызвать событие:

this.Loading(this, new EventHandler()); // All we care about is which object is loading.

Я получаю следующую ошибку:

Событие BaseClass.Loading может появляться только в левой части + = или - = (BaseClass ')

Я предполагаю, что не могу получить доступ к этим событиям так же, как другие унаследованные члены?


На этот вопрос уже ответил Фредерик Гейсель . Такая же практика рекомендуется для Microsoft Docs: как вызывать события базового класса в производных классах (Руководство по программированию в C #) в качестве официального «Руководства по программированию в C #»
Элиаху Аарон,

Ответы:


160

Что вы должны сделать, это:

В вашем базовом классе (где вы объявили события) создайте защищенные методы, которые можно использовать для вызова событий:

public class MyClass
{
   public event EventHandler Loading;
   public event EventHandler Finished;

   protected virtual void OnLoading(EventArgs e)
   {
       EventHandler handler = Loading;
       if( handler != null )
       {
           handler(this, e);
       }
   }

   protected virtual void OnFinished(EventArgs e)
   {
       EventHandler handler = Finished;
       if( handler != null )
       {
           handler(this, e);
       }
   }
}

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

Затем в классах, которые наследуются от этого базового класса, вы можете просто вызвать методы OnFinished или OnLoading, чтобы вызвать события:

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}

5
Эти методы должны быть защищены виртуально, если нет причин поступать иначе.
Макс Шмелинг

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

4
Официальные рекомендации: msdn.microsoft.com/en-us/library/w369ty8x(VS.80).aspx
meandmycode

5
Что касается того, чтобы сделать метод виртуальным, чтобы наследники могли переопределить поведение вызова события: сколько раз вы были в ситуации, когда это было необходимо? Рядом с этим; в переопределенном методе вы не можете вызвать событие, так как вы получите ту же ошибку, что и упомянутая TS.
Фредерик Гейселс

2
@Verax Я следую этому, потому что рассуждения разумны, в зависимости от того, насколько многократно и расширяемый я ожидаю от кода, я предоставил официальные рекомендации, подтверждающие это ... на момент написания вы также кажетесь очень новым для .NET Verax так что немного озадачивает, что вы выкопали это, чтобы не согласиться с моим 7-летним опытом
meandmycode

123

Вы можете получить доступ только к событию в объявленном классе, поскольку .NET создает закрытые переменные экземпляра за кулисами, которые фактически содержат делегат. Делая это..

public event EventHandler MyPropertyChanged;

на самом деле делает это;

private EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

и делает это ...

MyPropertyChanged(this, EventArgs.Empty);

на самом деле это ...

myPropertyChangedDelegate(this, EventArgs.Empty);

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

Соглашение состоит в том, чтобы обеспечить что-то подобное в объявлении класса.

protected virtual void OnMyPropertyChanged(EventArgs e)
{
    EventHandler invoker = MyPropertyChanged;

    if(invoker != null) invoker(this, e);
}

Затем вы можете позвонить OnMyPropertyChanged(EventArgs.Empty)из любого места в этом классе или из иерархии наследования, чтобы вызвать событие.


У меня были некоторые проблемы с реализацией этого ... stackoverflow.com/q/10593632/328397
goodguys_activate

Я предпочитаю этот ответ, поскольку он объясняет, ПОЧЕМУ вы должны использовать подход, а не только подход. Хорошая работа.
Ковбойдан

8

Я предполагаю, что не могу получить доступ к этим событиям так же, как другие унаследованные члены?

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

public event EventHandler Loading;

protected virtual void OnLoading() {
    EventHandler handler = Loading;
    if (handler != null)
        handler(this, EventArgs.Empty);
}

Вызывается в унаследованном классе:

OnLoading();

0

Вы можете попробовать этот способ, это работает для меня:

public delegate void MyEventHaldler(object sender, EventArgs e);

public class B
{
    public virtual event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

public class D : B
{
    public override event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

2
Обратите внимание, что эта статья предостерегает от объявления виртуальных событий и их переопределения, так как утверждает, что компилятор не обрабатывает это правильно: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
общедоступная беспроводная связь,

0

не воскрешать старую ветку, но на случай, если кто-то ищет, я сделал

protected EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

Это позволяет вам наследовать событие в производном классе, чтобы вы могли вызывать его без необходимости переноса метода, сохраняя синтаксис + =. Я думаю, вы могли бы сделать это с помощью методов упаковки, если вы сделали

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.