foreach loop can not cast type to the interface it implements
c#, covariance, exception
Solution
I solved this by adding another level of indirection. Having followed advice from @PeterDuniho, I broke apart the `INotification<TMessage>` interface in to two separate interfaces. By adding the new `INotificationProcessor` interface, I can change my collection of listeners from an `ISubscription` to an `INotificationProcessor` and then iterate over my collection of listeners as an INotificationProcessor.
public interface ISubscription
{
void Unsubscribe();
}
public interface INotification<TMessageType> : ISubscription where TMessageType : class, IMessage
{
void Register(Action<TMessageType, ISubscription> callback);
}
public interface INotificationProcessor
{
void ProcessMessage(IMessage message);
}
The `INotificationProcessor` implementation, implements both `INotificationProcessor` and `INotification<TMessageType>`. This allows the `Notification` class below to cast the IMessage provided in to the appropriate generic type for publication.
internal class Notification<TMessage> : INotificationProcessor, INotification<TMessage> where TMessage : class, IMessage
{
private Action<TMessage, ISubscription> callback;
public void Register(Action<TMessage, ISubscription> callbackMethod)
{
this.callback = callbackMethod;
}
public void Unsubscribe()
{
this.callback = null;
}
public void ProcessMessage(IMessage message)
{
// I can now cast my IMessage to T internally. This lets
// subscribers use this and not worry about handling the cast themselves.
this.callback(message as TMessage, this);
}
}
My `NotificationManager` can now hold a collection of `INotificationProcessor` types instead of `ISubscription` and invoke the `ProcessMessage(IMessage)` method regardless if what comes in to it is an `IMessage` or a `ServerMessage`.
public class NotificationManager
{
private ConcurrentDictionary<Type, List<INotificationProcessor>> listeners =
new ConcurrentDictionary<Type, List<INotificationProcessor>>();
public ISubscription Subscribe<TMessageType>(Action<TMessageType, ISubscription> callback) where TMessageType : class, IMessage
{
Type messageType = typeof(TMessageType);
// Create our key if it doesn't exist along with an empty collection as the value.
if (!listeners.ContainsKey(messageType))
{
listeners.TryAdd(messageType, new List<INotificationProcessor>());
}
// Add our notification to our listener collection so we can publish to it later, then return it.
var handler = new Notification<TMessageType>();
handler.Register(callback);
List<INotificationProcessor> subscribers = listeners[messageType];
lock (subscribers)
{
subscribers.Add(handler);
}
return handler;
}
public void Publish<T>(T message) where T : class, IMessage
{
Type messageType = message.GetType();
if (!listeners.ContainsKey(messageType))
{
return;
}
// Exception is thrown here due to variance issues.
foreach (INotificationProcessor handler in listeners[messageType])
{
handler.ProcessMessage(message);
}
}
}
The original app example now works without issue.
class Program
{
static void Main(string[] args)
{
var notificationManager = new NotificationManager();
ISubscription subscription = notificationManager.Subscribe<ServerMessage>(
(message, sub) => Console.WriteLine(message.Content));
notificationManager.Publish(new ServerMessage("This works"));
IMessage newMessage = MessageFactoryMethod("This works without issue.");
notificationManager.Publish(newMessage);
Console.ReadKey();
}
private static IMessage MessageFactoryMethod(string content)
{
return new ServerMessage(content);
}
}
Thanks everyone for the help.
Problem
Edited with complete, working, code-example. In my IRC app, the application receives content from an IRC server. The content is sent in to a factory and the factory spits out an `IMessage` object that can be consumed by the presentation layer of the application. The `IMessage` interface and a single implementation is shown below. ``` public interface IMessage { object GetContent(); } public interface IMessage<out TContent> : IMessage where TContent : class { TContent Content { get; } } public class ServerMessage : IMessage<string> { public ServerMessage(string content) { this.Content = content; } public string Content { get; private set; } public object GetContent() { return this.Content; } } ``` To receive the `IMessage` object, the presentation layer subscribes to notifications that are published within my domain layer. The notification system iterates over a collection of subscribers to a specified `IMessage` implementation and fires a callback method to the subscriber. ``` public interface ISubscription { void Unsubscribe(); } public interface INotification<TMessageType> : ISubscription where TMessageType : class, IMessage { void Register(Action<TMessageType, ISubscription> callback); void ProcessMessage(TMessageType message); } internal class Notification<TMessage> : INotification<TMessage> where TMessage : class, IMessage { private Action<TMessage, ISubscription> callback; public void Register(Action<TMessage, ISubscription> callbackMethod) { this.callback = callbackMethod; } public void Unsubscribe() { this.callback = null; } public void ProcessMessage(TMessage message) { this.callback(message, this); } } public class NotificationManager { private ConcurrentDictionary<Type, List<ISubscription>> listeners = new ConcurrentDictionary<Type, List<ISubscription>>(); public ISubscription Subscribe<TMessageType>(Action<TMessageType, ISubscription> callback) where TMessageType : class, IMessage { Type messageType = typeof(TMessageType); // Create our key if it doesn't exist along with an empty collection as the value. if (!listeners.ContainsKey(messageType)) { listeners.TryAdd(messageType, new List<ISubscription>()); } // Add our notification to our listener collection so we can publish to it later, then return it. var handler = new Notification<TMessageType>(); handler.Register(callback); List<ISubscription> subscribers = listeners[messageType]; lock (subscribers) { subscribers.Add(handler); } return handler; } public void Publish<T>(T message) where T : class, IMessage { Type messageType = message.GetType(); if (!listeners.ContainsKey(messageType)) { return; } // Exception is thrown here due to variance issues. foreach (INotification<T> handler in listeners[messageType]) { handler.ProcessMessage(message); } } } ``` In order to demonstrate how the above code works, I have a simple Console application that subscribes to notifications from the above `ServerMessage` type. The console app first publishes by passing the `ServerMessage` object in to the `Publish<T>` method directly. This works without any issues. The 2nd example has the app creating an IMessage instance using a factory method. The IMessage instance is then passed in to the `Publish<T>` method, causing my variance issue to throw an `InvalidCastException`. ``` class Program { static void Main(string[] args) { var notificationManager = new NotificationManager(); ISubscription subscription = notificationManager.Subscribe<ServerMessage>( (message, sub) => Console.WriteLine(message.Content)); notificationManager.Publish(new ServerMessage("This works")); IMessage newMessage = MessageFactoryMethod("This throws exception"); notificationManager.Publish(newMessage); Console.ReadKey(); } private static IMessage MessageFactoryMethod(string content) { return new ServerMessage(content); } } ``` The exception states that I can not cast an `INotification<IMessage>` (what the Publish method is understands the message being published to be) in to an `INotification<ServerMessage>`. I have tried to mark the INotification interface generic as contravariant, like `INotification<in TMessageType>` but can't do that because I'm consuming `TMessageType` as a parameter to the `Register` method's callbacks. Should I split the interface in to two separate interfaces? One that can register and one that can consume? Is that the best alternative? Any additional help on this would be great.