2013-09-05 11 views
5

Dati i seguenti LINQPad esempio:Perché IEnumerable non ha bisogno di un cast ma di una 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;} 
} 

Perché può EnumerableMethod prendere direttamente la lista mentre ListMethod ha bisogno di un cast in primo luogo?

Capisco che un cast stia accadendo da DbItem a IDbItem, ma cosa c'è di diverso nell'IEnumerable che gli permette di fare il cast senza una richiesta esplicita?

Immagino che la risposta implichi la parola Covariance, ma non ne so abbastanza per capirlo da solo.

risposta

4

Check out this post about covariance. Dà una spiegazione e un modo per far funzionare il codice senza il cast. Le liste non sono covarianti, ma è possibile solo cambiare il metodo in questo modo:

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

covarianza è la possibilità di assegnare un tipo generico di più derivato da un tipo generico di base. Il motivo per cui non è List covariante è perché si potrebbe fare questo:

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

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

dogs è davvero una lista di cani, non di animali. Quindi aggiungere un Cat sarebbe un problema, causando in particolare un errore del compilatore.

+0

ottengo che un cane è in realtà una lista di cani e non animali. Ma allora perché funziona per IEnumerable? – Vaccano

+0

'IEnumerable' non ha un metodo' Add', quindi non c'è paura di aggiungere un 'Cat' a' dogs'. La covarianza è qualcosa che deve essere esplicitamente specificato, ei ragazzi che hanno fatto 'Elenco' hanno deciso che non dovrebbe essere covariante per la ragione sopra. I ragazzi che hanno fatto "IEnumerable" hanno deciso che dovrebbe essere covariante. – ShadowCat7

0

Poiché non è possibile eseguire il cast di una raccolta di x in una raccolta di y, anche se x deriva da y e quindi ogni x è una y. Vedi this SO question.

Questo è il motivo per cui i concetti di covarianza e controvarianza sono stati aggiunti alla lingua.

Sfortunatamente List<T> non supporta queste funzionalità.

+0

.NET 4.0/C# 4.0 si applica solo alle interfacce generiche e alle dichiarazioni di delegati generici; non si applica ai tipi concreti (quindi non è una funzionalità di supporto di List, ma di tutti i tipi concreti). –

3

Immagino che la risposta coinvolge la parola covarianza

Sì hai ragione, il parametro tipo per IEnumerable<T> è covariante. in modo da poter utilizzare sia il tipo specificato o qualsiasi tipo che è più derivato

public interface IEnumerable<out T> : IEnumerable 

Dove come List<T> parametro di tipo non è covariante

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

La parola chiave 'out' rende IEnumerable covariant? Se è così, perché? Se no, allora cosa fa? – Vaccano

+0

Sì, l'uscita rende il parametro di tipo covariante, perché? questa è una domanda per i progettisti di linguaggio C# –

Problemi correlati