2011-06-17 13 views
9

Następne pytanie do a previous question, zostało to zidentyfikowane jako problem związany z koinwersją. Biorąc to jeden stopień dalej, jeśli zmodyfikować IFactory następująco:Dlaczego lista <T> jest nieważna na interfejsie kowariancyjnym MyInterface <out T>

class Program 
{ 
    static void Main(string[] args) 
    { 
     IFactory<IProduct> factory = new Factory(); 
    } 
} 

class Factory : IFactory<Product> 
{ 
} 

class Product : IProduct 
{ 
} 

interface IFactory<out T> where T : IProduct 
{ 
    List<T> MakeStuff(); 
} 

interface IProduct 
{ 
} 

uzyskać:

Nieprawidłowy wariancji: Rodzaj parametr T musi być niezmienniczo ważne w Sandbox.IFactory.MakeStuff(). T jest kowariantny.

Dlaczego to nie jest prawidłowe? Jak można/należy to rozwiązać?

Odpowiedz

7

@Craig's answer jest poprawna. Aby rozwiązać, zmień go na:

IEnumerable<T> MakeStuff() 

EDIT: Co do dlaczego, spojrzeć na definicję IEnumerable<T> Interface:

public interface IEnumerable<out T> : IEnumerable 

Należy pamiętać, że IList<T> Interface nie ma wyjścia słowa kluczowego. Wariancja jest obsługiwana dla ogólnych parametrów typów w interfejsach i delegatach, a nie klasach, więc nie ma zastosowania do Listy <T>.

+2

Należy zauważyć, że po napisaniu tej odpowiedzi została wydana nowa wersja .NET Framework, w której 'List ' implementuje nowy interfejs 'IReadOnlyList '. Jak wskazano, interfejs jest kowariantny, podobnie jak "IEnumerable ". –

6

Ponieważ można dodawać i usuwać obiekty z obiektu List<T>, T musi być zawsze niezmiennikiem, nawet jeśli lista jest wynikiem funkcji. Po wykonaniu var l = MakeStuff() możesz umieścić rzeczy na liście lub je wyjąć, więc T muszą być niezmienne.

+0

jak rozwiązać ten problem? – Firoso

+0

Użyj innego typu zwrotu dla 'MakeStuff'. –

11

Pozostałe odpowiedzi są poprawne, ale pouczające jest wyjaśnienie, dlaczego kompilator uznał to za niebezpieczne. Przypuśćmy, że na to pozwolimy; Co mogłoby pójść źle?

class Sprocket: Product {} 
class Gadget : Product {} 
class GadgetFactory : IFactory<Gadget> 
{ 
    public List<Gadget> MakeStuff() 
    { 
     return new List<Gadget>() { new Gadget(); } 
    } 
} 
... later ... 
IFactory<Gadget> gf = new GadgetFactory(); 
IFactory<Product> pf = gf; // Covariant! 
List<Product> pl = pf.MakeStuff(); // Actually a list of gadgets 
pl.Add(new Sprocket()); 

i hej, właśnie dodaliśmy kolec do listy, która może zawierać tylko gadżety.

Jest tylko jedno miejsce, w którym kompilator może wykryć problem, i to jest w deklaracji interfejsu.

Przepraszamy za nieco przesadnie żargonowy komunikat o błędzie. Nie mogłem wymyślić nic lepszego.

+0

Dziękuję za informację zwrotną, doceniam posiadanie więcej tła, wydaje mi się, że nie rozumiem co i kontrawariancji tak jak myślałem. Zawsze miło mieć więcej tła, teraz wypuść część 4 z serii niezmienności :-P – Firoso

Powiązane problemy