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
Чому він повинен бути віртуальним? Я б оголосив це віртуальним, якби хотів, щоб спадкоємці змінили спосіб піднесення події, але більшу частину часу я не бачу причин для цього ...
Фредерік Гейселс


5
Що стосується того, щоб зробити метод віртуальним, щоб спадкоємці могли перекрити поведінку виклику події: Скільки разів ви опинялися в ситуації, коли це було необхідно? Поруч із цим; у методі переопределення ви не можете підняти подію, оскільки ви отримаєте ту саму помилку, що і згадувана TS.
Фредерік Гейсельс

2
@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
Зауважте, що ця стаття застерігає від оголошення оголошень віртуальних подій та їх відміни,
громадський бездротовий зв'язок

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.