Quais são as diferenças na implementação de interfaces implícita e explicitamente em C #?
Quando você deve usar implícito e quando você deve usar explícito?
Existem prós e / ou contras em um ou outro?
As diretrizes oficiais da Microsoft (da primeira edição do Framework Design Guidelines ) afirmam que o uso de implementações explícitas não é recomendado , pois fornece um comportamento inesperado ao código.
Eu acho que essa diretriz é muito válida em um período pré-IoC , quando você não passa as coisas como interfaces.
Alguém poderia tocar nesse aspecto também?
Respostas:
Implícito é quando você define sua interface através de um membro da sua classe. Explícito é quando você define métodos na sua classe na interface. Eu sei que parece confuso, mas aqui está o que eu quero dizer:
IList.CopyTo
seria implicitamente implementado como:e explicitamente como:
A diferença é que a implementação implícita permite acessar a interface através da classe que você criou, lançando a interface como essa classe e como a própria interface. A implementação explícita permite acessar a interface apenas convertendo-a como a própria interface.
Eu uso explícito principalmente para manter a implementação limpa ou quando preciso de duas implementações. Independentemente disso, eu raramente uso.
Estou certo de que existem mais razões para usar / não usar explícito que outras pessoas postarão.
Veja o próximo post neste tópico para um excelente raciocínio por trás de cada um.
fonte
public
palavra - chave ... caso contrário, você receberá um erroUISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }
.IEnumerator<T>.Current
,IEnumerable<T>.GetEnumerator()
eISet<T>.Add(T)
. Isso é mencionado em outra resposta .A definição implícita seria apenas adicionar os métodos / propriedades, etc. exigidos pela interface diretamente à classe como métodos públicos.
A definição explícita força os membros a serem expostos apenas quando você está trabalhando com a interface diretamente, e não a implementação subjacente. Isso é preferido na maioria dos casos.
fonte
Cat
é de fatoIEatable
sem objeções.Além das excelentes respostas já fornecidas, há alguns casos em que a implementação explícita é NECESSÁRIA para que o compilador possa descobrir o que é necessário. Dê uma olhada
IEnumerable<T>
como um excelente exemplo que provavelmente surgirá com bastante frequência.Aqui está um exemplo:
Aqui,
IEnumerable<string>
implementaIEnumerable
, portanto precisamos também. Mas espere, a versão genérica e a normal implementam funções com a mesma assinatura de método (o C # ignora o tipo de retorno para isso). Isso é completamente legal e legal. Como o compilador resolve qual usar? Obriga você a ter apenas, no máximo, uma definição implícita, e pode resolver o que for necessário.ie
PS: O pequeno pedaço de indireção na definição explícita para IEnumerable funciona porque dentro da função o compilador sabe que o tipo real da variável é um StringList, e é assim que resolve a chamada da função. Um pequeno fato bacana para implementar algumas das camadas de abstração que algumas das interfaces principais do .NET parecem ter acumulado.
fonte
abstract
.Para citar Jeffrey Richter de CLR via C #
( EIMI significa E xplicit eu nterface M é todo eu MPLEMENTAÇÃO)
Se você usar uma referência de interface, QUALQUER cadeia virtual pode ser explicitamente substituída pelo EIMI em qualquer classe derivada e quando um objeto desse tipo é convertido para a interface, sua cadeia virtual é ignorada e a implementação explícita é chamada. Isso é tudo menos polimorfismo.
Os EIMIs também podem ser usados para ocultar membros de interface não fortemente tipados das implementações básicas do Framework Interfaces, como IEnumerable <T>, para que sua classe não exponha diretamente um método não fortemente tipado, mas é sintática correta.
fonte
Razão # 1
Costumo usar a implementação explícita da interface quando quero desencorajar a "programação para uma implementação" ( Princípios de Design dos Padrões de Design ).
Por exemplo, em um aplicativo Web baseado em MVP :
Agora, outra classe (como um apresentador ) tem menos probabilidade de depender da implementação do StandardNavigator e mais provável da interface do INavigator (já que a implementação precisaria ser convertida em uma interface para fazer uso do método Redirecionar).
Razão # 2
Outro motivo para eu seguir uma implementação explícita da interface seria manter a interface "padrão" de uma classe mais limpa. Por exemplo, se eu estivesse desenvolvendo um controle de servidor ASP.NET , poderia querer duas interfaces:
Um exemplo simples segue. É um controle de caixa de combinação que lista clientes. Neste exemplo, o desenvolvedor da página da web não está interessado em preencher a lista; em vez disso, eles apenas desejam selecionar um cliente por GUID ou obter o GUID do cliente selecionado. Um apresentador preencheria a caixa no carregamento da primeira página e esse apresentador é encapsulado pelo controle.
O apresentador preenche a fonte de dados e o desenvolvedor da página da web nunca precisa estar ciente de sua existência.
Mas não é uma bala de canhão de prata
Eu não recomendaria sempre o emprego de implementações explícitas de interface. Esses são apenas dois exemplos em que podem ser úteis.
fonte
Além dos outros motivos já mencionados, esta é a situação em que uma classe está implementando duas interfaces diferentes que possuem uma propriedade / método com o mesmo nome e assinatura.
Esse código compila e executa OK, mas a propriedade Title é compartilhada.
Claramente, gostaríamos que o valor de Title retornasse dependesse de estarmos tratando a Class1 como um livro ou uma pessoa. É quando podemos usar a interface explícita.
Observe que as definições explícitas da interface são inferidas como Públicas - e, portanto, você não pode declara-las públicas (ou não) explicitamente.
Observe também que você ainda pode ter uma versão "compartilhada" (como mostrado acima), mas, embora isso seja possível, a existência dessa propriedade é questionável. Talvez possa ser usado como uma implementação padrão do Title - para que o código existente não precise ser modificado para converter Class1 no IBook ou IPerson.
Se você não definir o Título "compartilhado" (implícito), os consumidores da Classe1 deverão converter explicitamente as instâncias da Classe1 para o IBook ou IPerson primeiro - caso contrário, o código não será compilado.
fonte
Eu uso a implementação explícita da interface na maioria das vezes. Aqui estão os principais motivos.
A refatoração é mais segura
Ao alterar uma interface, é melhor que o compilador possa verificá-la. Isso é mais difícil com implementações implícitas.
Dois casos comuns vêm à mente:
Adicionando uma função a uma interface, onde uma classe existente que implementa essa interface já possui um método com a mesma assinatura que a nova . Isso pode levar a um comportamento inesperado e me incomodou várias vezes. É difícil "ver" durante a depuração, porque essa função provavelmente não está localizada com os outros métodos de interface do arquivo (o problema de auto-documentação mencionado abaixo).
Removendo uma função de uma interface . Os métodos implementados implicitamente serão código morto subitamente, mas os métodos implementados explicitamente serão capturados por erro de compilação. Mesmo que seja bom manter o código morto, quero ser forçado a revisá-lo e promovê-lo.
É lamentável que o C # não tenha uma palavra-chave que nos obriga a marcar um método como uma implementação implícita, para que o compilador possa fazer verificações extras. Os métodos virtuais não apresentam nenhum dos problemas acima, devido ao uso obrigatório de 'substituir' e 'novo'.
Nota: para interfaces fixas ou que mudam raramente (normalmente das APIs do fornecedor), isso não é um problema. Para minhas próprias interfaces, porém, não posso prever quando / como elas mudarão.
É auto-documentado
Se eu vir 'public bool Execute ()' em uma classe, será necessário um trabalho extra para descobrir que faz parte de uma interface. Alguém provavelmente terá que comentar isso ou colocá-lo em um grupo de outras implementações de interface, tudo em uma região ou comentário de agrupamento dizendo "implementação do ITask". Obviamente, isso só funciona se o cabeçalho do grupo não estiver fora da tela.
Visto que: 'bool ITask.Execute ()' é claro e inequívoco.
Separação clara da implementação da interface
Penso nas interfaces como sendo mais "públicas" que os métodos públicos, porque são criadas para expor apenas um pouco da área superficial do tipo concreto. Eles reduzem o tipo a uma capacidade, um comportamento, um conjunto de características, etc. E na implementação, acho útil manter essa separação.
Enquanto olho o código de uma classe, quando encontro implementações explícitas de interface, meu cérebro muda para o modo "contrato de código". Freqüentemente, essas implementações simplesmente encaminham para outros métodos, mas às vezes realizam verificação extra de estado / parâmetro, conversão de parâmetros recebidos para melhor atender aos requisitos internos ou mesmo tradução para fins de versão (ou seja, várias gerações de interfaces, todas implementadas em implementações comuns).
(Eu percebo que os públicos também são contratos de código, mas as interfaces são muito mais fortes, especialmente em uma base de código orientada por interface em que o uso direto de tipos concretos geralmente é um sinal de código somente interno.)
Relacionado: Razão 2 acima por Jon .
E assim por diante
Além das vantagens já mencionadas em outras respostas aqui:
Problemas
Nem tudo é diversão e felicidade. Existem alguns casos em que fico com implícitos:
Além disso, pode ser difícil fazer a transmissão quando, na verdade, você tem o tipo concreto e deseja chamar um método de interface explícito. Eu lido com isso de duas maneiras:
public IMyInterface I { get { return this; } }
(que deve ser incorporado) e liguefoo.I.InterfaceMethod()
. Se várias interfaces que precisam dessa capacidade, expanda o nome além de I (na minha experiência, é raro que eu tenha essa necessidade).fonte
Se você implementar explicitamente, você poderá referenciar apenas os membros da interface através de uma referência que seja do tipo da interface. Uma referência que é o tipo da classe de implementação não expõe esses membros da interface.
Se sua classe de implementação não for pública, exceto pelo método usado para criar a classe (que pode ser um contêiner de fábrica ou IoC ) e exceto pelos métodos de interface (é claro), não vejo vantagem em implementar explicitamente interfaces.
Caso contrário, a implementação explícita de interfaces garante que as referências à sua classe de implementação concreta não sejam usadas, permitindo que você altere essa implementação posteriormente. "Garante que", suponho, é a "vantagem". Uma implementação bem fatorada pode fazer isso sem implementação explícita.
A desvantagem, na minha opinião, é que você encontrará tipos de conversão de / para a interface no código de implementação que tem acesso a membros não públicos.
Como muitas coisas, a vantagem é a desvantagem (e vice-versa). A implementação explícita de interfaces garantirá que seu código de implementação de classe concreto não seja exposto.
fonte
Uma implementação implícita da interface é onde você tem um método com a mesma assinatura da interface.
Uma implementação explícita da interface é onde você declara explicitamente a qual interface o método pertence.
MSDN: implementações implícitas e explícitas da interface
fonte
Todo membro da classe que implementa uma interface exporta uma declaração que é semanticamente semelhante à maneira como as declarações da interface do VB.NET são gravadas, por exemplo,
Embora o nome do membro da classe geralmente corresponda ao nome do membro da interface, e o membro da classe geralmente seja público, nada disso é necessário. Pode-se também declarar:
Nesse caso, a classe e seus derivados teriam permissão para acessar um membro da classe usando o nome
IFoo_Foo
, mas o mundo externo só seria capaz de acessar esse membro em particular, transmitindo paraIFoo
. Essa abordagem geralmente é boa nos casos em que um método de interface terá um comportamento especificado em todas as implementações, mas um comportamento útil em apenas algumas [por exemplo, o comportamento especificado para oIList<T>.Add
método de uma coleção somente leitura é o lançamentoNotSupportedException
]. Infelizmente, a única maneira correta de implementar a interface em C # é:Não é tão legal.
fonte
Um uso importante da implementação explícita da interface é quando é necessário implementar interfaces com visibilidade mista .
O problema e a solução estão bem explicados no artigo Interface interna do C # .
Por exemplo, se você deseja proteger o vazamento de objetos entre as camadas do aplicativo, essa técnica permite especificar uma visibilidade diferente dos membros que podem causar o vazamento.
fonte
As respostas anteriores explicam por que a implementação explícita de uma interface em C # pode ser preferível (por razões principalmente formais). No entanto, há uma situação em que a implementação explícita é obrigatória : Para evitar vazamento do encapsulamento quando a interface não é
public
, mas a classe de implementação épublic
.O vazamento acima é inevitável porque, de acordo com a especificação do C # , "Todos os membros da interface têm acesso público implicitamente". Como conseqüência, implementações implícitas também devem fornecer
public
acesso, mesmo que a própria interface seja por exemplointernal
.A implementação implícita da interface em C # é uma grande conveniência. Na prática, muitos programadores o usam o tempo todo / em qualquer lugar sem consideração adicional. Isso leva a superfícies de tipo confuso, na melhor das hipóteses, e a encapsulamento vazado, na pior das hipóteses. Outros idiomas, como o F #, nem mesmo permitem isso .
fonte