This is a difficult case. You can use co / contravariance, but ...
I will simplify the code a bit. Just to close the compiler, you can do this:
public interface IMessage { } public interface Message1 : IMessage { } public class Mop1 : IConsumer<Message1> { } public interface IConsumer<out IMessage> { } class Program { static void Main(string[] args) { var list = new List<IConsumer<IMessage>>(); var mop = new Mop1(); list.Add(mop);
out IMessage
will do the trick, as suggested in another answer, but doesnβt fix anything at the root. Let me show you now want to make your interface:
public interface IConsumer<out IMessage> { object Process (IMessage m); }
aaaand it will not compile. Because just put, if you say out IMessage
, it means that return types should be derived from IMessage
, not parameter types.
So you get it like this:
public interface IConsumer<in IMessage> { object Process (IMessage m); } public class Mop1 : IConsumer<Message1> { public object Process (Message1 msg) { return null; } }
Make it a compilation and reality. But now your list.Add(mop);
will not work. And rightfully so, because your Mop1
really not applicable to IConsumer<IMessage>
, because if it were the following code, it would be possible:
list[0].Process (new Message2 ());
and this is not possible because Mop1 only accepts Message1
.
So, to answer the question. You cannot really do anything meaningful with sending messages and pure C # compiler functions. I see where this happens; you wanted static typing with messages and stuff. Unfortunately, you cannot have a list of consumers who consume certain messages in the general list; you must have abstract signatures, such as bool CanProcess(IMessage); IMessage Process(IMessage);
bool CanProcess(IMessage); IMessage Process(IMessage);
, and enter inside. You can also do it a little better with custom attributes and reflection, but then again, not just with the C # compiler.