2013-09-05 12 views
5

Biorąc pod uwagę następujący przykład LINQPad:Dlaczego IEnumerable nie potrzebuje rzutu, ale robi to lista?

void Main() 
{ 
    List<DbItem> dbItems = new List<DbItem> 
    { 
     new DbItem{Id = 4, SomeValue = "Value For 4", OtherValue = 1}, 
     new DbItem{Id = 19, SomeValue = "Value For 19", OtherValue = 2} 
    }; 

    ListMethod(dbItems.Cast<IDbItem>().ToList()); <-+ 
    EnumerableMethod(dbItems);      <-+ 
}              | 
                 | 
// These are the methods are I asking about -------+ 

public void ListMethod(List<IDbItem> dbItems) 
{ 
    Console.WriteLine(dbItems); 
} 
public void EnumerableMethod(IEnumerable<IDbItem> dbItems) 
{ 
    Console.WriteLine(dbItems); 
} 
public class DbItem : IDbItem 
{ 
    public int Id {get; set;} 
    public string SomeValue {get; set;} 
    public int OtherValue {get; set;} 
} 
public interface IDbItem 
{ 
    int Id {get; set;} 
    string SomeValue {get; set;} 
} 

Dlaczego EnumerableMethod podjęcia listę bezpośrednio podczas ListMethod musi najpierw obsady?

Dostaję, że obsada dzieje się od DbItem do IDbItem, ale co różni się od IEnumerable, która pozwala mu obsadzić bez wyraźnego żądania?

Wyobrażam sobie, że odpowiedź zawiera słowo Kowariancja, ale nie wiem wystarczająco dużo o tym, aby samemu to zrozumieć.

Odpowiedz

4

Zapoznaj się z this post about covariance. Daje wyjaśnienie i sposób, aby kod zadziałał bez obsady. Listy nie są kowariantna, ale można po prostu zmienić metodę tak:

public void ListMethod<T>(List<T> dbItems) where T : IDbItem 
{ 
    Console.WriteLine(dbItems); 
} 

kowariancji jest możliwość przypisania bardziej ogólny typ pochodzący z podstawą typu rodzajowego. Powodem List nie kowariantna dlatego można to zrobić:

class Animal {} 
class Dog : Animal {} 
class Cat : Animal {} 

void someFunction() 
{ 
    List<Animal> dogs = new List<Dog>(); 
    dogs.Add(new Cat()); 
} 

dogs jest naprawdę lista psów, a nie zwierząt. Dlatego dodanie do niego numeru Cat stanowiłoby problem, szczególnie powodując błąd kompilatora.

+0

dostaję, że pies jest naprawdę lista psów i nie Zwierząt. Ale dlaczego to działa dla IEnumerable? – Vaccano

+0

'IEnumerable' nie ma metody' Add', więc nie ma obaw przed dodaniem 'Cat' do' dogs'. Kowariancja jest czymś, co musi być wyraźnie określone, a ci, którzy stworzyli "List" zdecydowali, że nie powinna ona być kowariantna z powyższego powodu. Faceci, którzy stworzyli "IEnountable" zdecydowali, że powinno to być kowariantne. – ShadowCat7

0

Ponieważ nie można rzucić kolekcji x do kolekcji y, nawet jeśli x pochodzi od y, a zatem każde x jest y. Zobacz this SO question.

Dlatego do języka dodano pojęcia kowariancji i kontrawariancji.

Niestety List<T> nie obsługuje tych funkcji.

+0

.NET 4.0/C# 4.0 stosuje się tylko do ogólnych interfejsów i ogólnych delegowanych deklaracji; nie ma zastosowania do konkretnych typów (więc nie jest to funkcja wsparcia listy, ale wszystkich konkretnych typów). –

3

sobie wyobrazić odpowiedź wiąże się słowo kowariancji

Tak masz rację, parametr typu dla IEnumerable<T> jest kowariantna. więc można użyć typu ty określony lub dowolny typ, który jest bardziej pochodzący

public interface IEnumerable<out T> : IEnumerable 

Gdzie jak List<T> parametr typu nie jest kowariantna

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable 
+0

Czy słowo kluczowe "out" powoduje, że IEnumerable kowariancja? Jeśli tak, dlaczego? Jeśli nie, to co? – Vaccano

+0

Tak, wyjście sprawia, że ​​parametr kowariancji parametru, dlaczego? to jest pytanie dla projektantów języka C# –

Powiązane problemy