1. Introdução
Nessa aula a gente vai ver como funciona a sintaxe de herança em C#: vamos aprender a declarar classes derivadas, entender o que é a palavra-chave base e como usar ela pra acessar a classe mãe. Isso não só vai facilitar tua vida na hora de usar código, mas também deixa o programa bem mais elegante (e às vezes até te deixa dormir mais tranquilo — porque não vai precisar ficar corrigindo erro de copiar-e-colar).
Olha um exemplo simples do dia a dia: temos uma classe Vehicle (veículo) — é o blueprintzão base, genérico. Todo veículo pode andar, tem cor, fabricante e tal:
public class Vehicle
{
public string Brand { get; set; }
public string Color { get; set; }
public void Drive()
{
Console.WriteLine("Bora andar!");
}
}
Agora, bora adicionar um novo tipo de transporte — carro. Carro ainda é um veículo, mas tem suas particularidades (tipo, número de portas). Seria estranho escrever tudo de novo sobre cor e fabricante, já que isso já tá definido!
Herança deixa a gente dizer: "Carro é um veículo, só que com um monte de coisa a mais."
2. Como declarar uma classe filha
Em C#, pra criar uma classe que herda de outra, a gente usa dois pontos :. Depois do nome da classe, diz de quem ela herda.
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
}
Lê assim: a classe Car herda todos os campos e métodos da classe Vehicle e ainda adiciona suas próprias paradas — tipo o número de portas.
Fica ligado: em C# só rola herança simples de classes (ou seja, uma classe só pode herdar de UMA mãe). Mas interface pode herdar de quantas tu quiser! (isso é papo pra depois).
Como funciona na prática?
Quando tu declara:
Car myCar = new Car();
...teu objeto myCar tem acesso tanto aos campos/métodos de Vehicle quanto às propriedades próprias dele. Exemplo:
myCar.Brand = "Toyota";
myCar.Color = "Azul";
myCar.NumberOfDoors = 4;
myCar.Drive(); // Método herdado
Cenário real:
No nosso app "em desenvolvimento", dava pra fazer um menu de veículos, onde o usuário escolhe o tipo de transporte e o programa mostra todas as propriedades dele. Quando tu adicionar um novo tipo de transporte, vai escrever o mínimo de código!
3. Herança de construtores
Quando a gente cria objetos da classe filha, às vezes quer inicializar as propriedades da classe base pelo construtor. Tipo, pra não esquecer de passar marca e cor:
public class Vehicle
{
public string Brand { get; set; }
public string Color { get; set; }
public Vehicle(string brand, string color)
{
Brand = brand;
Color = color;
}
}
Agora, no construtor da filha, tu pode (e deve) chamar o construtor da mãe! Pra isso usa a palavra base:
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public Car(string brand, string color, int numberOfDoors)
: base(brand, color) // chama o construtor de Vehicle!
{
NumberOfDoors = numberOfDoors;
}
}
Como fica na prática?
Car bmw = new Car("BMW", "Preto", 4);
Console.WriteLine($"{bmw.Brand}, {bmw.Color}, portas: {bmw.NumberOfDoors}");
bmw.Drive(); // Continua funcionando!
Ou seja, bmw é carro e veículo ao mesmo tempo. Ele lembra dos "pais".
4. Diferença entre base e this
Em C# tem duas palavras-chave pra mexer com membros da classe: this e base.
- this — acessa o objeto atual (tipo, o campo ou método dele mesmo).
- base — acessa membro da classe mãe (ou seja, da mãe).
Quando usar base? Imagina que tu quer expandir a lógica de um método da mãe, ou só inicializar a mãe a partir da filha. Exemplo:
public class Bicycle : Vehicle
{
public int NumberOfGears { get; set; }
public Bicycle(string brand, string color, int gears)
: base(brand, color) // chama o construtor da mãe
{
NumberOfGears = gears;
}
public void ShowInfo()
{
// Usando propriedades da mãe!
Console.WriteLine($"{Brand} ({Color}), marchas: {NumberOfGears}");
}
}
5. Palavra-chave base em métodos
base não é só pra construtor! Às vezes tu quer mudar o comportamento de um método da mãe, mas ainda usar parte da lógica dela.
Tipo, queremos que o método Drive na classe Car também mostre qual carro estamos dirigindo:
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public Car(string brand, string color, int numberOfDoors)
: base(brand, color)
{
NumberOfDoors = numberOfDoors;
}
public void DriveCar()
{
base.Drive(); // chama a implementação da mãe
Console.WriteLine($"Dirigindo {Brand} com {NumberOfDoors} portas!");
}
}
Explicando:
Chamar base.Drive(); é tipo falar: "Mãe, conta tua parte da história primeiro, depois eu dou mais detalhes!". Então, quando chamar DriveCar() vai aparecer:
Bora andar!
Dirigindo BMW com 4 portas!
Sobre sobrescrever métodos (override) a gente fala nas próximas aulas :P
6. Esquema de herança
Bora visualizar as relações entre as classes. Olha um esqueminha simples (porque UML assusta de manhã cedo!):
Vehicle (base)
/ \
Car Bicycle
- Todas as setas vão DA filha PRA mãe.
- Todas as classes filhas têm acesso aos membros public/protected da mãe.
Diferença entre public, protected e private
Só pra lembrar, pra não confundir.
Na filha, tu enxerga:
| Modificador | Visível na filha? |
|---|---|
| public | Sim |
| protected | Sim |
| private | Não |
Erro comum:
Tentar acessar um campo private da mãe na filha:
public class Vehicle
{
private string secret = "Ninguém vai saber!";
}
public class Car : Vehicle
{
public void RevealSecret()
{
Console.WriteLine(secret); // Erro! Não enxerga.
}
}
Comparando: base vs. filha
| Vehicle | Car (filha) | |
|---|---|---|
| Brand | tem | herda |
| Color | tem | herda |
| Drive() | tem | herda (ou expande) |
| NumberOfDoors | não | próprio (propriedade única) |
7. Exemplo
Se a gente continua evoluindo o appzinho "Controle de veículos", pode montar assim:
public class Vehicle
{
public string Brand { get; set; }
public string Color { get; set; }
public Vehicle(string brand, string color)
{
Brand = brand;
Color = color;
}
public void Drive()
{
Console.WriteLine($"{Brand} {Color} saiu andando!");
}
}
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public Car(string brand, string color, int numberOfDoors)
: base(brand, color)
{
NumberOfDoors = numberOfDoors;
}
public void ShowCar()
{
Console.WriteLine($"Marca: {Brand}, Cor: {Color}, Portas: {NumberOfDoors}");
}
}
public class Bicycle : Vehicle
{
public int NumberOfGears { get; set; }
public Bicycle(string brand, string color, int numberOfGears)
: base(brand, color)
{
NumberOfGears = numberOfGears;
}
}
No método principal dá pra criar uma lista de veículos e mostrar de jeitos diferentes:
Car ford = new Car("Ford", "Vermelho", 4);
ford.ShowCar();
ford.Drive();
Bicycle trek = new Bicycle("Trek", "Verde", 21);
trek.Drive();
8. Erros, pegadinhas e um pouco de zoeira
Erro muito comum — esquecer de chamar o construtor da mãe na filha. Se Vehicle não tem construtor sem parâmetro e tu não chamou base(...), vai dar erro de compilação: o compilador vai reclamar que não consegue inicializar a base. É tipo tentar construir o segundo andar sem o primeiro. Não rola. Tem que passar os dados pra cima.
Segundo erro: esquecer que campos/métodos da mãe marcados como private NÃO SÃO VISÍVEIS na filha. Se quiser deixar algo pros "filhos", usa protected (mais sobre isso nas próximas aulas).
E o principal: herança é massa, mas não inventa moda com hierarquia muito doida. Na vida real, se tua cadeia de herança tem mais de dois ou três níveis, provavelmente já deu ruim. É fácil se perder em quem herda de quem e pra quê tudo isso.
GO TO FULL VERSION