Virtual events in C#

Yesterday I ran into an interesting (and dangerous) little detail about the C# compiler regarding virtual (field-like) events; they do not behave as you might expect. Consider this code, which I've simplified from a real world example of some refactoring gone wrong:

public abstract class MyClassBase
{
    public virtual event EventHandler<EventArgs> MyEvent;

    public void DoStuff()
    {
        if (MyEvent != null)
        {
            MyEvent(this, EventArgs.Empty);
        }
    }
}

public class MyClassDerived : MyClassBase
{
    public override event EventHandler<EventArgs> MyEvent;
}

To my surprise, when one subscribes to MyEvent, it will never be called:

MyClassBase obj = new MyClassDerived();
obj.MyEvent += (s, e) => { /* Never gets called */ };

// Call method on base class that raises MyEvent
obj.DoStuff();

For reasons that are not clear to me, the compiler actually creates a new field for the overridden event; both of them are highlighted in the QuickWatch screenshot below.

This behavior would be expected when the new keyword had been used, but it goes against the expected semantics of the virtual keyword.

Now, luckily is is rarely necessary to make an event virtual, so this is not usually a problem. However, I would suggest that it would have been helpful for the compiler to produce a clear warning, to prevent developers from shooting themselves in the foot. Instead, the compiler only shows a seemingly innocuous warning message:
The event 'MyClassDerived.MyEvent' is never used.

After some searching on MSDN, I ran into a warning for exactly this situation, which unfortunately does not provide a satisfactory explanation either (emphasis mine **shudder**):

Do not declare virtual events in a base class and override them in a derived class. The C# compiler does not handle these correctly and it is unpredictable whether a subscriber to the derived event will actually be subscribing to the base class event.

ReSharper to the Rescue

Okay, so the C# compiler isn't being particularly helpful in this case. How about third party tools?

Personally I use ReSharper a lot, and fortunately it is aware of this problem and tries to warn the programmer of their mistake, by displaying a squiggly line with the message Invocation of polymorphic field-like events.

It's definitely better than nothing. Usually these squiggly lines indicate code issues that should be taken seriously. However, many of these messages are a missed oppurtunity to educate the developer; ReSharper makes no attempt to explain to me why my code is bad.

Back to my real world code for a bit: this warning message was not appearing here because I tend to follow a pattern when raising events:

public void DoStuff()
{
    RaiseEvent(MyEvent, EventArgs.Empty);
}

private void RaiseEvent<T>(EventHandler<T> evt, T eventArgs)
{
    if (evt != null)
    {
        evt(this, eventArgs);
    }
}

This pattern cleans up code a little bit when events are used a lot. At the same time, it improves thread safety of the code. Unfortunately, it also stops ReSharper from making this particular analysis. At the place of declaration in MyClassDerived, ReSharper does not show any type of warning.

When first writing the code for MyClassDerived, ReSharper does offer some assistance. Normally what I do when I want to override something is that I merely type override  and let IntelliSense do the work by selecting the member I want. ReSharper changes the code that gets generated when you do this, into the following:

public override event EventHandler<EventArgs> MyEvent
{
    add { base.MyEvent += value; }
    remove { base.MyEvent -= value; }
}

Rather than using a so-called field-like event declaration, an explicit implementation is given. Roughly analogous to auto-implemented properties versus regular properties, this code does not result in the compiler automatically creating a backing field for the event. Instead, we implement the event by reusing the event in the base class.

Obviously in this particular case the behavior is not being changed at all – so the override is not useful here – but I hope it illustrates the difference.

Conclusion

The only reason I can think of why one might want to declare an event as virtual, would be to include some non-standard behavior; for example some doing some checking before actually adding a subscriber. In this case, the overriden event would never be field-like in the first place. It's hard to come up with a practical application for even this case, however.

All in all, this behavior of the C# compiler is not a big problem, but it was certainly enough to raise an eyebrow for me. I was surprised to find something that seems to be a rough edge in the language or its compiler after all these years and I wonder why the current implementation was ever chosen. What do you think?

Add comment

Loading