Upcasting e Downcasting
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 29 de 70.
Ao trabalhar com hierarquias de herança, você frequentemente precisa converter entre tipos base e derivados. Upcasting converte um tipo derivado para seu tipo base, enquanto o downcasting faz o oposto.
O upcasting é implícito e sempre seguro porque uma classe derivada tem a garantia de ter tudo o que a classe base possui:
Dog myDog = new Dog();
Animal animal = myDog; // Upcasting - implícito, sempre funcionaDowncasting exige um cast explícito e pode falhar em tempo de execução se o objeto não for realmente o tipo para o qual você está convertendo:
Animal animal = new Dog();
Dog dog = (Dog)animal; // Downcasting - funciona porque é realmente um Dog
Animal cat = new Cat();
Dog wrong = (Dog)cat; // Lança InvalidCastException!Para realizar o downcast com segurança, use a palavra-chave is para verificar o tipo primeiro, ou use as que retorna null em vez de lançar uma exceção:
if (animal is Dog d)
{
d.Bark(); // Seguro - d só está disponível se a verificação passar
}
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}A abordagem de correspondência de padrões is é preferida no C# moderno porque combina a verificação de tipo e a conversão (cast) em uma única etapa, tornando seu código mais seguro e limpo.
Desafio
FácilVamos construir um sistema de inspeção de veículos que demonstra como o upcasting e o downcasting permitem que você trabalhe de forma flexível com objetos em uma hierarquia de herança. Você criará diferentes tipos de veículos e escreverá um código que identifica e interage com segurança com tipos específicos de veículos.
Você organizará seu código em quatro arquivos:
Vehicle.cs: Defina uma classe baseVehicleno namespaceInspection. Todo veículo possui uma propriedadeLicensePlate(string) definida através de um construtor. Inclua um método chamadoGetBasicInfo()que retorna"Vehicle: {LicensePlate}".Car.cs: Defina uma classeCarno namespaceInspectionque herda deVehicle. Carros possuem uma propriedade adicionalNumberOfDoors(int). O construtor aceita tanto a placa quanto a contagem de portas. Adicione um método chamadoHonk()que retorna"{LicensePlate} honks: Beep beep!".Motorcycle.cs: Defina uma classeMotorcycleno namespaceInspectionque herda deVehicle. Motocicletas possuem uma propriedadeHasSidecar(bool). O construtor aceita a placa e o status do sidecar. Adicione um método chamadoRev()que retorna"{LicensePlate} revs: Vroom!".Program.cs: No seu arquivo principal, você criará veículos e praticará tanto o upcasting quanto o downcasting seguro. Crie umCare umaMotorcycleusando valores de entrada, depois armazene ambos em um array de referências deVehicle(upcasting). Percorra o array e, para cada veículo, imprima suas informações básicas. Em seguida, use a palavra-chaveiscom correspondência de padrões (pattern matching) para realizar o downcasting com segurança: se for umCar, chameHonk(); se for umaMotorcycle, chameRev().
Você receberá quatro entradas:
- Placa do carro
- Número de portas
- Placa da motocicleta
- Possui sidecar (
trueoufalse)
Imprima a saída neste formato para cada veículo no array:
{GetBasicInfo() result}
{Honk() or Rev() result based on actual type}Por exemplo, se as entradas forem ABC-123, 4, XYZ-789, e false, a saída deve ser:
Vehicle: ABC-123
ABC-123 honks: Beep beep!
Vehicle: XYZ-789
XYZ-789 revs: Vroom!Observe como todos os veículos são armazenados como referências de Vehicle (upcasting), mas você pode descobrir com segurança seus tipos reais e acessar métodos específicos do tipo usando a sintaxe de correspondência de padrões is para downcasting!
Folha de consulta
Ao trabalhar com hierarquias de herança, o upcasting converte um tipo derivado para seu tipo base, enquanto o downcasting converte um tipo base para um tipo derivado.
Upcasting é implícito e sempre seguro:
Dog myDog = new Dog();
Animal animal = myDog; // Upcasting - implícito, sempre funcionaDowncasting requer um cast explícito e pode falhar em tempo de execução:
Animal animal = new Dog();
Dog dog = (Dog)animal; // Downcasting - funciona porque é realmente um Dog
Animal cat = new Cat();
Dog wrong = (Dog)cat; // Lança InvalidCastException!Downcasting seguro usando a palavra-chave is com correspondência de padrões (preferencial):
if (animal is Dog d)
{
d.Bark(); // Seguro - d só está disponível se a verificação passar
}Downcasting seguro usando a palavra-chave as (retorna null se o cast falhar):
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}Experimente você mesmo
using System;
using Inspection;
class Program
{
public static void Main(string[] args)
{
// Ler as entradas
string carPlate = Console.ReadLine();
int numberOfDoors = Convert.ToInt32(Console.ReadLine());
string motorcyclePlate = Console.ReadLine();
bool hasSidecar = Convert.ToBoolean(Console.ReadLine());
// TODO: Criar um objeto Car usando carPlate e numberOfDoors
// TODO: Criar um objeto Motorcycle usando motorcyclePlate e hasSidecar
// TODO: Criar um array de referências Vehicle contendo ambos os veículos (upcasting)
// TODO: Percorrer o array e para cada veículo:
// - Imprimir o resultado de GetBasicInfo()
// - Usar a palavra-chave 'is' com pattern matching para fazer o downcast com segurança:
// - Se for um Car, chamar e imprimir Honk()
// - Se for um Motorcycle, chamar e imprimir Rev()
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos