2011-02-11 41 views
37

Qual è la differenza tra IEnumerable e IEnumerable<T>?Differenza tra IEnumerable e IEnumerable <T>?

Ho visto molte classi di framework che implementano entrambe queste interfacce, quindi mi piacerebbe sapere quali vantaggi si ottengono implementando entrambi?

prega di dare un'occhiata come sono stati definiti:

public interface IEnumerable 
{ 
    [DispId(-4)] 
    IEnumerator GetEnumerator(); 
} 
public interface IEnumerable<T> : IEnumerable 
{ 
    IEnumerator<T> GetEnumerator(); 
} 

Come si vede, IEnumerable<T> deriva da IEnumerable, questo significa che tutto ciò che ha IEnumerable, IEnumerable<T> eredita, allora perché attuiamo sia invece di IEnumerable<T> ? L'implementazione di IEnumerable<T> non è sufficiente?

Analogamente, non vi sono altre coppie simili:

  • IList e IList<T>
  • ICollection e ICollection<T>

Vorrei sapere su questi.

risposta

48

Fondamentalmente le interfacce non generiche sono arrivate per prime, in .NET 1.0 e 1.1. Poi, quando è uscito .NET 2.0, sono usciti gli equivalenti generici. La vita sarebbe stata molto più semplice se i farmaci generici avevano fatto in .NET 1.0 :)

In termini di attuazione di "solo" IEnumerable<T> invece di due - che, fondamentalmente, hanno per implementare entrambi, e si deve usare l'interfaccia esplicita anche l'implementazione, dato che entrambi definiscono un metodo senza parametro GetEnumerator. Come IEnumerator<T> estende IEnumerator troppo, è di solito qualcosa di simile:

public IEnumerator<T> GetEnumerator() 
{ 
    // Return real iterator 
} 

// Explicit implementation of nongeneric interface 
IEnumerator IEnumerable.GetEnumerator() 
{ 
    // Delegate to the generic implementation 
    return GetEnumerator(); 
} 

D'altra parte, con i blocchi iteratore introdotte in C# 2 (con yield return ecc) che raramente necessario implementare queste cose interamente a mano, per fortuna. Potrebbe essere necessario scrivere qualcosa come sopra, e quindi utilizzare yield return nel metodo GetEnumerator.

Nota che IList<T> fa non estendono IList, e ICollection<T> fa non estendono ICollection. Questo perché è meno sicuro del tipo farlo ... mentre qualsiasi iteratore generico può essere visto come un iteratore non generico a causa della conversione (potenzialmente in box) di qualsiasi valore in object, IList e ICollection consentire valori da aggiunto al collezione; e non ha senso aggiungere (ad esempio) una stringa a IList<int>.

MODIFICA: Il motivo per cui abbiamo bisogno di IEnumerable<T> è che possiamo eseguire iterazioni in modo sicuro e propagare tali informazioni. Se restituisco un IEnumerable<string> a te, sai che puoi tranquillamente supporre che tutto quello che viene restituito sarà un riferimento di stringa o null. Con IEnumerable, abbiamo dovuto eseguire il cast efficace (spesso implicitamente in un'istruzione foreach) ogni elemento restituito dalla sequenza, poiché la proprietà Current di IEnumerator è solo di tipo object.Per quanto riguarda il motivo per cui abbiamo ancora necessario IEnumerable - perché le vecchie interfacce non vanno mai via, in pratica. C'è troppo codice esistente che lo usa.

Sarebbe stato possibile per IEnumerable<T> non estendere IEnumerable, ma poi qualsiasi codice che vogliono fare uso di un IEnumerable<T> non poteva mettere in un metodo accettare IEnumerable - e c'erano un sacco di metodi come quello da .NET 1.1 e 1.0.

+12

+1 La vita sarebbe stata molto più semplice se i generici l'avessero convertito in .NET 1.0 ... – Oded

+0

@Jon: la domanda è: perché abbiamo anche bisogno di implementare 'IEnumerable' quando implementiamo già' IEnumerable ' , o vice versa? – Nawaz

+0

@Nawaz: Ho modificato la mia risposta. Quanto sei a tuo agio con l'idea del perché i farmaci generici sono una buona cosa? –

5

Uno restituisce un oggetto di tipo Object, gli altri restituisce un oggetto di tipo T.

2

Come per questo che si vede classi definire sia, anche se abbastanza IEnumerable < T> implementa IEnumerable, la sua non è necessario, ma è è bello in una autodocumentazione per elencare le interfacce secondarie a volte. Considerare

interface IA { } 
interface IB : IA { } 

class A : IB {} // legal, IB implements IA 

class B : IA, IB {} // also legal, possible more clear on intent 
1

Durante l'iterazione del ciclo, IEnumerator mantiene lo stato. Memorizza la posizione del cursore e IEnumerable no.

Problemi correlati