Nouvelle réponse à la lumière de la réponse de Hans
Grâce à la réponse donnée par Hans, nous pouvons voir que la mise en œuvre est un peu plus compliquée qu'on ne le pense. Le compilateur et le CLR s'efforcent tous deux de donner l'impression qu'un type de tableau implémente IList<T>
- mais la variance de tableau rend cela plus délicat. Contrairement à la réponse de Hans, les types de tableaux (unidimensionnels, basés sur zéro de toute façon) implémentent directement les collections génériques, car le type d'un tableau spécifique ne l'est pas System.Array
- c'est juste le type de base du tableau. Si vous demandez à un type de tableau quelles interfaces il prend en charge, il inclut les types génériques:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Production:
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]
Pour les tableaux unidimensionnels à base zéro, en ce qui concerne le langage , le tableau est IList<T>
également implémenté . La section 12.1.2 de la spécification C # le dit. Donc, quelle que soit l'implémentation sous-jacente, le langage doit se comporter comme si le type d' T[]
implémentation était le même IList<T>
que pour toute autre interface. De ce point de vue, l'interface est implémentée avec certains des membres explicitement implémentés (comme Count
). C'est la meilleure explication au niveau linguistique de ce qui se passe.
Notez que cela ne vaut que pour les tableaux unidimensionnels (et les tableaux à base zéro, pas que C # en tant que langage ne dise rien sur les tableaux à base non nulle). T[,]
ne met pas en œuvre IList<T>
.
Du point de vue de CLR, quelque chose de plus funk se passe. Vous ne pouvez pas obtenir le mappage d'interface pour les types d'interface génériques. Par exemple:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Donne une exception de:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
Alors pourquoi cette bizarrerie? Eh bien, je pense que c'est vraiment dû à la covariance du tableau, qui est une verrue dans le système de types, IMO. Même si elle IList<T>
n'est pas covariante (et ne peut pas l'être en toute sécurité), la covariance de tableau permet à cela de fonctionner:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... ce qui le fait ressembler à des typeof(string[])
outils IList<object>
, alors que ce n'est pas vraiment le cas.
La partition 1 de la spécification CLI (ECMA-335), section 8.7.1, a ceci:
Un type de signature T est compatible avec un type de signature U si et seulement si au moins l'un des éléments suivants est valide
...
T est un tableau de rang 1 de base zéro V[]
, et U
est IList<W>
, et V est compatible avec les éléments de tableau avec W.
(Il ne mentionne pas réellement ICollection<W>
ou IEnumerable<W>
qui, je crois, est un bogue dans la spécification.)
Pour la non-variance, la spécification CLI est directement associée à la spécification de langue. De la section 8.9.1 de la partition 1:
De plus, un vecteur créé avec le type d'élément T, implémente l'interface System.Collections.Generic.IList<U>
, où U: = T. (§8.7)
(Un vecteur est un tableau unidimensionnel avec une base nulle.)
Maintenant, en termes de détails d'implémentation , il est clair que le CLR fait un mappage génial pour conserver la compatibilité des affectations ici: quand string[]
on demande l'implémentation de ICollection<object>.Count
, il ne peut pas gérer cela de manière tout à fait normale. Cela compte-t-il comme une implémentation d'interface explicite? Je pense qu'il est raisonnable de le traiter de cette façon, car à moins que vous ne demandiez directement le mappage d'interface, il se comporte toujours de cette façon du point de vue du langage.
Et quoi ICollection.Count
?
Jusqu'à présent, j'ai parlé des interfaces génériques, mais il y a ensuite le non générique ICollection
avec sa Count
propriété. Cette fois, nous pouvons obtenir le mappage de l'interface, et en fait, l'interface est implémentée directement par System.Array
. La documentation de l' ICollection.Count
implémentation de la propriété Array
indique qu'elle est implémentée avec l'implémentation d'interface explicite.
Si quelqu'un peut penser à une manière dont ce type d'implémentation d'interface explicite est différent de l'implémentation d'interface explicite "normale", je serais heureux de l'examiner plus en détail.
Ancienne réponse concernant l'implémentation d'interface explicite
Malgré ce qui précède, qui est plus compliqué en raison de la connaissance des tableaux, vous pouvez toujours faire quelque chose avec les mêmes effets visibles grâce à une implémentation d'interface explicite .
Voici un exemple simple et autonome:
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 devait être écrite en C #!