1. Introdução
Imagina que a gente tem um app pra controlar os animais do zoológico (aquele exemplo que a gente já começou a desenvolver nas aulas passadas). Criamos uma classe base Animal com o método MakeSound, pra todos os bichos poderem "fazer" algum som.
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal faz um som.");
}
}
Mas aí a gente percebe que o som do leão e do papagaio são bem diferentes. O "Animal faz um som" genérico já não serve. É aí que entra a sobrescrita de métodos. Você precisa que cada filho tenha seu próprio som!
2. Sintaxe: como funciona o override
Regras principais
- Pra sobrescrever um método, ele tem que estar marcado na classe base como virtual (ou abstract).
- Na classe derivada, usa a palavra-chave override antes do método com a mesma assinatura.
Exemplo: leões e papagaios
public class Lion : Animal
{
public override void MakeSound()
{
Console.WriteLine("Rrrrr!");
}
}
public class Parrot : Animal
{
public override void MakeSound()
{
Console.WriteLine("Papagaio bobo!");
}
}
Agora, se você criar uma lista de animais e chamar MakeSound pra cada um, cada bicho vai "falar" do seu jeito:
Animal[] zoo = new Animal[]
{
new Lion(),
new Parrot(),
new Animal()
};
foreach (var animal in zoo)
{
animal.MakeSound();
}
// Saída:
// Rrrrr!
// Papagaio bobo!
// Animal faz um som.
3. Tabela virtual de chamadas (v-table)
Quando você usa virtual e override, o compilador gera pra classe uma "tabela virtual de métodos" (v-table).
Quando chama o método por uma referência da classe base, o CLR olha na tabela: será que esse método foi sobrescrito no filho? Se sim — chama a versão do filho.
Essa é a "mágica" do late binding, ou polimorfismo dinâmico.
4. Juntando tudo: continuando nosso app
Bora adicionar mais uma classe:
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Au-au!");
}
}
E usar isso no nosso mini-app de controle de bichos:
Animal[] zoo = new Animal[]
{
new Lion(),
new Parrot(),
new Dog()
};
foreach (var animal in zoo)
{
animal.MakeSound();
}
Agora ficou muito mais fácil manter e expandir o código, e adicionar novos bichos é uma beleza!
5. Sobrescrevendo propriedades e override com valor de retorno
Métodos não são a única coisa que dá pra sobrescrever. Você também pode sobrescrever propriedades virtuais e indexadores.
public class Animal
{
public virtual string Name { get; set; } = "Animal";
}
public class Lion : Animal
{
public override string Name { get; set; } = "Leão";
}
Isso é muito útil quando você quer detalhar info pra um tipo específico.
6. Implementação base usando base
Às vezes você quer só expandir o comportamento do método base, não trocar tudo.
Pra isso, chama a implementação do método base com a palavra base dentro do sobrescrito.
public class Parrot : Animal
{
public override void MakeSound()
{
base.MakeSound(); // "Animal faz um som."
Console.WriteLine("Papagaio bobo!");
}
}
Nesse caso, o papagaio primeiro faz o som "genérico" do zoológico, depois solta o clássico "Papagaio bobo!".
7. Onde isso é útil na prática
Entender como funciona o override é essencial em qualquer projeto C# mais sério que use OOP.
- Modelos de animais no nosso zoológico? Já fizemos.
- Criar controles customizados pra UI: Você sobrescreve métodos visuais padrão e mete sua lógica.
- Lógica de negócio flexível: Dá pra montar o "esqueleto" do comportamento na base e implementar o detalhe nos filhos.
- Testes e mocks: Fica fácil "trocar" métodos via herança pra testes unitários.
- Plugins e extensões: Interface ou classe base abstrata, várias implementações — tudo funciona porque a sobrescrita tá certinha.
8. sealed override e métodos virtuais em cadeia
Se você não quiser que alguém mais embaixo na hierarquia sobrescreva seu método sobrescrito, pode usar sealed override:
public class Base
{
public virtual void Foo() { }
}
public class Middle : Base
{
public sealed override void Foo() { }
}
public class Last : Middle
{
public override void Foo() {} // Erro — não pode sobrescrever!
}
Isso serve pra "fechar" a cadeia de sobrescritas onde for crítico pro sistema funcionar direito.
9. Novo método (palavra-chave new)
Às vezes você quer declarar no filho um método com a mesma assinatura, mas o da base não é virtual.
Nesse caso, pode usar a palavra new — mas isso não é polimorfismo, é mais tipo uma "camuflagem":
public class Animal
{
public void MakeSound()
{
Console.WriteLine("Eu sou um animal!");
}
}
public class Cat : Animal
{
public new void MakeSound()
{
Console.WriteLine("Eu sou um gatinho!");
}
}
Animal animal = new Cat();
animal.MakeSound(); // "Eu sou um animal!"
Cat cat = new Cat();
cat.MakeSound(); // "Eu sou um gatinho!"
Aqui, tudo depende do tipo da variável na hora da chamada! Então, pra polimorfismo dinâmico de verdade, sempre use virtual + override.
10. Erros comuns ao sobrescrever métodos
Erro nº1: tentar sobrescrever um método que não foi declarado como virtual, abstract ou override.
Em C#, não dá pra sobrescrever métodos normais. Se o método na base não tem um modificador especial (virtual, abstract ou override), tentar usar override na derivada vai dar erro de compilação.
Erro nº2: assinatura do método diferente.
Pra realmente sobrescrever, o nome, tipo de retorno e parâmetros do método têm que ser idênticos ao da base. Qualquer diferença (tipo, parâmetro diferente) vai criar um método novo, não sobrescrever.
Erro nº3: esqueceu o modificador override.
Se você define um método com a mesma assinatura da base, mas não coloca override, o compilador não considera sobrescrita. Isso se chama ocultação de método (vamos ver isso separado). Nesse caso, chamar o método por uma variável do tipo base vai dar um resultado inesperado — vai chamar o método da base, não o seu.
GO TO FULL VERSION