Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();
    }
}
like image 674
Prankster Avatar asked Apr 03 '09 17:04

Prankster


2 Answers

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 (i.e. one of the methods) but it gives the right general impression.

like image 145
Jon Skeet Avatar answered Oct 21 '22 11:10

Jon Skeet


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.

like image 33
BFree Avatar answered Oct 21 '22 10:10

BFree