Palavras-chave Virtual e Override
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 21 de 70.
Quando uma classe derivada herda um método, às vezes você deseja alterar completamente como esse método se comporta. As palavras-chave virtual e override trabalham juntas para tornar isso possível de forma controlada.
Marque um método como virtual na classe base para sinalizar que as classes derivadas têm permissão para substituir sua implementação:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}A palavra-chave override na classe derivada informa ao compilador que você está substituindo intencionalmente o método base. Sem virtual no método base, você não pode usar override.
O verdadeiro poder aparece quando você usa uma referência de classe base para conter um objeto derivado:
Animal myPet = new Dog();
myPet.Speak(); // Saída: Woof!Embora myPet seja declarado como Animal, o C# chama a versão Dog de Speak(). Isso é chamado de polimorfismo de tempo de execução - o método real chamado depende do tipo verdadeiro do objeto, não do tipo declarado da variável.
Você ainda pode acessar a implementação base usando base.Speak() dentro da sua sobrescrita se desejar estender em vez de substituir completamente o comportamento.
Desafio
FácilVamos construir um sistema de notificação que demonstra como classes derivadas podem sobrescrever completamente o comportamento da classe base usando as palavras-chave virtual e override, enquanto também mostramos o poder do polimorfismo em tempo de execução.
Você criará três arquivos para organizar seu código:
Notification.cs: Defina uma classeNotificationno namespaceAlerts. Esta classe base representa uma notificação genérica com uma propriedadeMessage(string) e um construtor que a define. Inclua um métodovirtualchamadoSend()que retorna"Sending notification: " + Message.EmailNotification.cs: Defina uma classeEmailNotificationno namespaceAlertsque herda deNotification. Adicione uma propriedadeRecipient(string) e um construtor que aceita tanto a mensagem quanto o destinatário (usebasepara passar a mensagem). Sobrescreva o métodoSend()para retornar"Emailing " + Recipient + ": " + Message.SmsNotification.cs: Defina uma classeSmsNotificationno namespaceAlertsque também herda deNotification. Adicione uma propriedadePhoneNumber(string) e um construtor que aceita tanto a mensagem quanto o número de telefone. SobrescrevaSend()para retornar"SMS to " + PhoneNumber + ": " + Message.Program.cs: No seu arquivo principal, crie instâncias de todos os três tipos de notificação usando os valores de entrada. Em seguida, demonstre o polimorfismo em tempo de execução armazenando todos os três em variáveis do tipoNotificatione chamandoSend()em cada uma.
Você receberá três entradas:
- A mensagem da notificação
- O endereço de e-mail do destinatário
- O número de telefone para SMS
Imprima o resultado da chamada de Send() em cada notificação, armazenadas como referências da classe base:
{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}Por exemplo, se as entradas forem Meeting at 3pm, alice@email.com, e 555-1234, a saída deve ser:
Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pmObserve como, embora todos os três estejam armazenados como variáveis Notification, cada um chama sua própria versão sobrescrita de Send(). Isso é o polimorfismo em tempo de execução em ação!
Folha de consulta
Use a palavra-chave virtual em uma classe base para marcar um método que as classes derivadas podem sobrescrever:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}Use a palavra-chave override em uma classe derivada para substituir a implementação do método da classe base:
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}O polimorfismo de tempo de execução ocorre quando uma referência de classe base contém um objeto derivado - o método real chamado depende do tipo real do objeto, não do tipo declarado da variável:
Animal myPet = new Dog();
myPet.Speak(); // Saída: Woof!Você pode acessar a implementação base dentro de uma sobrescrita usando base:
public override void Speak()
{
base.Speak(); // Chama a versão da classe base
Console.WriteLine("Woof!");
}Experimente você mesmo
using System;
using Alerts;
class Program
{
public static void Main(string[] args)
{
// Lê as entradas
string message = Console.ReadLine();
string email = Console.ReadLine();
string phone = Console.ReadLine();
// TODO: Criar instâncias de todos os três tipos de notificação
// Armazene-as em variáveis Notification para demonstrar polimorfismo
// TODO: Criar uma Notification base
Notification baseNotification;
// TODO: Criar uma EmailNotification (armazenada como tipo Notification)
Notification emailNotification;
// TODO: Criar uma SmsNotification (armazenada como tipo Notification)
Notification smsNotification;
// TODO: Chamar Send() em cada uma e imprimir os resultados
// Os métodos sobrescritos serão chamados devido ao polimorfismo de tempo de execução
}
}
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