Interface Inheritance Using Shared Lists

I want to create a common system of producers and consumers for a simple project.

Now I have

public interface IMessage { } public interface Message1 : IMessage { } public interface Message2 : IMessage { } public interface IConsumer<T> { } public interface IProducer<T> { } public class Mop1 : IConsumer<Message1>, IProducer<Message2> { } class Program { static void Main(string[] args) { var list = new List<IConsumer<IMessage>>(); var mop = new Mop1(); list.Add(mop); // Error occurs here } } 

The last line gives an error, for example cannot convert from 'Mop1' to 'IConsumer<GenericPubSub.IMessage>'

But Mop1 implements an IConsumer type called by iMessage. Is this a problem with some deviations here? What's bad about it?

+6
source share
2 answers

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); // Error occurs here } } 

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.

+5
source

If you plan to implement a typical manufacturer, a consumer sample with these interfaces, first see this answer .


If you want IConsumer<TMessage> also be IConsumer<TMessageBase> , where TMessageBase is any type that inherits or implements TMessage , then you need to make your general parameter covariant. Use the out modifier.

 public interface IConsumer<out T> { } 

Now IConsumer<Message1> can be assigned to IConsumer<IMessage> , because Message1 implements IMessage .

+4
source

Source: https://habr.com/ru/post/979721/


All Articles