Question

I'm trying to register a generic that derives from a base class in the following way, but getting the error :

cannot convert MyCallback<T> expression to type MyCallback<Event>

I was hoping the constraints would make this possible but am I missing something?

public class Event
{ };

public delegate void MyCallback<T>(T arg1) where T : Event;

static class EventDispatcher
{

    public static Dictionary<string, MyCallback<Event>> eventTable = new Dictionary<string, MyCallback<Event>>();

    static void RegisterCallback<T>(MyCallback<T> callback) where T : Event
    {
        eventTable.Add("test", callback);
    }
}
Was it helpful?

Solution

When you have a MyCallback<Event> you're saying that you have a method that can take any type of event. It can accept an EventOne, or an EventTwo, or a SomeOtherEvent.

Let's say I call RegisterCallback and pass in a delegate pointing to a method with this signature:

public static void Foo(SomeOtherEvent arg)

If your code would work, and I could assign that to a MyCallback<Event>, then I could pass in an EventOne instance to that method when calling it. That's obviously a problem.

There's a term for that; you're expecting MyCallback to be covariant with respect to it's generic argument. In fact, it's contravariant. If I have a method that can accept any type of event, I can clearly pass in a SomeEvent, or a SomeOtherEvent, meaning I could assign a MyCallback<Event> to a MyCallback<SomeOtherEvent>, rather than the other way around.

If you want to tell the compiler that, "I know that this method cannot actually be called with any type of event, but I want you to allow this check and only fail at runtime if the given argument is not of the proper type." then you can do that, assuming you actually have a way of ensuring you call each callback with the proper arguments. You can't just do a cast either; you need to wrap the method in a new method that does the cast:

eventTable.Add("test", e => callback((T)e));

OTHER TIPS

You need to have the type parameter be part of the EventDispatcher class:

public class EventDispatcher<T> : where T : Event {
  public Dictionary<string, MyCallback<T>> eventTable = new Dictionary<string, MyCallback<T>>();

  void RegisterCallback(MyCallback<T> callback) {
    eventTable.Add("test", callback);
  }
}

This is because the MyCallback<Event> declared in eventTable is not going to be compiled into the same type declared in RegisteredCallback when written like your example.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top