2015-08-05 8 views
6

Ho letto un libro "CLR via C# Quarta edizione". E non riesco a capire una dichiarazione:Interfacce automatiche implementate negli array

Così, per esempio, se avete la seguente riga di codice:

FileStream[] fsArray; 

poi, quando il CLR crea il tipo FileStream[], sarà causare questo tipo di implementa automaticamente le interfacce IEnumerable<FileStream>, ICollection<FileStream> e IList<FileStream>. Inoltre, il tipo FileStream[] inoltre implementare le interfacce per i tipi di base: IEnumerable<Stream>, IEnumerable<Object>, ICollection<Stream>, ICollection<Object>, IList<Stream> e IList<Object>.

Ho testato questa affermazione con questo codice:

FileStream[] fsArray = new FileStream[0]; 

string s = null; 
foreach (var m in fsArray.GetType().GetInterfaces()) 
    s += m.ToString() + Environment.NewLine; 

e, di conseguenza, ho questo:

System.ICloneable 
System.Collections.IList 
System.Collections.ICollection 
System.Collections.IEnumerable 
System.Collections.IStructuralComparable 
System.Collections.IStructuralEquatable 
System.Collections.Generic.IList`1[System.IO.FileStream] 
System.Collections.Generic.ICollection`1[System.IO.FileStream] 
System.Collections.Generic.IEnumerable`1[System.IO.FileStream] 
System.Collections.Generic.IReadOnlyList`1[System.IO.FileStream] 
System.Collections.Generic.IReadOnlyCollection`1[System.IO.FileStream] 

Non v'è alcuna implementazione di IEnumerable<Stream> e gli altri! Ho fatto un errore da qualche parte? Oppure Jeffrey Richter ha sbagliato?

Inoltre, penso che sia privo di significato. Perché gli array supportano la co-varianza.

+0

Si ha 'System.Collections.Generic.IList'1 [System.IO.FileStream]' –

+0

@NikhilAgrawal 'IList ' è nel testo del libro; il testo del libro prosegue affermando che "le interfacce per i tipi di base ..."IEnumerable " dovrebbe anche essere presente, che a quanto pare non sono – AakashM

+0

Gli odori non corretti. Invia errata, whydoncha https://www.microsoftpressstore.com/contact-us/errata – Will

risposta

3

Non c'è implementazione di IEnumerable e di altri!

No. Eppure, IList<Stream> streamList = fsArray; funzionerà. Ed è possibile utilizzare streamList come previsto, anche se con eccezioni di runtime se si tenta di eseguire qualcosa non valido su un array (purché l'array sia a base zero e abbia una dimensione singola - "SZ array" in Microsoft parlance- altrimenti non è permesso).

Vuoi vedere qualcosa di peggio?

var listMap = typeof(List<FileStream>).GetInterfaceMap(typeof(IList<FileStream>)); // This works fine. 
var arrMap = typeof(typeof(FileStream[]).GetInterfaceMap(typeof(IList<FileStream>)); // This throws `ArgumentException` 

Quindi, a questo proposito, FileStream[] non ha nemmeno implementare IList<FileStream>; se lo fa allora sicuramente la linea sopra dovrebbe funzionare.

Otteniamo un indizio interessante da. NET 4.0. Prima di questo, il ArgumentException avrebbe un messaggio di "Interface not found", lo stesso come se avessimo cercato di ottenere quell'interfaccia su int o string[]. Ora è "Interface maps for generic interfaces on arrays cannot be retrived." [sic]

E ci dà anche questo se proviamo a ottenere la mappa di interfaccia per IList<Stream> ma non per un'interfaccia completamente non supportata come IList<bool>.

Qui sta succedendo qualcosa di insolito.

E quello che è, è che FileStream[] non supporta affatto alcuna interfaccia generica, nello stesso modo in cui lo farebbe un class o struct.

Invece esiste una classe stub denominata SZArrayHelper che fornisce queste interfacce in fase di esecuzione per matrici a dimensione zero basate su zero. Il commento sul .NET Core Version è informativo:

//---------------------------------------------------------------------------------------- 
// ! READ THIS BEFORE YOU WORK ON THIS CLASS. 
// 
// The methods on this class must be written VERY carefully to avoid introducing security holes. 
// That's because they are invoked with special "this"! The "this" object 
// for all of these methods are not SZArrayHelper objects. Rather, they are of type U[] 
// where U[] is castable to T[]. No actual SZArrayHelper object is ever instantiated. Thus, you will 
// see a lot of expressions that cast "this" "T[]". 
// 
// This class is needed to allow an SZ array of type T[] to expose IList<T>, 
// IList<T.BaseType>, etc., etc. all the way up to IList<Object>. When the following call is 
// made: 
// 
// ((IList<T>) (new U[n])).SomeIListMethod() 
// 
// the interface stub dispatcher treats this as a special case, loads up SZArrayHelper, 
// finds the corresponding generic method (matched simply by method name), instantiates 
// it for type <T> and executes it. 
// 
// The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be 
// array that is castable to "T[]" (i.e. for primitivs and valuetypes, it will be exactly 
// "T[]" - for orefs, it may be a "U[]" where U derives from T.) 
//---------------------------------------------------------------------------------------- 

E questo è ciò che accade. Se provi a trasmettere fsArray a IList<Stream>, ottieni questa classe che effettua le chiamate per te. Se chiami GetInterfaces() ottieni un codice stub simile fornendo solo quelli relativi al tipo di array. In entrambi i casi fsArrayfa implementare tutte le interfacce citate nel libro che citate, ma non lo fa allo stesso modo di un can. class o struct.

(consideri per analogia come un int può essere sia quattro byte del valore di 32 bit e un oggetto "pieno" con le implementazioni di interfaccia, metodo override, etc.)

Così il libro è corretto, ma non ti manca nulla, perché alcune delle cose che ci aspettiamo che accadano quando un tipo implementa un'interfaccia non lo fanno.

Inoltre, penso che sia senza significato. Perché gli array supportano la co-varianza.

La co-varianza di supporto non significa che implementeranno una determinata interfaccia, o viceversa. Soprattutto perché la covarianza degli array (probabilmente interrotta) è molto diversa da quella delle interfacce e la precede, e infatti gli array implementano interfacce generiche anche prima della covarianza dell'interfaccia.

Tuttavia, che è stato deciso che FileStream[] dovrebbe anzi implementare Stream[] non si riferiscono agli array di essere covariante (la decisione sarebbe stato altrettanto stranamente sbagliato in altro modo), ma ha bisogno l'aiuto supplementare che SZArrayHelper fornisce, invece di essere implicato automaticamente esso.

1

Perché gli array supportano la co-varianza.

Si è perchè array sono covariante che devono anche implementare le interfacce generiche classi base dell'elemento. In altre parole, tutti si aspettano questo lavoro:

var a = new FileStream[] { new FileStream("a", FileMode.Create) }; 
    Stream[] b = a;     // Fine, covariant 
    var iterb = (IList<Stream>)b; // Fine of course, actually iterates FileStreams 

L'assegnazione al flusso [] riferimento oggetto non modifica in alcun modo l'oggetto, è ancora un FileStream [] sotto il cofano. Quindi, il requisito fondamentale è che FileStream [] implementa anche IList<Stream>. E IList<Object>. E IEnumerable<Stream>, ecc.

Quindi, ciò che si scopre in realtà è che Reflection non emula perfettamente la covarianza dell'array. Per il quale può essere perdonato. Gli array non in realtà implementano queste interfacce, il CLR sa solo come fornire un oggetto sostitutivo con il comportamento desiderato. Digitazione di ciarlatani. Ulteriori informazioni su questo comportamento in this Q+A.