2014-12-16 16 views
6

Chcę zbudować ogólny system producentów i konsumentów dla prostego projektu.Dziedziczenie interfejsu z ogólnymi listami

Co mam teraz jest

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

Ostatni wiersz daje błąd jak cannot convert from 'Mop1' to 'IConsumer<GenericPubSub.IMessage>'

Ale MOP1 implementuje IConsumer o iMessage typu pochodnego. Czy jest tu jakiś problem z wariancją? Co jest w tym złego?

Odpowiedz

4

Jeśli masz zamiar wdrożyć typowego producenta, wzór konsumencki z tymi interfejsami, najpierw zobacz: this answer.


Jeśli chcesz IConsumer<TMessage> do również być IConsumer<TMessageBase>, gdzie TMessageBase jest każdy rodzaj że TMessage dziedziczy lub narzędzia, to trzeba dokonać rodzajowe parametru kowariantna. Użyj modyfikatora out.

public interface IConsumer<out T> { } 

Teraz IConsumer<Message1> jest przypisane do IConsumer<IMessage>, ponieważ Message1 realizuje IMessage.

+0

To całkiem fajne. –

+2

Poza tym, że nie jest to dokładnie to, chociaż kompiluje.Konsument pobiera wiadomości, więc powinno to być "w", ale wtedy nie będzie się kompilować. Oryginalna logika jest zepsuta. – Andrey

+0

@Andrey Musimy przyjrzeć się sygnaturom metod, aby dowiedzieć się, co powinno być i gdzie się znajduje. W tej chwili interfejsy są całkowicie puste. –

5

To podchwytliwy przypadek. Możesz użyć ko/contravariance, ale ...

Uproszczę trochę kod. Wystarczy zamknąć kompilator można to zrobić:

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 rade jak sugeruje inną odpowiedź, ale zasadniczo nie ma niczego naprawić. Pozwól mi pokazać, teraz chcesz, aby twój interfejs:

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

aaa i nie będzie się kompilować. Ponieważ po prostu umieścić, jeśli mówisz out IMessage oznacza to, że typy zwracane powinny pochodzić z IMessage, a nie typów parametrów.

Więc trzeba będzie to w ten sposób:

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

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

Aby uczynić go skompilować i ważność. Ale teraz twoje list.Add(mop); nie będzie działać. I słusznie, ponieważ Mop1 nie jest istotnie rzutować na IConsumer<IMessage> bo gdyby był następujący kod byłoby możliwe:

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

i nie jest możliwe, ponieważ MOP1 akceptuje tylko Message1.

Aby odpowiedzieć na pytanie. Naprawdę nie można zrobić nic sensownego z wysyłaniem wiadomości i czystymi kompilatorami C#. Widzę, dokąd zmierza, chciałeś statycznego pisania z wiadomościami i podobnymi rzeczami. Niestety, nie możesz mieć listy konsumentów, którzy zużywają określone wiadomości na liście ogólnej, musisz mieć abstrakcyjne podpisy, takie jak bool CanProcess(IMessage); IMessage Process(IMessage); i wrzucać je do środka. Możesz także zrobić to nieco lepiej dzięki niestandardowym atrybutom i refleksji, ale znowu nie tylko z kompilatorem C#.

+0

Zakładając, że OP planuje wdrożyć swoje interfejsy (co muszę przyznać jest bardzo prawdopodobne), jest to o wiele bardziej przydatna odpowiedź na to pytanie. –

+0

@Asad tylko założyłem, że 'Konsument' zużywa, tzn. Przyjmuje. – Andrey

+0

Nasz hipotetyczny' IConsumer .Consume' może użyć parametru dynamicznego komunikatu i być interfejsem znacznika, dla wszystkich, których znasz. Tak jak powiedziałem, jest wiele rzeczy, które zakładasz na podstawie pustego interfejsu. –

Powiązane problemy