简体   繁体   中英

C#: What are virtual events and how can they be used?

How does a virtual event work? How would you override it? How would that work? And in what cases would you do that?

Would it for example be an ok replacement for protected OnEvent methods? So that inheriting classes could just override the event and raise it directly? Or would that be wrong or just not work?

The MSDN says this about it:

An event can be marked as a virtual event by using the virtual keyword. This enables derived classes to override the event behavior by using the override keyword. An event overriding a virtual event can also be sealed, which specifies that for derived classes it is no longer virtual.

But that didn't make me much wiser. The sealed stuff is obvious though.

Note: I've seen the How virtual events work in C# ? question, but it wasn't really about how virtual events work. Rather it was how that person got the result he got from using them. Tried to figure out what virtual events were from his example and the answers, but couldn't really make sense out of it.

A virtual event is simply one which can be overridden in a derived class.

Are you happy with the concept of a virtual property, with a getter and setter which can be overridden? If so, you can think of a virtual event in exactly the same way: instead of a getter and setter, there's an "add" operation and a "remove" operation. These can be virtual, so handled polymorphically. You implement them the same way you implement any other virtual/overridden member.

Example:

using System;

class Base
{
    public virtual event EventHandler Foo
    {
        add
        {
            Console.WriteLine("Base Foo.add called");
        }
        remove
        {
            Console.WriteLine("Base Foo.remove called");
        }
    }
}

class Derived : Base
{
    public override event EventHandler Foo
    {
        add
        {
            Console.WriteLine("Derived Foo.add called");
        }
        remove
        {
            Console.WriteLine("Derived Foo.remove called");
        }
    }
}

class Test
{
    static void Main()
    {
        Base x = new Derived();

        x.Foo += (sender, args) => {};
    }
}

Note that the event itself is not responsible for what happens when it is raised - just the add/remove side. (In C#, anyway; the CLR itself has the notion of raising, but we'll ignore that for the moment.)

You may also want to read my article on events if you're slightly hazy on the difference between an event and a delegate.

Personally I find it very rare that I want a virtual event.

Also note that in C# a derived class is not able to fire the event purely defined in base class (no matter what modifier it has). So we either need have a new or overriding event for the derived class and in most cases an overriding one is preferred if the same event is meant to be fired.

For Foo to be raised there are 2 alternatives which I find missing from the accepted answer.

public class Base 
{
    public virtual event EventHandler Foo;

    public void Bar()
    {
        RaiseFoo();
    }

    protected virtual void RaiseFoo()
    {
        Foo?.Invoke(this, EventArgs.Empty);
    }
}

public class Derived : Base
{
    // In this case we use the base Foo as a backing store.
    public override event EventHandler Foo
    {
        add { base.Foo += value; }
        remove { base.Foo -= value; }
    }
}

public class Derived2 : Base
{
    public override event EventHandler Foo;

    // In this case we raise the overriden Foo.
    protected override void RaiseFoo()
    {
        Foo?.Invoke(this, EventArgs.Empty);
    }
}

class Test
{
    static void Main()
    {
        Base x = new Derived();
        x.Foo += (sender, e) => { };
        x.Bar();
        Base x2 = new Derived2();
        x2.Foo += (sender, e) => { };
        x2.Bar();
    }
}

Note that this doesn't work:

public class Derived3 : Base
{
    public override event EventHandler Foo
    {
        add { base.Foo += value; }
        remove { base.Foo -= value; }
    }

    protected override void RaiseFoo()
    {
        Foo?.Invoke(this, EventArgs.Empty);
    }
}

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