Nova resposta à luz da resposta de Hans
Graças à resposta dada por Hans, podemos ver que a implementação é um pouco mais complicada do que podemos imaginar. Tanto o compilador quanto o CLR tentam muito dar a impressão de que um tipo de array implementa IList<T>
- mas a variação de array torna isso mais complicado. Ao contrário da resposta de Hans, os tipos de array (unidimensional, baseado em zero de qualquer maneira) implementam as coleções genéricas diretamente, porque o tipo de qualquer array específico não é System.Array
- esse é apenas o tipo base do array. Se você perguntar a um tipo de array quais interfaces ele suporta, ele incluirá os tipos genéricos:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Resultado:
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 matrizes unidimensionais baseadas em zero, no que diz respeito à linguagem , a matriz IList<T>
também implementa . A seção 12.1.2 da especificação C # diz isso. Portanto, seja o que for que a implementação subjacente faça, a linguagem deve se comportar como se o tipo de T[]
implemento fosse feito IList<T>
com qualquer outra interface. Desta perspectiva, a interface é implementada com alguns dos membros sendo explicitamente implementados (como Count
). Essa é a melhor explicação em nível de linguagem para o que está acontecendo.
Observe que isso só é válido para arrays unidimensionais (e arrays baseados em zero, não que o C # como linguagem diga algo sobre arrays não baseados em zero). T[,]
não implementa IList<T>
.
De uma perspectiva CLR, algo mais funk está acontecendo. Você não pode obter o mapeamento de interface para os tipos de interface genéricos. Por exemplo:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Oferece uma exceção de:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
Então, por que a esquisitice? Bem, eu acredito que é realmente devido à covariância de array, que é uma verruga no sistema de tipos, IMO. Mesmo que nãoIList<T>
seja covariante (e não pode ser seguro), a covariância de matriz permite que isso funcione:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... o que torna olhar como typeof(string[])
implementos IList<object>
, quando isso não acontece realmente.
A especificação CLI (ECMA-335) partição 1, seção 8.7.1, tem o seguinte:
Um tipo de assinatura T é compatível com um tipo de assinatura U se e somente se pelo menos um dos seguintes for válido
...
T é uma matriz de classificação 1 baseada em zero V[]
e U
é IList<W>
, e V é compatível com elemento de matriz com W.
(Na verdade, não menciona ICollection<W>
ou IEnumerable<W>
acredito que seja um bug na especificação.)
Para não variação, a especificação CLI acompanha a especificação do idioma diretamente. Da seção 8.9.1 da partição 1:
Além disso, um vetor criado com o tipo de elemento T, implementa a interface System.Collections.Generic.IList<U>
, onde U: = T. (§8.7)
(Um vetor é uma matriz unidimensional com base zero.)
Agora em termos dos detalhes de implementação , claramente o CLR está fazendo algum mapeamento descolados para manter a compatibilidade atribuição aqui: quando um string[]
é convidado para a implementação ICollection<object>.Count
, não pode lidar com isso em muito a maneira normal. Isso conta como implementação de interface explícita? Acho que é razoável tratá-lo dessa forma, já que, a menos que você peça o mapeamento da interface diretamente, ele sempre se comportará dessa forma do ponto de vista da linguagem.
Sobre o quê ICollection.Count
?
Até agora, falei sobre as interfaces genéricas, mas há as não genéricas ICollection
com sua Count
propriedade. Desta vez, podemos obter o mapeamento da interface e, de fato, a interface é implementada diretamente por System.Array
. A documentação para a ICollection.Count
implementação da propriedade Array
indica que é implementada com a implementação explícita da interface.
Se alguém conseguir pensar em uma maneira em que esse tipo de implementação de interface explícita seja diferente da implementação de interface explícita "normal", ficaria feliz em investigar mais a fundo.
Resposta antiga sobre implementação de interface explícita
Apesar do acima, que é mais complicado por causa do conhecimento de matrizes, você ainda pode fazer algo com os mesmos efeitos visíveis por meio da implementação de interface explícita .
Aqui está um exemplo autônomo simples:
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
aula tinha que ser escrita em C #!