Regra de ouro: Interfaces definem capacidades (contratos sem estado) e suportam heranca multipla. Abstract classes fornecem estado + implementacao compartilhada e sao ideais para patterns como Template Method. C# 8+ tem default interface methods, mas use com moderacao.

Arvore de decisao

NaoSimSimDependePrecisa compartilhar comportamento?Abstract ClassHeranca multipla de contratos?InterfaceCombine ambos: IFoo + FooBaseInterface para DI/contrato, Abstract para reuso de logica

Quando escolher Interface

Interface - use quando:

  • Precisa de heranca multipla de contratos (capacidades)
  • Quer DI seams para injecao de dependencia e mocking em testes
  • Quer desacoplar features de hierarquias de classes base
  • A classe precisa implementar multiplos comportamentos ortogonais
  • Vai expor uma API publica que outros consumidores implementam

Abstract Class - use quando:

  • Precisa de estado/campos compartilhados ou protected helpers
  • Quer fornecer uma implementacao base reutilizavel
  • Controla a base e espera evoluir (adicionar virtual members sem quebrar)
  • Precisa de construtores para inicializacao garantida
  • Quer aplicar o pattern Template Method (orquestracao + passos overridable)
Na pratica, combine os dois: Defina uma interface para o contrato (DI/mocking), depois crie uma abstract class que a implementa com logica compartilhada. Classes concretas herdam da abstract e implementam os detalhes.

Exemplo A - Diferencas fundamentais

Interface define o contrato (Area()), abstract class adiciona estado compartilhado (Color):

// Interface define contrato β€” sem estado public interface IShape { double Area(); } // Abstract class adiciona estado + implementacao compartilhada public abstract class ShapeBase : IShape { public string Color { get; init; } = "Black"; // shared state public abstract double Area(); // derived must implement } // Classe concreta β€” herda estado e implementa o contrato public sealed class Circle : ShapeBase { public double Radius { get; init; } public override double Area() => Math.PI * Radius * Radius; }
Por que nao colocar tudo numa interface com default methods?
Default interface methods (C# 8+) nao podem acessar estado (campos de instancia). Abstract classes podem ter campos, construtores e protected helpers. Alem disso, default methods adicionam complexidade (diamond problem) e nao sao invocaveis diretamente pela classe implementadora β€” apenas via cast para a interface.

Exemplo B - Multiplas interfaces, uma base class

Rectangle herda de uma abstract base (RenderableShape) e implementa multiplas interfaces:

// Interfaces modelam capacidades ortogonais public interface IHasPerimeter { double Perimeter(); } public interface IRenderable { void Render(); } // Abstract class combina interface + logica compartilhada public abstract class RenderableShape : IShape, IRenderable { public abstract double Area(); public virtual void Render() => Console.WriteLine("Rendering a shape..."); } // Classe concreta: herda UMA base, implementa MUITAS interfaces public sealed class Rectangle : RenderableShape, IHasPerimeter { public double W { get; init; } public double H { get; init; } public override double Area() => W * H; public double Perimeter() => 2 * (W + H); public override void Render() => Console.WriteLine($"Rect {W}x{H}, area={Area()}"); }
C# suporta heranca multipla de classes?
Nao. Uma classe pode herdar de apenas uma classe base, mas pode implementar quantas interfaces quiser. Interfaces modelam capacidades ortogonais (IHasPerimeter, IRenderable), enquanto a abstract class centraliza logica compartilhada.