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?
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?
Respostas:
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>
e IComparable
geralmente é melhor esconder a IComparable
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, como IConvertible
, 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:
void SomeMethod<T>(T obj) where T:IConvertible
Não encaixotará um int quando você passar um para ele.
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 sistema string
, 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 utilizados
digitaçã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 permite Clone()
que seja fortemente digitada quando você a chama diretamente como MyObject
membro da instância:
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
interface ICloneable<out T> { T Clone(); T self {get;} }
. Observe que não há deliberadamente nenhuma ICloneable<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 um protected
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 seu Clone
método publicamente visível retorna seu próprio tipo.
Da mesma forma, um IAutomobileFactory
pode ter um Manufacture
método que retorna um Automobile
, mas a FordExplorerFactory
, que implementa IAutomobileFactory
, pode ter seu Manufacture
método retornando a FordExplorer
(que deriva de Automobile
). O código que sabe que ele FordExplorerFactory
pode usar FordExplorer
propriedades específicas em um objeto retornado por a FordExplorerFactory
sem ter que digitar, enquanto o código que apenas sabia que ele tinha algum tipo de IAutomobileFactory
ação simplesmente lidaria com seu retorno como um Automobile
.
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):
interface Cat
{
string Name {get;}
}
interface Dog
{
string Name{get;}
}
public class Animal : Cat, Dog
{
string Cat.Name
{
get
{
return "Cat";
}
}
string Dog.Name
{
get
{
return "Dog";
}
}
}
static void Main(string[] args)
{
Animal animal = new Animal();
Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
Dog dog = animal;
Console.WriteLine(cat.Name); //Prints Cat
Console.WriteLine(dog.Name); //Prints Dog
}
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 implementar IDisposable
explicitamente e fornecer um método público Close()
que faça mais sentido para o consumidor do que Dispose(
).
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.
Dispose
são aquelas que nunca exigirão limpeza); um exemplo melhor seria algo como a implementação de uma coleção imutável de IList<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.
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.
#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>
implementos IList<T>
e, portanto, ICollection<T>
( a fim de permitir ImmutableList<T>
a ser usada mais facilmente com código legado), mas void ICollection<T>.Add(T item)
não faz sentido para um ImmutableList<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 de IImmutableList<T>
quem IImmutableList<T> Add(T item)
pode ser usado para listas imutáveis.
Assim, no caso de Add
, as implementações ImmutableList<T>
acabam tendo a seguinte aparência:
public ImmutableList<T> Add(T item)
{
// Create a new list with the added item
}
IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);
void ICollection<T>.Add(T item) => throw new NotSupportedException();
int IList.Add(object value) => throw new NotSupportedException();
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:
interface Iphone{
void Money();
}
interface Ipen{
void Price();
}
class Demo : Iphone, Ipen{
void Iphone.Money(){ //it is private you can't give public
Console.WriteLine("You have no money");
}
void Ipen.Price(){ //it is private you can't give public
Console.WriteLine("You have to paid 3$");
}
}
// So you have to cast to call the method
class Program
{
static void Main(string[] args)
{
Demo d = new Demo();
Iphone i1 = (Iphone)d;
i1.Money();
((Ipen)i1).Price();
Console.ReadKey();
}
}
// You can't call methods by direct class object
É 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.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oops3
{
interface I5
{
void getdata();
}
interface I6
{
void getdata();
}
class MyClass:I5,I6
{
void I5.getdata()
{
Console.WriteLine("I5 getdata called");
}
void I6.getdata()
{
Console.WriteLine("I6 getdata called");
}
static void Main(string[] args)
{
MyClass obj = new MyClass();
((I5)obj).getdata();
Console.ReadLine();
}
}
}