C
C#•12mo ago
Altef

Generic types and inheritance

Hello, I am trying to implement a basic event bus to have a single point for subscribing and publishing events and keeping clients anonymous from each others. However I am having trouble defining type genericity for my event objects. Attached file is the complete code extract. The issue is raised at line 53 in subscribe() method when I try to add the event handler to my collection.
private void subscribe<TEvent, TEventHandler>()
where TEvent : Event
where TEventHandler : IEventHandler<TEvent>, new()
{
if (!_subjects.ContainsKey(typeof(TEvent)))
{
_subjects.Add(typeof(TEvent), new List<IEventHandler<Event>>());
}
_subjects[typeof(TEvent)].Add(new TEventHandler());
}
private void subscribe<TEvent, TEventHandler>()
where TEvent : Event
where TEventHandler : IEventHandler<TEvent>, new()
{
if (!_subjects.ContainsKey(typeof(TEvent)))
{
_subjects.Add(typeof(TEvent), new List<IEventHandler<Event>>());
}
_subjects[typeof(TEvent)].Add(new TEventHandler());
}
I am getting this error : Argument 1: cannot convert from 'TEventHandler' to 'Events.IEventHandler<Events.Event>' (CS1503) I don't know if I should look around improving the type constraints or if my _subjects dictionary should be typed differently. Any help would be much appreciated 🙂 ---- Full code extract below :
5 Replies
canton7
canton7•12mo ago
Your list contains IEventHandler<Event> instances. But you're trying to add a TEventHandler do it, which is an IEventHandler<TEvent>, not an IEventHandler<Event>. Put more simply, you can't do EventHandler<Event> handler = new EventHandler<UserCreatedEvent>(). The reason is that HandleEvent method. Your EventHandler<UserCreatedEvent> class has a HandleEvent(UserCreatedEvent evt) method -- i.e. it just takes UserCreatedEvent instances. But an EventHandler<Event> class has a HandleEvent(Event evt) method -- i.e. it can take any Event type. So if you do EventHandler<Event> handler = new EventHandler<UserCreatedEvent>(), you'd be able to do handler.HandleEvent(new Event()) -- you'd be able to pass an Event to a method which only accepts UserCreatedEvent instances. (Also, with event aggregators, it's much more common to pass an instance fo the Subscribe method, rather than creating the instance yourself. This lets the user create the handler themselves, pass ctor parameters, configure it, etc) If you want some keywords to read up more on this, search for "covariance" and "contravariance". You'll come across the in and out keywords, but they don't actually help this case
Altef
AltefOP•12mo ago
but there is a constraint for TEvent to inherit from Event
canton7
canton7•12mo ago
Please re-read my first message. Yes there is, but the problem is the opposite way around In other words, there's nothing stopping you from doing _subjects[typeof(UserCreatedEvent)][0].HandleEvent(new Event()) If that compiler error wasn't there, the above code would be allowed by the type system, but would obviously be bad, because an IEventHandler<UserCreatedEvent> is being passed an Event instance One quick workaround is to keep a list of "things which can invoke IEventHandlers", rather than the IEventHandlers themselves. For example, a List<Action<Event>>, and then do:
var handler = new TEventHandler();
_subjects[typeof(TEvent)].Add(evt => handler.HandleEvent((TEvent)evt);
var handler = new TEventHandler();
_subjects[typeof(TEvent)].Add(evt => handler.HandleEvent((TEvent)evt);
That works because it moves the problem to a runtime cast (the (TEvent)evt), so if you do _subjects[typeof(UserCreatedEvent)][0].Invoke(new Event()), the (TEvent)evt cast will fail to cast it to a UserCreatedEvent instance that the handler needs, and you'll get a runtime InvalidCastException
Altef
AltefOP•12mo ago
ok, I think I get a grasp on what you are explaining I am still missing some precise understanding but I will try to document myself based on your answers thank you for your time
canton7
canton7•12mo ago
Cool. Yeah everyone hits this sooner or later. In a simple case it's fairly easy to understand, but it tends to bite when the problems are hidden behind a few confounding layers, so if it's your first time seeing it, it can be hard to figure out what's going on But start with understanding why EventHandler<Event> handler = new EventHandler<UserCreatedEvent>() isn't allowed, gets your head solidly around that, then work from there
Want results from more Discord servers?
Add your server