In IMyMessage.cs
public interface IMyMessage
{
}
In IMyMessageReceiver.cs
public interface IMyMessageReceiver<T> where T: IMyMessage
{
void HandleMessage(T message);
void Subscribe();
}
In MyMessagePublisher.cs
public static class MyMessagePublisher
{
private static Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>> _subscribers;
static MyMessagePublisher
{
_subscribers = new Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>>();
}
public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage
{
Type messageType = typeof (T);
List<IMyMessageReceiver<IMyMessage>> listeners;
if(!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
List<IMyMessageReceiver<T>> newListeners = new List<IMyMessageReceiver<T>>();
// ERROR HERE: Can't convert List<IMyMessageReceiver<T>> to List<IMyMessageReceiver<IMyMessage>>
_subscribers.add(messageType, newListeners);
}
// I would then find the right list and add the receiver it to it but haven't got this far
}
}
So my hope was to use a bunch of 'IMyMessages' and 'IMyMessageReceivers' to pass messages around. I did a hard coded approach earlier but got sick of 100 different publish/subscrive function names so I figured I'd wrap it all nicely in generics.
My problem is that I can't get the code to work when using generics. Even though I specify the Type T will be of IMyMessage, I cannot use T anywhere where IMyMessage is expected. Maybe I'm just used to base/extended classes as it would work fine there. I've tried various approaches from casting, to being really generic, yet I always run in to the same issue.
OK here's how I can see it working. Since you're trying to use covariance in a way that is not supported, you'll need to avoid using generics in a few spots. But doing so won't lose any type-safety.
Create a non-generic IMessageReceiver
interface so that the types that can't use the generic parameter can use this instead:
public interface IMyMessageReceiver
{
void HandleMessage(IMyMessage message);
void Subscribe();
}
public interface IMyMessageReceiver<in T> : IMyMessageReceiver
where T : IMyMessage
{
void HandleMessage(T message);
}
You can create a base class to simplify things if you'd like:
public abstract class MyMessageReceiverBase<T> : IMyMessageReceiver<T>
where T : IMyMessage
{
public abstract void HandleMessage(T message);
public void HandleMessage(IMyMessage message)
{
if (!(message is T))
throw new InvalidOperationException();
HandleMessage((T)message);
}
public abstract void Subscribe();
}
Then you can change IMyMessageListeners
to use the non-generic version, since it doesn't really need the generic type anyways:
public interface IMyMessageListeners
{
void Add(IMyMessageReceiver receiver);
// I added this since I think this is how you're going to use it
void Send(IMyMessage message);
}
The concrete of this class looks like this:
public class MyMessageListeners : IMyMessageListeners
{
readonly List<IMyMessageReceiver> _list = new List<IMyMessageReceiver>();
public void Add(IMyMessageReceiver receiver)
{
_list.Add(receiver);
}
public void Send(IMyMessage message)
{
foreach (var listener in _list)
listener.HandleMessage(message);
}
}
Then (finally), your static class will look like this:
public static class MyMessagePublisher
{
static readonly Dictionary<Type, IMyMessageListeners> _subscribers = new Dictionary<Type, IMyMessageListeners>();
// I added this too, since I think this is how you intend to use it
public static void Publish<T>(T message) where T : IMyMessage
{
Type messageType = typeof(T);
IMyMessageListeners listeners;
if (_subscribers.TryGetValue(messageType, out listeners))
listeners.Send(message);
}
public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage
{
Type messageType = typeof(T);
IMyMessageListeners listeners;
if (!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
listeners = new MyMessageListeners();
_subscribers.Add(messageType, listeners);
}
listeners.Add(receiver);
}
}
And you can use your static class like so:
MyMessagePublisher.Subscribe(new FooMessageReceiver());
MyMessagePublisher.Publish(new FooMessage());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With