简体   繁体   中英

How virtual events work in C#?

Below is the program I used for the test. It prints (as expected):

Raise A
Event from A
Raise B
Event from B

Now, if we change first two lines of the Main to be:

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

the Program will print:

Raise A
Raise B
Event from B

which is also expected, as overriding event hides the private backing field in the base class and therefore events fired by the base class are not visible to clients of the derived class.

Now I am changing the same lines to:

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

and the program starts printing:

Raise A
Raise B
Event from A
Event from B

What's going on?

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();
    }
}

We have a single instance (of B) which has the following fields:

  • A.VirtualEvent: null
  • B.VirtualEvent: Two event handlers

The call to a.RaiseA() just prints "Raise A" - but nothing more, because the private field in A is null.

The call to b.RaiseB() prints the remaining three lines, because the event has been subscribed to twice (once to print "Event from A" and once to print "Event from B").

Does that help?

EDIT: To make it clearer - think of the virtual event as a pair of virtual methods. It's very much like this:

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
}

Now is it clearer? It's not quite like that because as far as I'm aware you can't override just "part" of an event (ie one of the methods) but it gives the right general impression.

You hooked up two event handlers to the same event. Since A and B are pointing to the same object, when you call b.RaiseB() both event handlers get fired. So first you're calling RaiseA which is a basecalss method. That prints Raise A. It then doesn't actually fire off the event because it's null. Then, you're raising B but TWO handlers are hooked up to it, therefore it first prints Raise B, and when the event fires, both handlers get called.

Try making your RaiseA function protected + virtual.

A rule of thumb: If derived class overrides event accessors, it must also override the function that invokes the event.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM