Как виртуальные события работают в C #? - PullRequest
9 голосов
/ 03 апреля 2009

Ниже приведена программа, которую я использовал для теста. Он печатает (как и ожидалось):

Raise A
Event from A
Raise B
Event from B

Теперь, если мы изменим первые две строки Main на:

        A a = new B();
        B b = new B();

Программа напечатает:

Raise A
Raise B
Event from B

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

Теперь я изменяю те же строки на:

 B b = new B();
 A a = b;

и программа начинает печатать:

Raise A
Raise B
Event from A
Event from B

Что происходит?

class A
{
    public virtual event EventHandler VirtualEvent;
    public void RaiseA()
    {
        Console.WriteLine("Raise A");
        if (VirtualEvent != null)
        {
            VirtualEvent(this, EventArgs.Empty);
        }
    }
}
class B : A
{
    public override event EventHandler VirtualEvent;
    public void RaiseB()
    {
        Console.WriteLine("Raise B");             
        if (VirtualEvent != null)
        {
            VirtualEvent(this, EventArgs.Empty);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        B b = new B();

        a.VirtualEvent += (s, e) => Console.WriteLine("Event from A");
        b.VirtualEvent += (s, e) => Console.WriteLine("Event from B");

        a.RaiseA();
        b.RaiseB();
    }
}

Ответы [ 3 ]

12 голосов
/ 03 апреля 2009

У нас есть один экземпляр (из B), который имеет следующие поля:

  • A.VirtualEvent: null
  • B.VirtualEvent: два обработчика событий

При вызове a.RaiseA() просто выводится «Поднять A» - но не более того, поскольку личное поле в A равно нулю.

Вызов b.RaiseB() печатает оставшиеся три строки, потому что на событие было подписано дважды (один раз для печати «Событие из А» и один раз для печати «Событие из В»).

Это помогает?

РЕДАКТИРОВАТЬ: Чтобы сделать его более понятным - представьте виртуальное событие как пару виртуальных методов Это очень похоже на это:

public class A
{
    private EventHandler handlerA;

    public virtual void AddEventHandler(EventHandler handler)
    {
        handlerA += handler;
    }

    public virtual void RemoveEventHandler(EventHandler handler)
    {
        handlerA -= handler;
    }

    // RaiseA stuff
}

public class B : A
{
    private EventHandler handlerB;

    public override void AddEventHandler(EventHandler handler)
    {
        handlerB += handler;
    }

    public override void RemoveEventHandler(EventHandler handler)
    {
        handlerB -= handler;
    }

    // RaiseB stuff
}

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

2 голосов
/ 03 апреля 2009

Вы подключили два обработчика событий к одному и тому же событию. Поскольку A и B указывают на один и тот же объект, при вызове b.RaiseB () оба обработчика событий запускаются. Итак, сначала вы вызываете RaiseA, который является методом basecalss. Это печатает Raise A. Затем оно фактически не запускает событие, потому что оно нулевое. Затем вы поднимаете B, но к нему подключаются обработчики TWO , поэтому сначала печатается Raise B, и когда происходит событие, оба обработчика вызывают.

0 голосов
/ 03 апреля 2009

Попробуйте сделать вашу функцию RaiseA защищенной + виртуальной.

Практическое правило. Если производный класс переопределяет средства доступа к событиям, он также должен переопределять функцию, которая вызывает событие.

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