2012-01-26 9 views
10

Siccome non so come si chiama il mio problema, non posso garantire che nessuno abbia mai fatto la stessa domanda di recente o del tutto.Informazioni su Generics ed Ereditarietà (perdonami il mio brutto titolo)

Ho notato, tuttavia, che ci sono alcuni thread con un titolo simile, ma non sembrano essere rilevanti per il mio problema.

Ho una classe elenco personalizzata, che implementa Generics.

class MyList<T> 
{ 
    public void add(T item) // adds an item to the list 
    { /* code */ } 
    public void add(MyList<T> list) // attaches an existing list to the end of the current one 
    { /* code */ } 
} 

Ho anche le classi:

class Apple : Fruit 

e

class Banana : Fruit 

Ora, arriva il codice rilevante:

MyList<Fruit> fruitList = new MyList<Fruit>(); 
// fill fruitList 

fruitList.add(new Apple()); // works, of course 
fruitList.add(new Banana()); // works as well, of course 

MyList<Apple> appleList = new MyList<Apple>(); 
// fill appleList 

fruitList.add(appleList); // doesn't work. Why? 

Anche se appleList è una MyList (Of Apple) e Apple è Fruit, VisualStudio non accetta t MyList (Of Apple) come argomento, quando viene chiesto MyList (Of Fruit).

Tuttavia, se dovessi dichiarare l'elenco come questo:

MyList<object> fruitList = new MyList<object>(); 

Poi tutto funziona di nuovo. Cosa ho fatto di sbagliato?

Una risposta sarebbe molto apprezzata, e grazie per aver dedicato del tempo a leggere, anche senza rispondere.

+1

FYI, il le principali parole chiave su questo sono covarianza, controvarianza e invarianza. Sì, è piuttosto difficile da trovare quando non lo sai. – delnan

+2

Dovresti implementare 'IList '. – SLaks

+0

I nomi dei membri pubblici in .Net devono essere UpperCamelCase. – SLaks

risposta

8

Stai cercando di utilizzare covariance.
. Net supporta solo la varianza generica sulle interfacce, quindi non funzionerà.

Inoltre, la covarianza ha senso solo sui tipi immutabili.
Se fosse stato possibile convertire un MyList<Apple> in un MyList<Fruit>, sarà quindi possibile aggiungere un Orange all'elenco, violando il tipo di sicurezza.

Invece, è possibile effettuare il metodo generico:

public void Add<U>(IList<U> list) where U : T 
3

penso che il design dell'interfaccia IMyList dovrebbe essere:

public interface IMyList<T> : IEnumerable<T> 
{ 
    void Add(T item); 
    void AddRange(IEnumerable<T> itemList); 
} 

Tutto funziona come previsto. Perché? Solo perché in .NET 4.0 interfaccia IEnumerable è covariante in esso è di tipo parametro T, questo è ciò che appare la definizione del tipo:

public interface IEnumerable<out T> : IEnumerable 

semplice implementazione dell'interfaccia IMyList (Lista decorator):

public class MyList<T> : IMyList<T> 
{ 
    private readonly List<T> _list = new List<T>(); 

    #region Implementation of IMyList<in T> 

    public void Add(T item) 
    { 
     Console.WriteLine("Adding an item: {0}", item); 
     _list.Add(item); 
    } 

    public void AddRange(IEnumerable<T> itemList) 
    { 
     Console.WriteLine("Adding items!"); 
     _list.AddRange(itemList); 
    } 

    #endregion 

    #region Implementation of IEnumerable 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _list.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    #endregion 
} 
+0

+1 per suggerire un design migliore – phoog