2014-12-16 4 views
6

Я хочу создать общую систему производителей и потребителей для простого проекта.Наследование интерфейса с помощью общих списков

То, что я сейчас

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 
    } 
} 

Последняя строка выдает ошибку, как cannot convert from 'Mop1' to 'IConsumer<GenericPubSub.IMessage>'

Но MOP1 реализует IConsumer из IMessage производного типа. Это проблема с некоторыми отклонениями здесь? Что в этом плохого?

ответ

4

В случае, если вы планируете внедрить типичный производитель, образец потребителя с этими интерфейсами, пожалуйста, см. this answer.


Если вы хотите, чтобы IConsumer<TMessage>также быть IConsumer<TMessageBase>, где TMessageBase любого типа, который TMessage наследует или реализует, то вам необходимо сделать свой общий параметр коварианты. Используйте модификатор out.

public interface IConsumer<out T> { } 

IConsumer<Message1> Теперь отнесено к IConsumer<IMessage>, поскольку Message1 реализует IMessage.

+0

Это довольно круто. –

+2

За исключением того, что это не совсем то, хотя компилируется.Потребитель принимает сообщения, поэтому он должен быть 'in', но тогда он не будет компилироваться. Исходная логика нарушена. – Andrey

+0

@ Andrey Нам нужно посмотреть на некоторые подписи методов, чтобы выяснить, что должно быть и должно быть. В настоящее время интерфейсы полностью пусты. –

5

Это сложный случай. Вы можете использовать co/contravariance, но ...

Я немного упрощу код. Просто заткнись компилятор вы можете сделать это:

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 будет делать трюк, как это предлагается в другой ответ, но принципиально ничего не исправить. Позвольте мне показать, теперь вы хотите сделать свой интерфейс:

public interface IConsumer<out IMessage> 
{ 
    object Process (IMessage m); 
} 

aaaand не компилируется. Потому что просто положите, если вы скажете out IMessage, это означает, что типы возврата должны быть получены из IMessage, а не типов параметров.

Таким образом, вы будете иметь это так:

public interface IConsumer<in IMessage> 
{ 
    object Process (IMessage m); 
} 

public class Mop1 : IConsumer<Message1> 
{ 
    public object Process (Message1 msg) 
    { 
     return null; 
    } 
} 

Чтобы сделать его скомпилировать и действительным. Но теперь ваш list.Add(mop); не будет работать. И это справедливо, потому что ваш Mop1 действительно не преобразуемый IConsumer<IMessage>, потому что если он был следующим код будет возможен:

list[0].Process (new Message2()); 

и это не представляется возможным, потому что СС1 принимает только Message1.

Чтобы ответить на вопрос. Вы не можете реально сделать что-либо значимое с отправкой сообщений и чистыми функциями компилятора C#. Я вижу, где это происходит, вы хотели, чтобы статическая типизация с сообщениями и прочее. К сожалению, у вас не может быть списка потребителей, которые потребляют определенные сообщения в общем списке, у вас должны быть абстрактные сигнатуры, такие как bool CanProcess(IMessage); IMessage Process(IMessage); и литые внутрь. Вы также можете сделать это немного лучше с пользовательскими атрибутами и отражением, но опять же, а не только с C# компилятором.

+0

Предполагая, что ОП планировал реализовать свои интерфейсы (что, я признаю, очень вероятно), это гораздо более полезный ответ на вопрос. –

+0

@ Асад только предположение Я сделал, что 'потребитель' потребляет, т. Е. Принимает. – Andrey

+0

Наш гипотетический' IConsumer .Consume' может использовать параметр динамического сообщения и быть интерфейсом маркера, насколько вам известно. Как я уже сказал, есть много предположений, основанных на пустом интерфейсе. –

Смежные вопросы