Como você deve saber, arrays em C # implementam IList<T>
, entre outras interfaces. Porém, de alguma forma, eles fazem isso sem implementar publicamente a propriedade Count de IList<T>
! Matrizes têm apenas uma propriedade de comprimento.
Este é um exemplo flagrante de C # / .net quebrando suas próprias regras sobre a implementação da interface ou estou faltando alguma coisa?
Array
aula tinha que ser escrita em C #!Array
é uma classe "mágica" que não poderia ser implementada em C # ou em qualquer outro idioma de destino .net. Mas esse recurso específico está disponível em C #.Respostas:
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:Resultado:
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 deT[]
implemento fosse feitoIList<T>
com qualquer outra interface. Desta perspectiva, a interface é implementada com alguns dos membros sendo explicitamente implementados (comoCount
). 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 implementaIList<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:
Oferece uma exceção de:
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ão
IList<T>
seja covariante (e não pode ser seguro), a covariância de matriz permite que isso funcione:... o que torna olhar como
typeof(string[])
implementosIList<object>
, quando isso não acontece realmente.A especificação CLI (ECMA-335) partição 1, seção 8.7.1, tem o seguinte:
...
(Na verdade, não menciona
ICollection<W>
ouIEnumerable<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:
(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çãoICollection<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 suaCount
propriedade. Desta vez, podemos obter o mapeamento da interface e, de fato, a interface é implementada diretamente porSystem.Array
. A documentação para aICollection.Count
implementação da propriedadeArray
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:
fonte
Count
é bom - masAdd
sempre lançará, já que os arrays têm tamanho fixo.Bem, sim, erm não, realmente não. Esta é a declaração para a classe Array na estrutura .NET 4:
Ele implementa System.Collections.IList, não System.Collections.Generic.IList <>. Não pode, Array não é genérico. O mesmo vale para as interfaces genéricas IEnumerable <> e ICollection <>.
Mas o CLR cria tipos de array concretos dinamicamente, então ele poderia criar tecnicamente um que implemente essas interfaces. No entanto, este não é o caso. Experimente este código, por exemplo:
A chamada GetInterfaceMap () falha para um tipo de array concreto com "Interface não encontrada". No entanto, uma conversão para IEnumerable <> funciona sem problemas.
Esta é a digitação de quacks-like-a-duck. É o mesmo tipo de digitação que cria a ilusão de que todo tipo de valor deriva de ValueType que deriva de Object. Tanto o compilador quanto o CLR têm conhecimento especial dos tipos de array, assim como têm dos tipos de valor. O compilador vê sua tentativa de lançar para IList <> e diz "ok, eu sei como fazer isso!". E emite a instrução castclass IL. O CLR não tem problemas com isso, ele sabe como fornecer uma implementação de IList <> que funciona no objeto array subjacente. Ele possui conhecimento integrado da classe System.SZArrayHelper, de outra forma oculta, um wrapper que realmente implementa essas interfaces.
O que não acontece explicitamente como todos afirmam, a propriedade Count sobre a qual você perguntou se parece com isto:
Sim, certamente você pode chamar esse comentário de "quebrar as regras" :) De outra forma, é muito útil. E extremamente bem escondido, você pode verificar isso em SSCLI20, a distribuição de código-fonte compartilhado para o CLR. Procure por "IList" para ver onde ocorre a substituição de tipo. O melhor lugar para vê-lo em ação é o método clr / src / vm / array.cpp, GetActualImplementationForArrayGenericIListMethod ().
Este tipo de substituição no CLR é bastante brando em comparação com o que acontece na projeção da linguagem no CLR, que permite escrever código gerenciado para WinRT (também conhecido como Metro). Quase qualquer tipo de núcleo .NET é substituído lá. IList <> mapeia para IVector <> por exemplo, um tipo totalmente não gerenciado. Sendo uma substituição, o COM não oferece suporte a tipos genéricos.
Bem, essa foi uma olhada no que acontece por trás da cortina. Pode ser muito desconfortável, mares estranhos e desconhecidos com dragões que vivem no final do mapa. Pode ser muito útil tornar a Terra plana e modelar uma imagem diferente do que realmente está acontecendo no código gerenciado. Mapear para a resposta favorita de todos é confortável dessa forma. O que não funciona tão bem para tipos de valor (não modifique uma estrutura!), Mas este está muito bem escondido. A falha do método GetInterfaceMap () é o único vazamento na abstração que posso pensar.
fonte
Array
classe, que não é o tipo de um array. É o tipo base para um array. Uma matriz unidimensional em C # O implementarIList<T>
. E um tipo não genérico pode certamente implementar uma interface genérica de qualquer maneira ... o que funciona porque há muitos tipos diferentes -typeof(int[])
! = Typeof (string []), so
typeof (int []) `implementaIList<int>
etypeof(string[])
implementaIList<string>
.Array
(que, como você mostra, é uma classe abstrata, portanto, não pode ser o tipo real de um objeto de matriz) e a conclusão (que ele não implementaIList<T>
) são IMO incorretos. A maneira como ele implementaIList<T>
é incomum e interessante, eu concordo - mas isso é puramente um detalhe de implementação . Afirmar queT[]
não implementaIList<T>
é enganoso IMO. Isso vai contra as especificações e todos os comportamentos observados.IList<T>
porqueArray
não o fazem? Essa lógica é uma grande parte do que estou discordando. Além disso, acho que teríamos que concordar sobre uma definição do que significa para um tipo implementar uma interface: na minha opinião, os tipos de array exibem todos os recursos observáveis de tipos que implementamIList<T>
, excetoGetInterfaceMapping
. Novamente, como isso é alcançado é menos importante para mim, assim como não vejo problema em dizer queSystem.String
é imutável, embora os detalhes de implementação sejam diferentes.IList<T>
para funcionar.IList<T>.Count
é implementado explicitamente :Isso é feito para que, quando você tiver uma variável de array simples, não tenha ambos
Count
eLength
diretamente disponíveis.Em geral, a implementação de interface explícita é usada quando você deseja garantir que um tipo possa ser usado de uma maneira particular, sem forçar todos os consumidores do tipo a pensar sobre isso dessa maneira.
Edit : Opa, lembrança ruim aí.
ICollection.Count
é implementado explicitamente. O genéricoIList<T>
é tratado como Hans descreve abaixo .fonte
string
).ICollection
declaraCount
, e ficaria ainda mais confuso se um tipo com a palavra "coleção" não usasseCount
:). Sempre há compensações ao se tomar essas decisões.IList<T>
, apesar de as especificações de linguagem e CLI parecerem o contrário. Ouso dizer que a maneira como a implementação da interface funciona nos bastidores pode ser complicada, mas é o caso em muitas situações. Você também votaria negativamente em alguém dizendo que issoSystem.String
é imutável, só porque o funcionamento interno é mutável? Para todos os efeitos práticos - e certamente no que diz respeito à linguagem C # - é implícito explícito.Implementação de interface explícita . Em suma, você declara como
void IControl.Paint() { }
ouint IList<T>.Count { get { return 0; } }
.fonte
Não é diferente de uma implementação de interface explícita de IList. Só porque você implementa a interface não significa que seus membros precisam aparecer como membros da classe. Ele faz implementar a propriedade Count, ele simplesmente não expô-lo em X [].
fonte
Com fontes de referência disponíveis:
Especificamente esta parte:
(Ênfase minha)
Fonte (role para cima).
fonte