1. Introdução
Normalmente, quando você implementa uma interface, só cria métodos públicos na sua classe, com as mesmas assinaturas declaradas na interface. Isso é chamado de implementação implícita ou implementação pública. O compilador é esperto o suficiente pra sacar: "Ah, esse método DoSomething() na classe MyClass é pra implementar IDoable.DoSomething()!".
Mas e se:
- Sua classe SmartDevice implementa a interface ICamera (que tem o método TakePicture()) e a interface IScreen (que também tem o método TakePicture() — pra fazer screenshot)?
- Ou sua classe Robot já tem um método público Reset() pra resetar tudo, mas você quer que ela também implemente a interface IDevice com o método Reset(), que deveria resetar só parte das configs?
Nesses casos rola ambiguidade ou você quer separar bem as funções. É aí que entra a implementação explícita de interface.
Implementação explícita de interface deixa você dizer pro compilador: "Esse método aqui é pra implementar essa interface, e só vai estar disponível através dessa interface". Tipo o Peter Parker, que só solta teia quando tá atuando como Homem-Aranha, e usa as skills de fotógrafo quando tá de "Peter normal".
2. Quando e por que usar implementação explícita de interface
Quando você implementa uma interface do jeito normal, os métodos e propriedades dela viram parte da interface pública da sua classe. Mas às vezes você quer que eles só estejam disponíveis via interface, não direto pela classe. Isso acontece mais do que parece! Por exemplo, se duas interfaces exigem o mesmo método (mas com sentido diferente), ou você quer limitar o acesso à implementação só pra quem usa o objeto via interface.
É aí que a implementação explícita salva o rolê. É tipo uma passagem secreta na arquitetura do código: por fora ninguém vê, mas quem sabe, passa!
Cenário 1: Conflito de nomes
Imagina que você tem uma classe que implementa duas interfaces, e as duas exigem um método com o mesmo nome, mas lógica totalmente diferente. Tipo:
interface IWriter
{
void Print();
}
interface IPrinter
{
void Print();
}
Você quer que IWriter.Print() grave texto num arquivo, e IPrinter.Print() mande pra impressora. Se fizer do jeito normal, não dá pra separar o comportamento. É aí que a implementação explícita resolve.
Cenário 2: Esconder métodos técnicos/desnecessários da interface
Às vezes sua classe tem que implementar um método da interface, mas você não quer oferecer ele pra todo mundo que usa a classe (tipo, o membro da interface só serve pra infra interna).
Cenário 3: Proteger contra chamada "sem querer"
Se o método da interface não é pra ser chamado direto (tipo, mecanismo interno do framework), pode implementar explicitamente — aí ninguém chama ele sem querer pelo objeto da classe.
3. Sintaxe da implementação explícita de interface
A diferença principal: na implementação explícita, você nomeia métodos e propriedades com o nome completo da interface. E nada de modificador de acesso (public/private) ou palavra-chave override!
Sintaxe geral:
TipoDeRetorno NomeDaInterface.NomeDoMetodo(parametros)
{
// implementação
}
Parece que você tá dizendo claramente o nome da interface, pra ninguém (nem o compilador, nem o colega) se confundir sobre qual contrato essa implementação pertence.
Resolvendo conflito de nomes
Bora ver aquele caso do IWriter e IPrinter. Seguimos com nosso app de estudos, onde, por exemplo, temos uma classe de relatórios:
interface IWriter
{
void Print();
}
interface IPrinter
{
void Print();
}
public class Report : IWriter, IPrinter
{
// Implementação explícita de IWriter.Print
void IWriter.Print()
{
Console.WriteLine("Salvando relatório no arquivo (Writer)...");
}
// Implementação explícita de IPrinter.Print
void IPrinter.Print()
{
Console.WriteLine("Mandando relatório pra impressora (Printer)...");
}
// Método extra pra exibir pro usuário
public void Show()
{
Console.WriteLine("Mostrando relatório na tela.");
}
}
Agora bora usar as interfaces diferentes:
var report = new Report();
report.Show(); // Método público normal
// report.Print(); // Erro! Não existe método Print com esse nome em Report
IWriter writer = report;
writer.Print(); // Chama a implementação de IWriter.Print()
IPrinter printer = report;
printer.Print(); // Chama a implementação de IPrinter.Print()
Aqui os métodos Print não estão disponíveis direto pelo objeto report, só via interface certa. Esse é o núcleo da implementação explícita: só pelo "portal" da interface você chega no método certo.
Como fica na memória: ilustração simples
Na real, a implementação explícita "esconde" o membro da interface dentro da classe. Pra visualizar, imagina essa tabela:
| Como chama | O que realmente executa |
|---|---|
|
Erro de compilação — não existe esse método |
|
Implementação explícita |
|
Implementação explícita |
|
Método Show da classe |
4. Exemplo: Interface — só pra infraestrutura
Num projeto real, é comum esse cenário: a classe tem que implementar uma interface de serviço, mas a implementação não é pra quem usa a classe normalmente.
interface IBroadcastable
{
void Broadcast();
}
public class SecretMessage : IBroadcastable
{
void IBroadcastable.Broadcast()
{
Console.WriteLine("Mensagem secreta foi pro ar...");
}
public void Reveal()
{
Console.WriteLine("Mostrando o segredo na tela.");
}
}
// No código normal:
var message = new SecretMessage();
message.Reveal(); // Método do usuário
// message.Broadcast(); // Erro! — não existe esse método
// Só a infraestrutura sabe o que fazer:
((IBroadcastable)message).Broadcast();
Aqui o método Broadcast() — só pra quem trabalha pelo contrato da interface.
5. Implementação explícita de propriedades e indexadores
Não só métodos podem ser implementados explicitamente, mas também propriedades e até indexadores.
interface IDescribable
{
string Description { get; }
}
public class Product : IDescribable
{
// Implementação explícita da propriedade
string IDescribable.Description => "Descrição só disponível via interface";
// Propriedade pública normal
public string Name { get; set; }
}
// Exemplo de uso:
var p = new Product { Name = "Gadget" };
// p.Description; // Erro! Não existe essa propriedade em Product
var descr = ((IDescribable)p).Description;
Console.WriteLine(descr);
6. Dicas e nuances
Como funciona herança com implementação explícita
Na herança, tudo funciona de forma intuitiva: se a classe base implementa a interface explicitamente, a classe derivada herda esse "comportamento". Mas se a derivada quiser sobrescrever a implementação do método da interface, não rola: implementação explícita não pode ser virtual. Ou seja, não dá pra sobrescrever membro implementado explicitamente.
Se precisar disso — tem que usar implementação normal ou pensar num padrão tipo "template method".
Implementação explícita e implícita
+----------------+
| Invoice |
+----------------+
| Show() | // Pode chamar direto
+----------------+
| ITxtExportable.Export() // Só via ITxtExportable
| IJsonExportable.Export() // Só via IJsonExportable
+----------------+
Características/limitações da implementação explícita
- Métodos e propriedades implementados explicitamente não podem ter modificadores de acesso. Eles são privados pro mundo externo e só acessíveis via interface.
- Esses membros não podem ser static, virtual, abstract ou override.
- Não dá pra acessar membro implementado explicitamente direto pelo objeto da classe (só por variável do tipo interface).
- Se a interface herda de outra, pode implementar explicitamente membros de qualquer nível da hierarquia.
7. Vantagens da implementação explícita
Implementação explícita não é só um truque de sintaxe pra resolver colisão. Tem vários motivos bons pra usar:
Resolver colisão de nomes (The Big One): Esse é o motivo principal. Se duas interfaces que você implementa declaram métodos (ou propriedades) com a mesma assinatura, a implementação explícita deixa você dar implementações separadas, específicas pra cada interface. Sem isso, ia rolar ambiguidade.
Exemplo do mundo real: Imagina que você tem uma impressora. Ela pode ser scanner também. A interface IPrinter tem o método Print(), e a interface IScanner — o método Scan(). Mas e se as duas tivessem ProcessDocument()? Implementação explícita deixa você fazer IPrinter.ProcessDocument() pra imprimir e IScanner.ProcessDocument() pra escanear, cada um com lógica diferente.
Esconder detalhes da implementação e deixar o API da classe limpo: Métodos implementados explicitamente não fazem parte do API público da sua classe. Só dá pra acessar convertendo o objeto pro tipo da interface. Isso é ótimo quando você quer que certa funcionalidade só esteja disponível "pelo contrato", não como parte do comportamento geral do objeto.
Exemplo: Você cria um instrumento financeiro complexo, tipo CreditCard. Ele pode implementar IPayable (pra fazer pagamentos) e IAdminConfigurable (pra configs internas, tipo definir limites). O método IAdminConfigurable.SetLimit() não deve estar disponível pra todo mundo que tem um CreditCard. Só o sistema administrativo, que trabalha com CreditCard como IAdminConfigurable, deve acessar. Implementação explícita deixa o SetLimit() escondido do acesso geral ao CreditCard, deixando o API da classe mais limpo e seguro.
Garantia de contrato: Às vezes um método na sua classe por acaso tem a mesma assinatura que um método da interface, mas você não quer que ele seja a implementação dela. Por exemplo, você tem uma classe MyList com o método Clear() pra limpar o estado interno. Se decidir que MyList deve implementar IList<T>, que também tem Clear(), por padrão seu MyList.Clear() vira a implementação de IList<T>.Clear(). Se a lógica for diferente, implementação explícita IList<T>.Clear() deixa você separar as coisas.
Implementação implícita vs. explícita
Pra ver bem a diferença, olha essa tabelinha comparativa:
| Característica | Implementação implícita | Implementação explícita |
|---|---|---|
| Disponibilidade | Disponível tanto pela classe quanto pela interface. | Disponível só pela interface. |
| Modificador de acesso | Normalmente public (ou protected etc). | Sem modificador de acesso (não pode ser public). |
| Sintaxe | |
|
| Resolução de colisão | Não resolve, o método serve pra todas as interfaces com essa assinatura. | Resolve colisão, permitindo implementações diferentes. |
| Limpeza do API da classe | Método faz parte do API público da classe. | Método não faz parte do API público da classe. |
| Finalidade | Pra implementações gerais, sem ambiguidade. | Pra resolver colisão ou esconder lógica específica. |
Viu só? São dois lados da mesma moeda, cada um com seu propósito. Na maioria das vezes você vai usar implementação implícita, porque é mais simples e prática. Implementação explícita é pra casos específicos, mas importantes.
8. Erros comuns na implementação explícita de interfaces
Erro #1: método não visível pelo objeto da classe.
Métodos implementados explicitamente não estão disponíveis direto pela instância da classe. Isso confunde muita gente: o compilador diz que não achou o método, mas ele tá lá — só que "escondido". Solução: converte o objeto pro tipo da interface, tipo: (IMyInterface)obj.Method().
Erro #2: uso errado de modificadores de acesso.
Na implementação explícita não pode usar modificadores tipo public ou override. Se tentar, vai dar erro de compilação — o compilador não aceita.
Erro #3: tentar sobrescrever método explicitamente implementado na classe derivada.
Se o método da interface foi implementado explicitamente na base, não dá pra sobrescrever na derivada. Essa limitação é importante na hora de desenhar hierarquia de classes com interfaces.
GO TO FULL VERSION