A herança e o polimorfismo nos deram famílias de objetos poderosas, mas ambos partem de uma classe base que traz alguma implementação concreta — Animal tinha um Nome e um EmitirSom genérico. Há situações, porém, em que não queremos compartilhar implementação alguma, mas apenas estabelecer um contrato: dizer "todo objeto deste tipo sabe fazer tal coisa", sem dizer como — e permitir que classes completamente diferentes, sem parentesco entre si, cumpram esse contrato. Além disso, lembra que uma classe só pode herdar de uma classe base? E quando um objeto precisa cumprir vários papéis ao mesmo tempo? Para ambas as necessidades, o C# oferece um mecanismo elegante e distinto da herança: a interface. Esta aula, que fecha a fase das relações entre objetos, explica o que são as interfaces, como definem contratos, e por que são uma das ferramentas mais valiosas do design orientado a objetos.
A ideia: um contrato sem implementação
Uma interface é um contrato que declara quais comportamentos um objeto deve ter, sem fornecer nenhuma implementação para eles. Ela lista métodos (e properties) que qualquer classe que "assinar" o contrato se compromete a implementar. A interface diz o quê — "quem for deste tipo sabe calcular sua área" — e deixa o como inteiramente por conta de cada classe que a adota. É uma promessa de capacidade, não uma herança de código.
Uma analogia útil: pense numa interface como uma habilidade certificada. Uma "certificação de primeiros socorros" não diz como uma pessoa específica presta socorro — um médico, um bombeiro e um professor certificados o farão de formas diferentes —, mas garante que todos os certificados sabem prestar socorro. A certificação é o contrato; cada pessoa a cumpre à sua maneira. Classes totalmente diferentes — que não têm nenhum parentesco de herança entre si — podem, ainda assim, compartilhar a mesma "certificação" (interface), comprometendo-se a oferecer os comportamentos que ela exige. Isso é algo que a herança, presa à relação "é um" e à única classe base, não consegue fazer.
Definindo e implementando uma interface
Definimos uma interface com a palavra-chave interface. Por convenção do C#, nomes de interface começam com a letra I maiúscula (de interface), o que as distingue das classes à primeira vista. Dentro dela, listamos os comportamentos exigidos — apenas suas assinaturas (nome, tipo de retorno, parâmetros), sem corpo, pois a interface não implementa nada:
// Uma interface: o contrato. Note o 'I' inicial e a ausência de corpos.
interface IArea
{
double CalcularArea(); // apenas a assinatura, sem implementação
}
Uma classe implementa uma interface usando a mesma sintaxe de dois-pontos da herança, e assume a obrigação de fornecer o corpo de todos os comportamentos que a interface exige — caso contrário, o programa não compila:
class Circulo : IArea // "Circulo cumpre o contrato IArea"
{
public double Raio { get; set; }
// Obrigatório implementar CalcularArea, pois assinamos o contrato IArea:
public double CalcularArea()
{
return 3.14159 * Raio * Raio;
}
}
class Quadrado : IArea
{
public double Lado { get; set; }
public double CalcularArea()
{
return Lado * Lado;
}
}
Circulo e Quadrado implementam IArea, cada um à sua maneira — o círculo com a fórmula do círculo, o quadrado com a do quadrado. Não há classe base compartilhada, não há implementação herdada; há apenas o compromisso comum de saber calcular a área, cumprido de formas diferentes. O C# força o cumprimento: se Circulo não fornecesse o método CalcularArea, o compilador recusaria o código, lembrando que o contrato IArea não foi honrado. A interface, portanto, é uma promessa que a linguagem faz valer.
O poder: tratar por contrato, não por tipo concreto
O grande valor das interfaces, assim como no polimorfismo, é poder tratar objetos pelo contrato que cumprem, ignorando seus tipos concretos. Você pode ter uma coleção de IArea que contenha círculos, quadrados e quaisquer outras formas — o que importa é que todos sabem calcular área:
// Uma lista de IArea: qualquer objeto que cumpra o contrato cabe aqui.
List<IArea> formas = new()
{
new Circulo { Raio = 2 },
new Quadrado { Lado = 3 }
};
foreach (IArea forma in formas)
{
Console.WriteLine($"Área: {forma.CalcularArea()}");
}
Isto se parece com o polimorfismo da aula anterior — e o espírito é o mesmo —, mas com uma diferença crucial: Circulo e Quadrado não precisam ter uma classe base comum. Eles não são parentes; apenas cumprem o mesmo contrato. A interface permite unificar objetos por capacidade, não por ancestralidade. Um método que receba um IArea funcionará com qualquer objeto que saiba calcular área, presente ou futuro, sem se importar com sua classe. Essa é uma flexibilidade que a herança, limitada à hierarquia "é um", não oferece.
A solução para os "múltiplos papéis"
Lembra que uma classe só pode herdar de uma classe base? Pois uma classe pode implementar quantas interfaces quiser. Isso resolve elegantemente o problema dos objetos que precisam cumprir vários papéis:
interface IArea { double CalcularArea(); }
interface IDesenhavel { void Desenhar(); }
// Uma classe pode cumprir VÁRIOS contratos ao mesmo tempo:
class Circulo : IArea, IDesenhavel
{
public double Raio { get; set; }
public double CalcularArea() => 3.14159 * Raio * Raio;
public void Desenhar() => Console.WriteLine("Desenhando um círculo.");
}
O Circulo cumpre dois contratos: sabe calcular área (IArea) e sabe se desenhar (IDesenhavel). Onde a herança de classes é única, as interfaces são múltiplas — um objeto pode ter tantas "certificações" quantas fizerem sentido. Como as interfaces não trazem implementação (apenas exigências), não há a ambiguidade que a herança múltipla de classes causaria; cada contrato é apenas uma lista de compromissos, e a classe os cumpre todos. É por isso que o C# escolheu "herança única de classe + múltiplas interfaces": você obtém a flexibilidade de combinar papéis sem os conflitos da herança múltipla de implementação. Aliás, você já vinha usando interfaces sem saber — coleções como a List cumprem contratos internos que permitem, por exemplo, que o foreach as percorra uniformemente.
Interface ou herança: uma orientação
Fecho com uma orientação prática, pois é natural se perguntar quando usar cada mecanismo. Use herança quando há uma genuína relação "é um" e você quer compartilhar implementação comum entre parentes próximos (um Gerente que reaproveita o código de Funcionario). Use interface quando quer apenas definir um contrato de capacidade — especialmente se classes não relacionadas precisarem cumpri-lo, ou se uma classe precisar cumprir vários contratos. Uma regra de bolso frequente entre programadores: na dúvida, prefira interfaces, pois elas são mais flexíveis e acoplam menos as classes umas às outras. Mas não trate isso como dogma; herança e interfaces são ferramentas complementares, e com a prática você desenvolverá o discernimento de qual serve melhor a cada situação. O importante, agora, é compreender que existem dois modos de relacionar objetos — por ancestralidade (herança) e por contrato (interface) — e que ambos têm seu lugar.
A interface trouxe uma forma de contrato sem implementação: ela declara o que um tipo deve saber fazer, sem dizer como. Quem a implementa assume o compromisso, e o compilador cobra o cumprimento integral.
O poder disso está em tratar objetos pelo contrato, e não pelo tipo concreto. Um método que recebe a interface aceita qualquer classe que a implemente, incluindo classes que ainda serão escritas, e isso desacopla profundamente as partes de um sistema. É também a resposta ao limite da herança única: uma classe tem uma só base, mas pode assumir quantos contratos forem necessários, o que resolve com naturalidade os casos em que um tipo desempenha vários papéis.
Fontes e leituras recomendadas
- Interfaces em C# — a referência oficial sobre definição e implementação de interfaces; a base desta aula.
- Como implementar uma interface — o passo a passo de fazer uma classe cumprir um contrato.
- Interfaces versus classes abstratas — orientações sobre quando usar cada mecanismo.
- Implementando múltiplas interfaces — como uma classe cumpre vários contratos ao mesmo tempo.
- Convenções de nomenclatura de interfaces — a convenção do prefixo
Ie outras diretrizes.
Exercícios
Exercício 1
Defina uma interface INotificavel com um método Enviar(string mensagem). Crie duas classes não relacionadas — Email e SMS — que a implementem, cada uma exibindo a mensagem de forma diferente. Crie objetos das duas, coloque-os numa List<INotificavel>, e envie a mesma mensagem por todas com um foreach.
Ver resposta
✓ Resposta: Exemplo:
interface INotificavel { void Enviar(string mensagem); }
class Email : INotificavel
{
public void Enviar(string mensagem) => Console.WriteLine($"E-mail: {mensagem}");
}
class SMS : INotificavel
{
public void Enviar(string mensagem) => Console.WriteLine($"SMS: {mensagem}");
}
List<INotificavel> canais = new() { new Email(), new SMS() };
foreach (INotificavel c in canais)
c.Enviar("Olá!");
Apesar de Email e SMS não terem parentesco, ambos cumprem o contrato INotificavel, o que permite tratá-los uniformemente na lista e enviar a mensagem por todos.
Exercício 2
Explique, com suas palavras, a diferença entre uma interface e uma herança de classe. O que a interface não fornece que a classe base fornece, e por que essa diferença permite que classes sem parentesco compartilhem um mesmo contrato?
Ver resposta
✓ Resposta: Uma interface é apenas um contrato — uma lista de comportamentos exigidos, sem nenhuma implementação —, enquanto uma herança de classe fornece implementação concreta (código de métodos, properties com dados) que a derivada reaproveita. A interface não fornece implementação nem estado; fornece só a exigência de que certos comportamentos existam. Essa diferença permite que classes sem parentesco compartilhem um mesmo contrato porque, não havendo código ou hierarquia a herdar, qualquer classe — independentemente de sua ancestralidade — pode se comprometer a cumprir a interface, implementando os comportamentos exigidos à sua própria maneira. A interface une por capacidade, não por linhagem.
Exercício 3
Faça a classe Email do exercício 1 implementar também uma segunda interface, IRegistravel, com um método Registrar(). Mostre que uma única classe pode cumprir dois contratos ao mesmo tempo, e explique por que isso seria impossível com herança de classes.
Ver resposta
✓ Resposta: Exemplo:
interface IRegistravel { void Registrar(); }
class Email : INotificavel, IRegistravel
{
public void Enviar(string mensagem) => Console.WriteLine($"E-mail: {mensagem}");
public void Registrar() => Console.WriteLine("E-mail registrado no histórico.");
}
A classe Email cumpre dois contratos simultaneamente. Isso seria impossível com herança de classes porque uma classe só pode herdar de uma classe base — não pode ter duas mães. As interfaces, por não trazerem implementação e por poderem ser implementadas em qualquer número, permitem que uma classe assuma múltiplos papéis sem a ambiguidade que a herança múltipla de classes causaria.
Exercício 4
Defina uma interface IReprodutivel com métodos Tocar() e Pausar(). Implemente-a em duas classes distintas, Musica e Video. Escreva um método void Controlar(IReprodutivel item) que chame Tocar() e Pausar() no item recebido, e demonstre que ele funciona tanto com uma música quanto com um vídeo. Explique por que o método aceita ambos.
Ver resposta
✓ Resposta: Exemplo:
interface IReprodutivel { void Tocar(); void Pausar(); }
class Musica : IReprodutivel
{
public void Tocar() => Console.WriteLine("Tocando música.");
public void Pausar() => Console.WriteLine("Música pausada.");
}
class Video : IReprodutivel
{
public void Tocar() => Console.WriteLine("Reproduzindo vídeo.");
public void Pausar() => Console.WriteLine("Vídeo pausado.");
}
void Controlar(IReprodutivel item)
{
item.Tocar();
item.Pausar();
}
Controlar(new Musica());
Controlar(new Video());
O método Controlar aceita tanto uma música quanto um vídeo porque seu parâmetro é do tipo IReprodutivel — ou seja, ele aceita qualquer objeto que cumpra esse contrato, sem se importar com a classe concreta. Como Musica e Video implementam IReprodutivel, ambos são aceitos, e o método chama Tocar e Pausar confiando que todo IReprodutivel os oferece.
Exercício 5
Sem código: usando a analogia da "certificação" da aula, explique o que é uma interface e por que ela representa uma promessa de capacidade em vez de uma partilha de código. Em que situação você preferiria uma interface a uma herança de classe?
Ver resposta
✓ Resposta: Uma interface é como uma certificação de habilidade: ela garante que todo objeto que a "possui" sabe realizar certos comportamentos, sem dizer como — cada classe os realiza à sua maneira, assim como cada pessoa certificada aplica a habilidade de forma própria. Ela representa uma promessa de capacidade em vez de uma partilha de código porque não contém nenhuma implementação: não há métodos prontos nem dados a herdar, apenas a exigência de que os comportamentos existam. Prefere-se uma interface à herança de classe quando o objetivo é apenas garantir que objetos — possivelmente de classes sem qualquer parentesco — compartilhem uma capacidade, ou quando um objeto precisa cumprir vários papéis ao mesmo tempo; nesses casos, a interface une os objetos pela habilidade comum, com mais flexibilidade e menos acoplamento do que a herança conseguiria.