Então, qual é exatamente um bom caso de uso para implementar uma interface explicitamente?
É apenas para que as pessoas que usam a classe não precisem examinar todos esses métodos / propriedades no intellisense?
Se você implementar duas interfaces, ambas com o mesmo método e implementações diferentes, precisará implementá-lo explicitamente.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
É útil ocultar o membro não preferido. Por exemplo, se você implementar os dois
IComparable<T>
eIComparable
geralmente é melhor esconder aIComparable
sobrecarga para não dar às pessoas a impressão de que você pode comparar objetos de tipos diferentes. Da mesma forma, algumas interfaces não são compatíveis com CLS, comoIConvertible
, portanto, se você não implementar explicitamente a interface, os usuários finais de idiomas que exigem conformidade com CLS não poderão usar seu objeto. (O que seria muito desastroso se os implementadores da BCL não ocultassem os membros IConvertible das primitivas :))Outra observação interessante é que normalmente o uso dessa construção significa que a estrutura que implementa explicitamente uma interface só pode invocá-la encaixotando no tipo de interface. Você pode contornar isso usando restrições genéricas:
Não encaixotará um int quando você passar um para ele.
fonte
string
existia antes dos genéricos e essa prática estava em voga. Quando o .net 2 apareceu, eles não queriam quebrar a interface pública do sistemastring
, deixando-o como estava com a salvaguarda em vigor.Alguns motivos adicionais para implementar uma interface explicitamente:
compatibilidade com versões anteriores : caso a
ICloneable
interface seja alterada, os membros da classe de método de implementação não precisam alterar suas assinaturas de método.código mais limpo : haverá um erro do compilador se o
Clone
método for removido do ICloneable, no entanto, se você implementar o método implicitamente, poderá acabar com métodos públicos 'órfãos' não utilizadosdigitação forte : para ilustrar a história de supercat com um exemplo, esse seria o meu código de exemplo preferido, a implementação
ICloneable
explicitamente permiteClone()
que seja fortemente digitada quando você a chama diretamente comoMyObject
membro da instância:fonte
interface ICloneable<out T> { T Clone(); T self {get;} }
. Observe que não há deliberadamente nenhumaICloneable<T>
restrição em T. Embora um objeto geralmente possa ser clonado com segurança apenas se sua base puder ser, pode-se derivar de uma classe base que pode ser clonada com segurança em um objeto de uma classe que não pode. Para permitir isso, recomendo que as classes herdáveis exponham um método de clone público. Em vez disso, tenha classes herdáveis com umprotected
método de clonagem e classes seladas que derivam delas e expõem a clonagem pública.Outra técnica útil é fazer com que a implementação pública de um método de uma função retorne um valor mais específico do que o especificado em uma interface.
Por exemplo, um objeto pode ser implementado
ICloneable
, mas ainda assim seuClone
método publicamente visível retorna seu próprio tipo.Da mesma forma, um
IAutomobileFactory
pode ter umManufacture
método que retorna umAutomobile
, mas aFordExplorerFactory
, que implementaIAutomobileFactory
, pode ter seuManufacture
método retornando aFordExplorer
(que deriva deAutomobile
). O código que sabe que eleFordExplorerFactory
pode usarFordExplorer
propriedades específicas em um objeto retornado por aFordExplorerFactory
sem ter que digitar, enquanto o código que apenas sabia que ele tinha algum tipo deIAutomobileFactory
ação simplesmente lidaria com seu retorno como umAutomobile
.fonte
Também é útil quando você tem duas interfaces com o mesmo nome e assinatura de membro, mas deseja alterar o comportamento dele, dependendo de como é usado. (Eu não recomendo escrever código como este):
fonte
public class Animal : Cat, Dog
Ele pode manter a interface pública mais limpa para implementar explicitamente uma interface, ou seja, sua
File
classe pode implementarIDisposable
explicitamente e fornecer um método públicoClose()
que faça mais sentido para o consumidor do queDispose(
).O F # oferece apenas implementação explícita da interface, portanto você sempre deve transmitir para a interface específica para acessar sua funcionalidade, o que contribui para um uso muito explícito (sem trocadilhos) da interface.
fonte
Dispose
são aquelas que nunca exigirão limpeza); um exemplo melhor seria algo como a implementação de uma coleção imutável deIList<T>.Add
.Se você possui uma interface interna e não deseja implementar publicamente os membros de sua classe, você os implementaria explicitamente. Implementações implícitas devem ser públicas.
fonte
Outro motivo para implementação explícita é a manutenção .
Quando uma classe fica "ocupada" - sim, acontece, nem todos temos o luxo de refatorar o código de outros membros da equipe - então ter uma implementação explícita deixa claro que existe um método para satisfazer um contrato de interface.
Por isso, melhora a "legibilidade" do código.
fonte
#region
é necessário, com uma sequência de títulos apropriada. E um comentário sobre o método.Um exemplo diferente é dado por
System.Collections.Immutable
, no qual os autores optaram por usar a técnica para preservar uma API familiar para os tipos de coleção, raspando as partes da interface que não têm significado para seus novos tipos.Concretamente,
ImmutableList<T>
implementosIList<T>
e, portanto,ICollection<T>
( a fim de permitirImmutableList<T>
a ser usada mais facilmente com código legado), masvoid ICollection<T>.Add(T item)
não faz sentido para umImmutableList<T>
: desde a adição de um elemento de uma lista imutável não deve alterar a lista existente,ImmutableList<T>
decorre também deIImmutableList<T>
quemIImmutableList<T> Add(T item)
pode ser usado para listas imutáveis.Assim, no caso de
Add
, as implementaçõesImmutableList<T>
acabam tendo a seguinte aparência:fonte
No caso de interfaces explicitamente definidas, todos os métodos são automaticamente privados, você não pode dar acesso ao modificador de acesso público. Suponha:
fonte
É assim que podemos criar uma interface explícita: se tivermos 2 interfaces e ambas tiverem o mesmo método e uma única classe herdar essas 2 interfaces, portanto, quando chamamos um método de interface, o compilador ficou confuso com o método a ser chamado, para que possamos gerencie esse problema usando a interface explícita. Aqui está um exemplo que eu dei abaixo.
fonte