Nuova risposta alla luce della risposta di Hans
Grazie alla risposta data da Hans, possiamo vedere che l'implementazione è un po 'più complicata di quanto potremmo pensare. Sia il compilatore e il CLR prova molto duro per dare l'impressione che un tipo di matrice attrezzi IList<T>
- ma varianza gamma rende questo più complicato. Contrariamente alla risposta di Hans, i tipi di matrice (unidimensionale, in ogni caso a base zero) implementano direttamente le raccolte generiche, perché il tipo di un array specifico non lo è System.Array
, è solo il tipo di base dell'array. Se chiedi a un tipo di array quali interfacce supporta, include i tipi generici:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Produzione:
System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]
Per array monodimensionali a base zero, per quanto riguarda il linguaggio , anche l'array implementa IList<T>
davvero. La sezione 12.1.2 della specifica C # lo dice. Quindi, qualunque sia l'implementazione sottostante, il linguaggio deve comportarsi come se il tipo di T[]
implementa IList<T>
come con qualsiasi altra interfaccia. Da questo punto di vista, l'interfaccia è implementata con alcuni dei membri che vengono implementati esplicitamente (come Count
). Questa è la migliore spiegazione a livello di lingua per quello che sta succedendo.
Si noti che questo vale solo per gli array unidimensionali (e gli array a base zero, non che il linguaggio C # dica qualcosa sugli array non a base zero). T[,]
non implementa IList<T>
.
Dal punto di vista di CLR, sta succedendo qualcosa di più funky. Non è possibile ottenere la mappatura dell'interfaccia per i tipi di interfaccia generici. Per esempio:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Dà un'eccezione di:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
Allora perché la stranezza? Bene, credo che sia davvero dovuto alla covarianza degli array, che è una verruca nel sistema dei tipi, IMO. Anche se nonIList<T>
è covariante (e non può essere sicuro), la covarianza dell'array consente a questo di funzionare:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... il che lo fa sembrare un typeof(string[])
attrezzo IList<object>
, quando in realtà non lo è.
La partizione 1 delle specifiche CLI (ECMA-335), sezione 8.7.1, ha questo:
Un tipo di firma T è compatibile con un tipo di firma U se e solo se almeno una delle seguenti condizioni è valida
...
T è un array di rango 1 a base zero V[]
, ed U
è IList<W>
, e V è compatibile con l'elemento dell'array W.
(In realtà non menziona ICollection<W>
o IEnumerable<W>
che credo sia un bug nelle specifiche.)
Per non varianza, la specifica CLI va di pari passo con la specifica della lingua. Dalla sezione 8.9.1 della partizione 1:
Inoltre, un vettore creato con il tipo di elemento T, implementa l'interfaccia System.Collections.Generic.IList<U>
, dove U: = T. (§8.7)
(Un vettore è un array unidimensionale con una base zero.)
Ora, in termini di dettagli di implementazione , chiaramente il CLR sta facendo un po 'di mappatura funky per mantenere la compatibilità dell'assegnazione qui: quando a string[]
viene richiesta l'implementazione di ICollection<object>.Count
, non può gestirlo in modo abbastanza normale. Questo conta come implementazione esplicita dell'interfaccia? Penso che sia ragionevole trattarlo in questo modo, poiché a meno che tu non chieda direttamente la mappatura dell'interfaccia, si comporta sempre in quel modo dal punto di vista del linguaggio.
Di cosa ICollection.Count
?
Finora ho parlato delle interfacce generiche, ma poi c'è il non generico ICollection
con la sua Count
proprietà. Questa volta siamo in grado di ottenere la mappatura di interfaccia, e in effetti l'interfaccia è implementata direttamente System.Array
. La documentazione per l' ICollection.Count
implementazione della proprietà Array
afferma che è implementata con l'implementazione dell'interfaccia esplicita.
Se qualcuno può pensare a un modo in cui questo tipo di implementazione esplicita dell'interfaccia è diverso dalla "normale" implementazione esplicita dell'interfaccia, sarei felice di esaminarlo ulteriormente.
Vecchia risposta sull'implementazione dell'interfaccia esplicita
Nonostante quanto sopra, che è più complicato a causa della conoscenza degli array, puoi ancora fare qualcosa con gli stessi effetti visibili attraverso l' implementazione dell'interfaccia esplicita .
Ecco un semplice esempio autonomo:
public interface IFoo
{
void M1();
void M2();
}
public class Foo : IFoo
{
// Explicit interface implementation
void IFoo.M1() {}
// Implicit interface implementation
public void M2() {}
}
class Test
{
static void Main()
{
Foo foo = new Foo();
foo.M1(); // Compile-time failure
foo.M2(); // Fine
IFoo ifoo = foo;
ifoo.M1(); // Fine
ifoo.M2(); // Fine
}
}
Array
classe doveva essere scritta in C #!