EventHandler upcasting/downcasting

Viewed 135

I've a library that defines ConfigurationChanged event. It used to use EventArgs, but I want to extend it to MyEventArgs, but don't want to bring BC break to customers. They shall still be able to have EventArgs signature to consume the event args.

It works fine as long as they assign their handling method directly. But If some of them pass EventHandler<EventArgs> around, then it won't assign with Cannot implicitly convert type 'System.EventHandler<System.EventArgs>' to 'System.EventHandler<MyEventArgs>'.

Code snippet

public class Test
{
    private static event EventHandler<MyEventArgs> ConfigurationChanged;

    public Test(EventHandler<EventArgs> eventHandler)
    {
        ConfigurationChanged += eventHandler; // Cannot implicitly convert
        ConfigurationChanged += OnConfigurationChanged; // Works
    }

    private static void OnConfigurationChanged(object sender, EventArgs e)
    {
    }
}

public class MyEventArgs : EventArgs
{
}

Would you have any workaround/best practice around that or there is no way to obey breaking change?

1 Answers

Changing the type of the event is a binary breaking change whatever you do. What you've described is trying to make it not a source breaking change.

What you can do is add the more specific event as a separate event, and proxy event subscription by creating a new EventHandler<MyEventArgs> from the handler that's passed to the add/remove parts. Fortunately, delegate equality still works in that situation, so unsubscribing does the right thing automatically:

using System;

public class MyEventArgs : EventArgs
{
}

public class Test
{
    public event EventHandler<EventArgs> GeneralEvent
    {
        add => SpecificEvent += new EventHandler<MyEventArgs>(value);
        remove => SpecificEvent -= new EventHandler<MyEventArgs>(value);
    }

    public event EventHandler<MyEventArgs> SpecificEvent;

    private void OnEvent(MyEventArgs args)
    {
        SpecificEvent?.Invoke(this, args);
    }

    public static void Main()
    {
        var test = new Test();

        EventHandler<EventArgs> generalHandler = (sender, args) => Console.WriteLine("General");
        EventHandler<MyEventArgs> specificHandler = (sender, args) => Console.WriteLine("Specific");

        test.GeneralEvent += generalHandler;
        test.SpecificEvent += specificHandler;
        Console.WriteLine("Raising event with both subscribed");
        test.OnEvent(new MyEventArgs());

        test.GeneralEvent -= generalHandler;
        test.SpecificEvent -= specificHandler;
        Console.WriteLine("Raising event with both unsubscribed");
        test.OnEvent(new MyEventArgs());
    }
}
Related