Ao projetar aplicações, é importante saber quando usar uma classe abstrata e quando usar uma interface. Embora as classes e interfaces abstratas pareçam semelhantes em alguns aspectos, existem diferenças importantes que determinarão qual é a melhor escolha para o que você está tentando realizar. Nesta postagem do blog discutirei essas diferenças e como decidir quando usar quais.

A resposta curta: uma classe abstrata permite criar funcionalidades que as subclasses podem implementar ou substituir. Uma interface permite apenas definir funcionalidades, não implementá-las. E embora uma classe possa estender apenas uma classe abstrata, ela pode tirar vantagem de múltiplas interfaces.

Classe abstrata C# explicada

Uma classe abstrata é um tipo especial de classe que não pode ser instanciada. Uma classe abstrata é projetada para ser herdada por subclasses que implementam ou substituem seus métodos. Em outras palavras, as classes abstratas são parcialmente implementadas ou nem sequer são implementadas. Você pode ter funcionalidade em sua classe abstrata – os métodos em uma classe abstrata podem ser abstratos e concretos. Uma classe abstrata pode ter construtores – esta é uma grande diferença entre uma classe abstrata e uma interface. Você pode aproveitar classes abstratas para projetar componentes e especificar algum nível de funcionalidade comum que deve ser implementada por classes derivadas.

Interface C# explicada

Uma interface é basicamente um contrato – não possui nenhuma implementação. Uma interface pode conter apenas declarações de métodos; não pode conter definições de métodos. Você também não pode ter dados de membros em uma interface. Enquanto uma classe abstrata pode conter definições de métodos, campos e construtores, uma interface pode conter apenas declarações de eventos, métodos e propriedades. Os métodos declarados em uma interface devem ser implementados pelas classes que implementam a interface. Observe que uma classe pode implementar mais de uma interface, mas estender apenas uma classe. A classe que implementa a interface deve implementar todos os seus membros. Assim como uma classe abstrata, uma interface não pode ser instanciada.

Principais semelhanças entre classes abstratas e interfaces em C#

Existem quatro semelhanças principais entre interfaces e classes abstratas.

Comportamento sem implementação: A linguagem de programação C# fornece interfaces e classes abstratas para definir comportamento sem implementação. Você pode aproveitar uma interface ou classe abstrata para definir um contrato que deve ser seguido por tipos que estendem ou implementam essa interface ou classe abstrata. Por exemplo, você pode definir métodos, propriedades e eventos em uma interface ou classe abstrata que deve ser implementada por tipos que a estendem. Entretanto, do C# 8.0 em diante, uma interface pode conter implementações padrão para seus membros, ou seja, métodos e propriedades.

Sem instanciação: Você pode usar classes abstratas e interfaces para definir tipos que não podem ser instanciados. Em vez disso, você deve implementar uma interface ou estender uma classe abstrata por outros tipos.

Suporte para polimorfismo: Você pode obter polimorfismo usando classes de interface e abstratas. Mais importante ainda, você pode fazer com que uma classe derivada estenda uma classe base abstrata e implemente várias interfaces.

Modificadores de acesso: Métodos em interfaces e classes abstratas podem incluir modificadores de acesso. No entanto, deve-se observar que a partir do C# 7.2, todos os membros de uma interface são públicos por padrão.

Principais diferenças entre classes abstratas e interfaces em C#

Embora existam certas semelhanças entre interfaces e classes abstratas, também existem diferenças sutis.

Implementações padrão: Você pode ter métodos em uma classe abstrata que possuem implementações padrão. No entanto, as interfaces não suportavam implementações padrão para os seus membros até recentemente. Com o C# 8.0, você pode ter implementações padrão para os membros de uma interface, semelhantes às classes abstratas.

Tipos de membros: Em uma classe abstrata, você pode ter vários tipos de membros, como campos, construtores, destruidores e membros estáticos. Você também pode ter construtores em uma classe abstrata, mas não pode ter construtores em uma interface. Para definir um destruidor em qualquer classe em C#, você prefixa o nome do destruidor com um sinal de til (~).

Estado: Além disso, classes abstratas podem possuir informações de estado, ou seja, podem possuir campos. Entretanto, você não pode ter campos de instância em interfaces. É por isso que você pode serializar uma classe abstrata, mas nunca serializar uma interface. Porque você pode serializar apenas uma classe, uma estrutura, um enum ou um delegado. Quando você serializa uma classe abstrata, é a instância da classe derivada que estende a classe abstrata que é realmente serializada.

Herança: Embora uma classe possa estender apenas uma única classe abstrata, ela pode implementar diversas interfaces. Ao implementar várias interfaces, você pode simular herança múltipla aproveitando interfaces em C#.

Vantagens de usar classes abstratas em vez de interfaces em determinados cenários

As classes abstratas fornecem recursos estruturais, comportamentais e de design que as tornam uma boa escolha em determinados cenários.

Gestão estadual: Você pode usar classes abstratas para manter informações de estado que as classes derivadas podem acessar.

Modificadores de acesso: Você pode usar classes abstratas com modificadores de acesso que fornecem controle refinado sobre seus membros.

Construtores e destruidores: Você pode ter construtores e destruidores em uma classe abstrata, permitindo inicializar seus membros e escrever código para lógica de limpeza. Assim, você pode inicializar os membros da classe abstrata quando uma instância de qualquer uma de suas classes derivadas for criada e recuperar recursos de memória quando essas instâncias forem destruídas.

Vantagens de usar interfaces em vez de classes abstratas em determinados cenários

Existem também certas vantagens em usar interfaces em vez de classes abstratas.

Múltiplas implementações: As interfaces são preferidas às classes abstratas quando você deseja estabelecer contratos em seu aplicativo sem impor uma linhagem direta. Uma classe pode implementar diversas interfaces, permitindo que ela esteja em conformidade com vários contratos simultaneamente. Isso pode ajudá-lo a criar componentes que aproveitem as preocupações transversais de um aplicativo.

Modularidade: As interfaces promovem um design no qual você define um contrato ao qual qualquer classe de implementação deve aderir, promovendo assim uma arquitetura modular, extensível e flexível que torna seu código mais fácil de testar, estender e manter.

Zombando: Você pode usar interfaces para criar objetos simulados ao escrever seus testes de unidade. Como você pode definir comportamento sem implementação em uma interface, você pode usar interfaces para criar testes unitários que não dependam de implementações definidas em classes concretas.

Flexibilidade de projeto: Você pode aproveitar as interfaces para criar aplicativos com designs em evolução aproveitando o recurso de implementação padrão introduzido no C# 8.0. Portanto, você pode usar interfaces para criar aplicativos flexíveis e evolutivos sem interromper as implementações existentes. Ao programar para uma interface em vez de suas implementações, você pode trabalhar com abstrações em vez de classes concretas. Isto promove um design em que os componentes são intercambiáveis, desde que cumpram o mesmo contrato definido pela interface que implementam.

Devo usar uma classe abstrata ou uma interface?

As classes abstratas fornecem flexibilidade para ter certos métodos concretos e alguns outros métodos que as classes derivadas devem implementar. Por outro lado, se você usar interfaces, precisará implementar todos os métodos da classe que estende a interface. Uma classe abstrata é uma boa escolha se você tiver planos para expansão futura – ou seja, se houver probabilidade de uma expansão futura na hierarquia de classes. Se quiser fornecer suporte para expansão futura ao usar interfaces, você precisará estender a interface e criar uma nova.

Por outro lado, é fácil adicionar uma nova interface à hierarquia, se necessário. Entretanto, se você já tiver uma classe abstrata em sua hierarquia, não poderá adicionar outra — ou seja, poderá adicionar uma classe abstrata somente se nenhuma estiver disponível. Você deve usar uma interface se quiser um contrato sobre algum comportamento ou funcionalidade. Você não deve usar uma interface se precisar escrever o mesmo código para os métodos de interface. Nesse caso, você deve usar uma classe abstrata, definir o método uma vez e reutilizá-lo conforme necessário. Use interfaces para dissociar o código do seu aplicativo de implementações específicas dele ou para restringir o acesso a membros de um determinado tipo.

Como afirma a documentação de interfaces da Microsoft:

Ao usar interfaces, você pode, por exemplo, incluir comportamento de múltiplas fontes em uma classe. Esse recurso é importante em C# porque a linguagem não oferece suporte a herança múltipla de classes. Além disso, você deve usar uma interface se quiser simular herança para estruturas, porque elas não podem herdar de outra estrutura ou classe.

Implementações de interface implícitas e explícitas

As interfaces podem ser implementadas implícita ou explicitamente. Deixe-me explicar como essas duas implementações diferem. Considere uma interface chamada IBusinessLogic.

public interface IBusinessLogic
{
   void Initialize();
}

A seguinte classe chamada BusinessLogic implementa o IBusinessLogic interface.

public class BusinessLogic : IBusinessLogic
{
   public void Initialize()
   {
       //Some code
   }
}

Você pode criar uma instância do BusinessLogic class explicitamente e então chamar o Initialize() método conforme mostrado abaixo.

 IBusinessLogic businessLogic = new BusinessLogic();
businessLogic.Initialize();

O trecho de código a seguir ilustra como você pode implementar o IBusinessLogic interface implicitamente.

public class BusinessLogic : IBusinessLogic
{
   void IBusinessLogic.Initialize()
   {
   }
}

Agora você pode invocar o Initialize() método da mesma maneira usando uma referência ao IBusinessLogic interface. A diferença entre as duas abordagens é que quando você implementa a interface explicitamente em sua classe, você fica restrito a invocar um método de sua interface usando apenas uma referência à interface. Portanto o trecho de código a seguir não funcionaria, ou seja, não seria compilado.

 BusinessLogic businessLogic = new BusinessLogic();
businessLogic.Initialize();

Classes abstratas versus interfaces em perguntas frequentes sobre C#

Qual é a diferença fundamental entre uma classe abstrata e uma interface?

Embora as interfaces em C# possam ter apenas propriedades, as classes abstratas em C# podem ter propriedades e campos. Normalmente, usamos classes abstratas para criar uma classe base abstrata que outras classes podem estender. Por outro lado, as interfaces são implementadas por classes para obedecer a um contrato.

Com o C# 8.0, uma interface pode ter membros estáticos, virtuais, abstratos, selados, privados e externos. No entanto, uma interface não pode ter construtores ou destruidores em nenhuma versão da linguagem de programação C#.

Uma classe pode herdar de múltiplas interfaces ou classes abstratas?

Uma classe em C# pode implementar múltiplas interfaces, mas pode herdar de uma e apenas uma classe abstrata.

Quando devo usar uma classe abstrata em uma interface?

Devemos usar uma classe abstrata quando precisarmos definir informações de estado e especificar como os campos devem ser inicializados.

Uma classe abstrata ou interface pode conter campos?

Uma classe abstrata pode conter campos, mas você não pode ter campos em uma interface. Isso explica por que você não pode serializar uma interface em C#.

Como as implementações padrão no C# 8.0 afetam a diferença entre classes abstratas e interfaces?

Com a introdução do C# 8.0, as interfaces podem ter implementações padrão para seus membros. No entanto, eles ainda não podem ter construtores ou campos. E diferentemente dos métodos de uma classe abstrata, as implementações padrão de interfaces não podem acessar campos privados.

Como as classes e interfaces abstratas em C# diferem em desempenho?

A diferença de desempenho entre interfaces e classes abstratas depende da implementação. Você pode ter várias definições de membros em uma classe abstrata ou interface. Você também pode tornar sua interface ou classe abstrata leve tendo apenas declarações de membros. Entretanto, como as classes abstratas podem ter construtores e métodos virtuais, elas tendem a exigir mais memória e recursos de CPU do que interfaces.

Em outras palavras, você incorre em uma pequena penalidade de desempenho ao usar classes abstratas. A Microsoft recomenda o uso de tipos concretos sempre que possível para melhor desempenho.