AGGIORNAMENTO: questa domanda era la base di my blog entry for Monday April 4th 2011. Grazie per l'ottima domanda.
Lasciarlo scomporre in molte domande più piccole.
Il List<T>
implementa davvero tutte quelle interfacce?
Sì.
Perché?
Poiché quando un'interfaccia (ad esempio, IList<T>
) eredita da un'interfaccia (diciamo IEnumerable<T>
) allora implementatori dell'interfaccia più derivato devono attuare anche l'interfaccia meno derivata. Questo è ciò che significa l'ereditarietà dell'interfaccia; se si adempie il contratto del tipo più derivato, allora si richiede anche di adempiere il contratto del tipo meno derivato.
Quindi una classe è necessaria per implementare tutti i metodi di tutte le interfacce nella chiusura transitiva delle sue interfacce di base?
Esattamente.
È una classe che implementa un'interfaccia più derivata anche per indicare nella sua lista di tipi di base che sta implementando tutte quelle interfacce meno derivate?
No.
è la classe necessaria per NON affermarlo?
No.
Quindi è opzionale se le interfacce implementate meno-derivati sono indicati nella lista di tipo di base?
Sì.
Sempre?
Quasi sempre:
interface I1 {}
interface I2 : I1 {}
interface I3 : I2 {}
E 'facoltativo se I3 afferma che eredita da I1.
class B : I3 {}
Implementers di I3 sono tenuti ad attuare I2 e I1, ma non sono tenuti a dichiarare esplicitamente che lo stanno facendo. È facoltativo
class D : B {}
classi derivate non sono tenuti a ri-stato che implementano l'interfaccia dalla loro classe di base, ma sono autorizzati a farlo. (Questo caso è speciale, vedi sotto per maggiori dettagli.)
class C<T> where T : I3
{
public virtual void M<U>() where U : I3 {}
}
tipo argomenti corrispondente a T e U sono necessari per implementare I2 e I1, ma è facoltativo per i vincoli T o U per affermare che.
È sempre facoltativo di ri-stato qualsiasi interfaccia di base in una classe parziale:
partial class E : I3 {}
partial class E {}
La seconda metà di E è consentito affermare che implementa I3 o I2 o I1, ma non necessario per così.
OK, ho capito; è facoltativo. Perché qualcuno dovrebbe indicare inutilmente un'interfaccia di base?
Forse perché credono che farlo rende il codice più facile da capire e più autodocumentante.
O, forse lo sviluppatore ha scritto il codice come
interface I1 {}
interface I2 {}
interface I3 : I1, I2 {}
e si rese conto, oh, aspetta un minuto, I2 dovrebbe ereditare da I1.Perché la modifica dovrebbe richiedere allo sviluppatore di tornare indietro e modificare la dichiarazione di I3 a non con menzione esplicita di I1? Non vedo alcun motivo per costringere gli sviluppatori a rimuovere le informazioni ridondanti.
Oltre ad essere più facile da leggere e capire, c'è qualche tecnica differenza tra affermando un'interfaccia in modo esplicito nella lista tipo di base e lasciando non dichiarato ma implicito?
Di solito no, ma in un caso può esserci una sottile differenza. Supponiamo di avere una classe derivata D la cui classe base B ha implementato alcune interfacce. D implementa automaticamente tali interfacce tramite B. Se si re-dichiarano le interfacce nella lista delle classi di base di D, il compilatore C# eseguirà una nuova implementazione dell'interfaccia . I dettagli sono un po 'sottili; se sei interessato a come funziona allora ti consiglio una lettura attenta della sezione 13.4.6 della specifica C# 4.
Il codice sorgente List<T>
indica effettivamente tutte quelle interfacce?
No. Il codice sorgente dice
public class List<T> : IList<T>, System.Collections.IList
Perché MSDN avere la lista completa interfaccia ma il codice sorgente reale non?
Perché MSDN è documentazione; dovrebbe fornire tutte le informazioni che desideri. È molto più chiaro che la documentazione sia completa in un unico posto piuttosto che farti cercare tra dieci diverse pagine per scoprire quale sia l'intero set di interfacce.
Perché Reflector mostra l'intera lista?
Riflettore ha solo metadati da cui lavorare. Poiché l'inserimento nell'elenco completo è facoltativo, Reflector non ha idea se il codice sorgente originale contenga o meno l'elenco completo. È meglio sbagliare dalla parte di maggiori informazioni. Di nuovo, Reflector sta tentando di aiutarti mostrandoti più informazioni piuttosto che nascondendo le informazioni di cui potresti aver bisogno.
BONUS Domanda: Perché IEnumerable<T>
ereditano da IEnumerable
ma IList<T>
non ereditare da IList
?
Una sequenza di numeri interi può essere trattata come una sequenza di oggetti, eseguendo il pugilato di ogni numero intero come esce dalla sequenza. Ma un elenco di interi in lettura e scrittura non può essere considerato come un elenco di oggetti in lettura-scrittura, perché è possibile inserire una stringa in un elenco di oggetti in lettura e scrittura. Un IList<T>
non è tenuto a soddisfare l'intero contratto di IList
, quindi non eredita da esso.
Sì Lista implementa tutte quelle interfacce, perché - perché sono tutte rilevanti per una raccolta di elenchi. A cosa serve il tuo codice? È una domanda diversa? –
Il tuo codice di esempio non ha nulla a che fare con la Lista 'classe' ... –
BoltClock
Penso che sta chiedendo il motivo per cui 'Lista pubblica di classe: IList , ICollection , IEnumerable , etc.' piuttosto che' Lista public class : IList '. Si noti che 'ICollection' e' IList' (quelli non generici) non sono ereditati da 'IList '. –
Rup