2014-08-29 7 views
5

La ragione per cui questo codice funziona è dovuto al fatto che il Enumerator non possono modificare la raccolta:Perché la covarianza non è consentita con ReadOnlyCollection?

var roList = new List<string>() { "One", "Two", "Three" }; 
IEnumerable<object> objEnum = roList; 

Se proviamo a fare la stessa cosa con un List<object>, invece di IEnumerable<object>, ci sarebbe un eloquente errore di compilazione noi che non possiamo convertire implicitamente il tipo List<string> in List<object>. Bene, questo ha senso ed è stata una buona decisione di Microsoft, a mio parere, come una lezione appresa dagli array.

Tuttavia, quello che non riesco a capire è perché nel mondo è questa regola hardline applicabile a qualcosa come ReadOnlyCollection? Non saremo in grado di modificare gli elementi nello ReadOnlyCollection, quindi qual è il problema di sicurezza che ha causato l'impossibilità Microsoft di utilizzare la covarianza con qualcosa di solo lettura? C'è un modo possibile di modificare quel tipo di raccolta che Microsoft stava cercando di rendere conto, ad esempio attraverso i puntatori?

+1

"una lezione appresa dagli array": per quanto riguarda la covarianza dell'array, questa era in realtà una decisione di progettazione consapevole per consentirlo, perché Java ce l'aveva. Si è rivelata una pessima idea ... http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array- covariance.aspx –

+0

@ThomasLevesque Buona lettura, grazie. –

risposta

7

Varianza e controvarianza funzionano solo su interfacce e delegati, non su classi.

Tuttavia, è possibile farlo (usando .NET 4.5):

ReadOnlyCollection<string> roList = ... 
IReadOnlyCollection<object> objects = roList; 

(perché IReadOnlyCollection<T> è covariante)


per rispondere alla tua domanda sul perché la varianza non è consentito su classi , ecco la spiegazione di Jon Skeet dal suo libro C# in Depth (seconda edizione, §13.3.5, pagina 394).

NO varianza per parametri di tipo delle classi

Solo le interfacce e delegati possono avere parametri di tipo variante. Anche se si dispone di una classe che utilizza solo il parametro type per l'input (o lo utilizza solo per l'output), non è possibile specificare i modificatori in o . Ad esempio Comparer<T>, l'implementazione comune di IComparer<T>, è invariata - non c'è conversione da Comparer<IShape> a Comparer<Circle>.

A prescindere da eventuali difficoltà di implementazione che questo potrebbe avere sostenute, direi che concettualmente ha un certo senso. Le interfacce rappresentano un modo di guardare un oggetto da una particolare prospettiva , mentre le classi sono più radicate nel tipo effettivo dell'oggetto. Questo argomento è un po 'indebolito dall'ereditarietà che consente a di trattare un oggetto come un'istanza di una delle classi nella sua gerarchia di ereditarietà , ammettiamolo. In ogni caso, il CLR non consente lo .

+0

Hai ragione.L'ho testato e funziona con le interfacce di sola lettura 'IReadOnlyCollection ' e 'IReadOnlyList '. Qualche ragione particolare per cui non funziona con le classi? –

+0

@ B.K., Non lo so, ma suppongo che sia correlato con alcuni dettagli di implementazione interni CLR ... –

+0

È probabilmente più semplice implementare il supporto per le interfacce. Quando si esegue il cast da un tipo concreto a un tipo concreto di base, ora il tipo base deve decidere se chiamare i suoi metodi o i tipi di base che dipendono da scenari di virtual/override/hiding che sono definiti solo nell'ereditarietà reale. A ReadOnlyCollection non eredita da ReadOnlycollection quindi è difficile definire cosa succede. Le interfacce passano sempre le chiamate al tipo concreto e non vi è alcuna ambiguità sul fatto che venga chiamato base o derivato. È molto più chiaro cosa significa accedere a un'interfaccia covariante. IMO – AaronLS