Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Sugestão
Novo no desenvolvimento de software? Começa primeiro pelos tutoriais para começar . Vais encontrar interfaces quando precisares de definir comportamentos partilhados entre tipos não relacionados.
Experiente noutra língua? As interfaces C# são semelhantes às interfaces em Java ou aos protocolos no Swift. Revise a secção de implementação explícita para padrões específicos de C#.
Uma interface define um contrato: um grupo de métodos, propriedades, eventos e indexadores relacionados que um class ou struct deve implementar. As interfaces permitem que um único tipo implemente múltiplos contratos, o que é importante porque o C# não suporta múltiplas heranças de classes. Os structs não podem herdar de outros structs ou classes, por isso interfaces são a única forma de adicionar comportamento partilhado entre tipos de structs.
O exemplo seguinte declara uma interface e uma classe que a implementa:
interface IEquatable<T>
{
bool Equals(T obj);
}
public class Car : IEquatable<Car>
{
public string? Make { get; set; }
public string? Model { get; set; }
public string? Year { get; set; }
public bool Equals(Car? car) =>
car is not null &&
(Make, Model, Year) == (car.Make, car.Model, car.Year);
}
Qualquer classe ou struct que implemente IEquatable<T> deve fornecer um Equals método que corresponda à assinatura da interface. Pode contar com qualquer IEquatable<T> implementação para suportar a comparação de igualdade, independentemente do tipo concreto. Essa previsibilidade é o valor central das interfaces.
Declarar uma interface
Defina uma interface com a interface palavra-chave. Por convenção, os nomes das interfaces começam com maiúscula I:
interface ILogger
{
void Log(string message);
string Name { get; }
}
As interfaces podem conter métodos, propriedades, eventos e indexadores. Uma interface não pode conter campos de instância, construtores de instância ou finalizadores. Os membros são public por defeito. Pode especificar outros modificadores de acessibilidade quando necessário. Por exemplo, usar internal para membros que não deveriam ser visíveis fora da assembleia.
Implementar uma interface
Uma classe ou struct lista as interfaces que implementa após dois pontos na sua declaração. A classe deve fornecer uma implementação para cada membro declarado na interface:
public class ConsoleLogger : ILogger
{
public string Name => "Console";
public void Log(string message) =>
Console.WriteLine($"[{Name}] {message}");
}
public class FileLogger : ILogger
{
public string Name => "File";
public void Log(string message)
{
// In a real app, write to a file
Console.WriteLine($"[{Name}] Writing to file: {message}");
}
}
Uma classe pode implementar múltiplas interfaces, separadas por vírgulas. Deve fornecer implementações para todos os membros a partir de todas as interfaces que lista.
Implementação explícita
Por vezes, é preciso implementar um membro da interface sem o tornar parte da API pública da classe. A implementação explícita qualifica o membro com o nome da interface. O membro só é acessível através de uma variável do tipo de interface:
interface IMetric
{
double GetDistance(); // Returns meters
}
interface IImperial
{
double GetDistance(); // Returns feet
}
public class Runway(double meters) : IMetric, IImperial
{
// Explicit implementation for IMetric
double IMetric.GetDistance() => meters;
// Explicit implementation for IImperial
double IImperial.GetDistance() => meters * 3.28084;
}
A implementação explícita é útil quando duas interfaces declaram membros com o mesmo nome, ou quando se quer manter a superfície pública da classe limpa. Para mais detalhes, consulte Implementação de Interface Explícita.
Herança de interface
As interfaces podem herdar de uma ou mais outras interfaces. Uma classe que implementa uma interface derivada deve implementar todos os membros da interface derivada e todas as suas interfaces base:
interface IDrawable
{
void Draw();
}
interface IShape : IDrawable
{
double Area { get; }
}
public class Circle(double radius) : IShape
{
public double Area => Math.PI * radius * radius;
public void Draw() =>
Console.WriteLine($"Drawing circle with area {Area:F2}");
}
Uma classe que implementa IShape pode ser implicitamente convertida em IDrawable, porque IShape herda dela.
Interfaces vs. classes abstratas
Tanto as interfaces como as classes abstratas definem contratos que os tipos derivados devem cumprir.
- Use uma classe abstrata quando tipos relacionados partilham estado (campos), construtores ou membros não públicos. As classes abstratas permitem-lhe evoluir uma hierarquia adicionando novos membros com comportamento padrão sem quebrar os tipos derivados existentes.
- Use uma interface quando um tipo precisa cumprir um contrato que atravessa hierarquias não relacionadas, ou quando precisa implementar múltiplos contratos. Interfaces não podem declarar campos de instância ou construtores, por isso são mais indicadas para adicionar capacidades a tipos que já têm uma classe base. Para cenários avançados, as interfaces também suportam implementações padrão de membros.
Uma classe pode herdar apenas de uma classe base, mas pode implementar múltiplas interfaces. Essa distinção faz frequentemente das interfaces a melhor escolha para definir capacidades que atravessam hierarquias de tipos.
Trabalhando com interfaces internas
Normalmente pode implementar uma interface interna com membros públicos, desde que todos os tipos na assinatura da interface sejam acessíveis publicamente. Quando uma interface usa tipos internos nas assinaturas dos membros, deve usar implementação explícita porque o membro implementador não pode ser público enquanto expõe tipos internos:
internal class InternalConfiguration
{
public string Setting { get; set; } = "";
}
internal interface ILoggable
{
void Log(string message);
}
internal interface IConfigurable
{
void Configure(InternalConfiguration config);
}
public class ServiceImplementation : ILoggable, IConfigurable
{
// Implicit implementation: ILoggable uses only public types in its signature
public void Log(string message) =>
Console.WriteLine($"Log: {message}");
// Explicit implementation: IConfigurable uses internal types
void IConfigurable.Configure(InternalConfiguration config) =>
Console.WriteLine($"Configured with: {config.Setting}");
}
No exemplo anterior, IConfigurable usa um tipo InternalConfiguration interno na sua assinatura de método.
ServiceImplementation Usa uma implementação explícita para esse membro. Em contraste, ILoggable utiliza apenas tipos públicos (string) na sua assinatura e pode ser implementado implicitamente.
Membros padrão de interface e membros abstratos estáticos
As interfaces suportam duas funcionalidades avançadas que vão além dos contratos básicos:
- Os membros padrão da interface permitem que uma interface forneça um corpo de método. Os tipos de implementação herdam a implementação padrão e podem, opcionalmente, sobrepê-la. Para obter mais informações, consulte métodos de interface padrão.
- Os membros abstratos estáticos exigem que tipos de implementação forneçam um membro estático, o que é útil para definir contratos de operadores ou padrões de fábrica. Para mais informações, veja membros abstratos estáticos nas interfaces.
Ambas as funcionalidades são abordadas no artigo sobre interfaces na referência linguística. A maior parte do uso quotidiano de interfaces envolve os padrões de declaração e implementação descritos anteriormente neste artigo.
Resumo de interfaces
- Uma interface define um contrato de métodos, propriedades, eventos e indexadores.
- Uma classe ou struct que implemente uma interface deve fornecer implementações para todos os membros declarados (a menos que a interface forneça uma implementação padrão).
- Não podes instanciar uma interface diretamente.
- Uma classe ou struct pode implementar várias interfaces. Uma classe pode herdar uma classe base e também implementar uma ou mais interfaces.
- Os nomes das interfaces começam convencionalmente por
I.