Menu

Classe abstrata em C#: métodos abstratos, exemplos e comparação com interface

O que é uma classe abstrata em C#: uma classe base que não pode ser instanciada e pode declarar membros abstratos que as classes derivadas precisam implementar. Cobre métodos e propriedades abstratos, construtores, o padrão template method e quando escolher uma interface.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Uma classe abstrata é uma classe que só existe para ser derivada. Ela não pode ser instanciada e pode declarar membros abstratos: métodos e propriedades sem corpo que toda classe derivada concreta precisa implementar. Todo o resto é uma classe comum: ela pode ter campos, construtores e métodos totalmente implementados que as classes derivadas herdam.

Declarando uma classe abstrata

Marque a classe como abstract, e marque como abstract cada membro que as classes derivadas precisam fornecer, terminando-o com ponto e vírgula em vez de um corpo:

Saída:

Circle: area 7.07, perimeter 9.42
Rectangle: area 10.00, perimeter 13.00

Describe é escrito uma vez, na classe base, e chama Area e Perimeter, que nem existem em Shape. Esse é o ponto de um membro abstrato: a classe base pode contar com ele, e o compilador garante que toda forma concreta o forneça. Uma classe derivada que esquece um não compila:

class Triangle : Shape
{
    public Triangle() : base("Triangle") { }
    public override double Area() => 6;
}
// error CS0534: 'Triangle' does not implement inherited abstract member 'Shape.Perimeter()'

A solução é implementar Perimeter, ou marcar o próprio Triangle como abstract, se ele for uma base intermediária para outras classes.

Regras dos membros abstratos

  • Um membro abstrato não tem corpo e é implicitamente virtual: as classes derivadas o implementam com override.
  • Só classes abstratas podem conter membros abstratos. Colocar um em uma classe normal é um erro de compilação.
  • Membros abstratos não podem ser private (ninguém poderia sobrescrevê-los) nem static em uma classe.
  • Métodos, propriedades, indexadores e eventos podem ser abstratos. Campos e construtores não.
  • Uma classe abstrata não precisa conter nenhum membro abstrato. Marcar uma classe só como abstract impede a instanciação, o que é útil para uma classe base que só faz sentido por meio das subclasses.

Uma propriedade abstrata declara quais acessores a classe derivada precisa fornecer:

abstract class Plan
{
    public abstract decimal MonthlyPrice { get; }
    public abstract string Name { get; }
}

class ProPlan : Plan
{
    public override decimal MonthlyPrice => 12.99m;
    public override string Name => "Pro";
}

Construtores em classes abstratas

Uma classe abstrata pode ter construtores. Eles não podem ser chamados com new, mas todo construtor derivado executa um por meio de : base(...), então é neles que o estado compartilhado é inicializado e validado. Declare-os como protected, o que diz exatamente isso: só as classes derivadas podem usar este construtor.

O padrão template method

O motivo mais comum para escrever uma classe abstrata é fixar a ordem de um algoritmo na classe base e deixar as classes derivadas preencherem os passos individuais. O método da base que executa os passos não é virtual, então nenhuma subclasse pode reordená-los ou pulá-los.

Saída:

item,qty,price
Coffee beans,2,11.50
Filter papers,1,3.20

| Item | Qty | Price |
| --- | --- | --- |
| Coffee beans | 2 | 11.50 |
| Filter papers | 1 | 3.20 |

Total: 26.20

Cada linha é uma tupla nomeada, e a saída é montada com um StringBuilder. Repare na mistura: Header e Row são abstract porque todo formato precisa decidi-los, e Footer é virtual com um padrão vazio porque a maioria dos formatos não precisa de nada ali. Os passos são protected, então o código de fora só pode chamar Export. As classes do framework usam a mesma ideia: Stream e TextWriter são classes base abstratas cujos muitos métodos de conveniência são construídos sobre poucos membros que uma classe derivada fornece (Read e Write para um stream, Write(char) para um text writer).

Classe abstrata vs interface

As duas permitem escrever código para um tipo sem conhecer a classe concreta. Elas diferem no que podem conter e em quantas uma classe pode ter:

Classe abstrataInterface
Uma classe pode herdar ou implementarumamuitas
Campos de instância (estado)simnão
Construtoressimnão
Métodos com corposimsim, desde o C# 8 (métodos padrão de interface)
Membros staticsimsim, desde o C# 8; membros static abstract desde o C# 11
Modificadores de acesso nos membrosqualquer umpublic por padrão; outros permitidos desde o C# 8
Structs podem usá-lanãosim
Adicionar um membro depoisseguro se não for abstratoquebra quem implementa, a menos que tenha um corpo padrão

Os métodos padrão de interface (C# 8) diminuíram a distância, mas não a diferença central:

public interface ILogger
{
    void Write(string message);
    void Error(string message) => Write("ERROR: " + message);   // default implementation: C# 8, .NET Core 3.0+
}

Um método padrão continua sem poder mexer em campos de instância, porque a interface não tem nenhum; ele só pode chamar outros membros da interface.

Uma forma prática de escolher:

  • Interface quando você descreve uma capacidade que tipos sem relação podem ter (IComparable<T>, IDisposable, IShippingProvider), quando structs devem poder participar, ou quando uma classe precisa de vários desses papéis. A maior parte do código com injeção de dependência é escrita para interfaces por esse motivo.
  • Classe abstrata quando os tipos derivados são de fato variações de uma mesma coisa e compartilham estado ou um algoritmo fixo, como os exportadores acima.
  • As duas é comum: uma interface da qual quem chama depende, e uma classe base abstrata que a implementa e poupa quem implementa de reescrever as partes compartilhadas. A página de interfaces cobre o lado da interface em detalhe.

Erros comuns

  • Tentar instanciar a classe base. new Shape(...) é o CS0144. Instancie uma classe derivada.
  • Esquecer o override na implementação. Escrever public double Area() na classe derivada não implementa o membro abstrato; o compilador reporta CS0534 pela implementação ausente, mais um aviso de que o método novo oculta o herdado.
  • Um construtor público em uma classe abstrata. Inofensivo, mas enganoso. Use protected.
  • Classes abstratas sem código compartilhado. Se a classe base só tem membros abstratos e nenhum estado, uma interface diz a mesma coisa e não gasta a única vaga de classe base.
  • Chamar membros abstratos a partir do construtor da base. A implementação derivada executa antes do corpo do construtor derivado, então os campos que esse corpo atribui ainda estão em 0 ou null.

Perguntas frequentes

O que é uma classe abstrata em C#?

Uma classe marcada como abstract serve só como classe base: você não pode criar uma instância dela com new (erro CS0144). Ela pode conter campos, construtores e métodos normais, mais membros abstract que não têm corpo e precisam ser implementados por toda classe derivada não abstrata.

Qual a diferença entre uma classe abstrata e uma interface em C#?

Uma classe só pode derivar de uma classe abstrata, mas pode implementar muitas interfaces. Uma classe abstrata pode guardar estado (campos de instância), construtores e membros com qualquer nível de acesso, e é o lugar natural para implementação compartilhada. Uma interface descreve uma capacidade sem estado de instância; desde o C# 8 ela pode ter corpos padrão de métodos, mas continua sem campos de instância nem construtores.

Qual a diferença entre métodos abstract e virtual em C#?

Um método virtual tem um corpo que as classes derivadas podem sobrescrever. Um método abstract não tem corpo e as classes derivadas precisam sobrescrevê-lo, senão elas mesmas ficam abstratas (erro CS0534 para uma classe concreta). Métodos abstratos só podem aparecer em classes abstratas.

Uma classe abstrata pode ter construtor em C#?

Sim. Ele executa quando uma classe derivada é construída, por meio de : base(...), e normalmente inicializa os campos compartilhados. Declare-o como protected: um construtor público em uma classe abstrata sugere que quem chama pode usá-lo, e não pode.

Por que não posso criar uma instância de uma classe abstrata?

Porque ela pode ter membros abstratos sem implementação: chamar new Shape().Area() não teria código para executar. O compilador reporta CS0144 ("Cannot create an instance of the abstract type or interface"). Crie uma instância de uma classe derivada concreta, e guarde-a em uma variável do tipo abstrato se quiser: Shape s = new Circle(2);.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR