Nueva respuesta a la luz de la respuesta de Hans
Gracias a la respuesta de Hans, podemos ver que la implementación es algo más complicada de lo que pensamos. Tanto el compilador como el CLR se esfuerzan mucho en dar la impresión de que se implementa un tipo de matriz IList<T>
, pero la variación de la matriz hace que esto sea más complicado. Contrariamente a la respuesta de Hans, los tipos de matriz (unidimensional, de base cero de todos modos) implementan las colecciones genéricas directamente, porque el tipo de cualquier matriz específica no lo es System.Array
, es solo el tipo base de la matriz. Si le pregunta a un tipo de matriz qué interfaces admite, incluye los tipos genéricos:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Salida:
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]
Para matrices unidimensionales basadas en cero, en lo que respecta al lenguaje , la matriz realmente IList<T>
también se implementa . La sección 12.1.2 de la especificación de C # lo dice. Entonces, independientemente de lo que haga la implementación subyacente, el lenguaje tiene que comportarse como si fuera el tipo de T[]
implementos IList<T>
como con cualquier otra interfaz. Desde esta perspectiva, la interfaz se implementa con algunos de los miembros implementados explícitamente (como Count
). Esa es la mejor explicación a nivel de idioma de lo que está sucediendo.
Tenga en cuenta que esto solo es válido para matrices unidimensionales (y matrices de base cero, no que C # como lenguaje diga algo sobre las matrices de base no cero). T[,]
no implementa IList<T>
.
Desde la perspectiva de CLR, está sucediendo algo más divertido. No puede obtener la asignación de interfaz para los tipos de interfaz genéricos. Por ejemplo:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Da una excepción de:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
Entonces, ¿por qué la rareza? Bueno, creo que realmente se debe a la covarianza de la matriz, que es una verruga en el sistema de tipos, en mi opinión. Aunque noIList<T>
es covariante (y no puede ser seguro), la covarianza de matriz permite que esto funcione:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... que lo hace parecer como typeof(string[])
implementos IList<object>
, cuando no es así en realidad.
La partición 1 de la especificación CLI (ECMA-335), sección 8.7.1, tiene esto:
Una firma tipo T es compatible con una firma tipo U si y solo si se cumple al menos uno de los siguientes
...
T es una matriz de rango 1 de base cero V[]
, y U
es IList<W>
, y V es compatible con elementos de matriz con W.
(En realidad, no menciona ICollection<W>
o IEnumerable<W>
creo que es un error en la especificación).
Para la no variación, la especificación CLI va directamente junto con la especificación del idioma. De la sección 8.9.1 de la partición 1:
Además, un vector creado con el tipo de elemento T, implementa la interfaz System.Collections.Generic.IList<U>
, donde U: = T. (§8.7)
(Un vector es una matriz unidimensional con base cero).
Ahora bien, en cuanto a los detalles de implementación , claramente el CLR está haciendo un mapeo cobarde para mantener la compatibilidad de asignaciones aquí: cuando una string[]
pregunta se hace por la ejecución de ICollection<object>.Count
, que no puede manejar que, en todo el camino normal. ¿Esto cuenta como implementación de interfaz explícita? Creo que es razonable tratarlo de esa manera, ya que a menos que solicite el mapeo de la interfaz directamente, siempre se comportará de esa manera desde la perspectiva del lenguaje.
¿Qué hay de ICollection.Count
?
Hasta ahora he hablado de las interfaces genéricas, pero luego están las no genéricas ICollection
con su Count
propiedad. Esta vez podemos obtener la asignación de interfaz, y de hecho la interfaz se implementa directamente System.Array
. La documentación para la ICollection.Count
implementación de la propiedad Array
indica que se implementa con una implementación de interfaz explícita.
Si alguien puede pensar en una forma en la que este tipo de implementación de interfaz explícita es diferente de la implementación de interfaz explícita "normal", estaría feliz de investigarlo más a fondo.
Respuesta anterior sobre la implementación de interfaz explícita
A pesar de lo anterior, que es más complicado debido al conocimiento de las matrices, aún puede hacer algo con los mismos efectos visibles a través de la implementación explícita de la interfaz .
Aquí hay un ejemplo sencillo e independiente:
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
clase tenía que estar escrita en C #!