1. Introdução
Imagina que você tem uma classe base Animal com um método Speak(). Por padrão, pra todos os animais ele escreve "Algum som". Você quer criar classes filhas, tipo Dog e Cat, pra que eles falem frases de verdade ("Au au!" e "Miau!"), não só "Algum som".
Você até poderia escrever um método Speak() em cada filho, mas se você trabalhar com um array do tipo Animal[], chamar animal.Speak() ainda vai chamar o método da classe Animal, não do cachorro ou do gato — porque o C# por padrão olha pro tipo da variável, não pro objeto real que tá ali.
Olha um exemplo bem simples:
Animal dog = new Dog();
dog.Speak(); // E aí, o que será que aparece?
Por padrão — "Algum som", não "Au au!", mesmo se Dog tiver seu próprio método Speak(). E agora? Tem que declarar o método como virtual (na base) e override (no filho).
O que é um método virtual?
virtual-método é um método declarado na classe base com o modificador virtual e serve pra que os filhos possam sobrescrever ele ( override).Quando você chama esse método por uma referência da classe base, vai ser chamado o método do objeto "real" — tipo na vida real: se você adota um animal e ele é um gato, ele vai miar ("Miau!"), não só fazer "Algum som".
Métodos virtuais são a base do polimorfismo em C#.
2. Declarando métodos virtuais
Como declarar um método virtual
Na classe base, declara o método com a palavra-chave virtual:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Algum som");
}
}
No filho, usa a palavra-chave override:
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Au au!");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Miau!");
}
}
Exemplo: mostrando polimorfismo
// Por exemplo, no arquivo Program.cs
// Classe base Animal com método virtual Speak
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Algum som");
}
}
// Classe Dog, sobrescreve o comportamento de Speak
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Au au!");
}
}
// Classe Cat, também sobrescreve Speak
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Miau!");
}
}
public static class Program
{
public static void Main()
{
Animal[] animals = new Animal[]
{
new Animal(),
new Dog(),
new Cat()
};
foreach (Animal animal in animals)
{
animal.Speak(); // Vai mostrar: "Algum som", "Au au!", "Miau!"
}
}
}
Viu só a mágica do método virtual? Mesmo se a variável for do tipo Animal, vai ser chamada a versão do método da implementação concreta.
3. O que rola "por baixo dos panos": virtual table
Resumindo o esquema
Quando você declara um método como virtual, o compilador coloca ele numa "virtual table" especial de métodos pra cada tipo (tipo um cardápio diferente pra cada prato). Quando esse método é chamado, o C# não olha pro tipo da variável, mas sim pro que tá de verdade na referência — e "entrega" o método que corresponde ao tipo real do objeto.
Dá pra dizer que o C# troca a receita padrão pela personalizada, se a classe tiver sua própria versão.
Analogia: contrato com "cláusula extra em letras miúdas"
Imagina que você tem um contrato de aluguel (classe base), e nele tá escrito que o inquilino paga um valor fixo todo mês (método PayRent()). Mas você — espertinho — deixa colocar anexos com condições especiais (método virtual). Se algum inquilino (filho) usar esse direito, ele escreve seu próprio override, e agora o pagamento é diferente.
4. Quando usar métodos virtuais?
- Se o método na classe base deve funcionar pra maioria dos filhos, mas alguns podem precisar de um comportamento diferente.
- Quando você monta uma hierarquia de classes que pode ser expandida ou ter parte da lógica alterada.
- Quando tá criando uma arquitetura flexível, tipo pra lógica de negócio, onde tipos diferentes de operações podem precisar de comportamentos próprios.
Em projetos reais, métodos virtuais são a base dos padrões Template Method, Strategy, e de metade da OOP.
Comparando com métodos normais
| Método normal | Método virtual | |
|---|---|---|
| Dá pra sobrescrever | Não | Sim (override) |
| Como é chamado | Pelo tipo da variável | Pelo tipo "real" do objeto |
| Polimorfismo | Não | Sim |
Perguntas frequentes
- Dá pra fazer construtor virtual?
Não! Construtores nunca são virtuais — eles sempre funcionam pelo tipo do objeto. - Métodos virtuais podem ser estáticos?
Não, só métodos de instância podem ser virtuais. Métodos estáticos não participam do polimorfismo, porque não tem objeto — quem ia sobrescrever? - Campos podem ser virtuais?
Não, só métodos, propriedades e eventos.
5. Prática: evoluindo o app "Mundo Animal"
No nosso app de estudo você já fez uma hierarquia simples de animais. Bora adicionar novos métodos virtuais pros bichos comerem de jeitos diferentes!
Exemplo de código com comentários
// Na classe base, criamos o método virtual Eat
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Algum som");
}
public virtual void Eat()
{
Console.WriteLine("Come comida genérica.");
}
}
// Em Dog, sobrescrevemos Eat
public class Dog : Animal
{
public override void Speak() => Console.WriteLine("Au au!");
public override void Eat() => Console.WriteLine("Come ração de cachorro.");
}
// Em Cat, também tem comportamento próprio
public class Cat : Animal
{
public override void Speak() => Console.WriteLine("Miau!");
public override void Eat() => Console.WriteLine("Come peixe.");
}
public static class Program
{
public static void Main()
{
Animal[] animals = new Animal[]
{
new Animal(),
new Dog(),
new Cat()
};
foreach (var animal in animals)
{
animal.Speak();
animal.Eat();
Console.WriteLine("---");
}
}
}
Pronto, agora cada gatinho e cachorrinho come do seu jeito! E o Animal base continua comendo algo estranho, como um funcionalzão com "default".
6. Erros comuns e pegadinhas
- Dá pra esquecer o override no filho — aí o método da base continua valendo.
- Ou, ao contrário, esquecer o virtual na base, aí não dá pra usar override no filho.
Não dá pra escrever override sem virtual
O compilador C# não deixa sobrescrever um método que não foi declarado como virtual (ou abstrato) na base.
Pra que serve o override
override deixa claro pro compilador e pra quem lê o código: "Não tô só criando um método novo — tô mudando de propósito o que veio da base". Isso evita erro quando você quer "sobrescrever", mas sem querer só cria um método novo com assinatura parecida.
Se esquecer o override e escrever um método novo
public class Dog : Animal
{
public void Speak()
{
Console.WriteLine("Au au!");
}
}
Mas isso não é sobrescrita! Se você acessar Dog por uma variável do tipo Animal, vai chamar o método do animal ("Algum som"), não do cachorro. Essas surpresas de tipos são fogo!
GO TO FULL VERSION