Polimorfismo: Compilação vs Execução
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 25 de 70.
Polimorfismo significa "muitas formas" - a capacidade de o mesmo código se comportar de maneira diferente dependendo do contexto. O C# suporta dois tipos distintos de polimorfismo, e entender quando cada um é resolvido ajuda você a escrever códigos mais flexíveis.
Polimorfismo de tempo de compilação (também chamado de polimorfismo estático) é resolvido pelo compilador antes de seu programa ser executado. A sobrecarga de métodos é o exemplo principal - múltiplos métodos compartilham o mesmo nome, mas possuem parâmetros diferentes:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // O compilador escolhe a versão int
calc.Add(5.0, 3.0); // O compilador escolhe a versão doubleO compilador examina os tipos de argumentos e seleciona o método correto em tempo de compilação. Esta decisão é fixada antes mesmo de o programa ser executado.
Polimorfismo de tempo de execução (polimorfismo dinâmico) é resolvido enquanto o programa é executado. Isso acontece com métodos virtual e override - o tipo real do objeto determina qual método será executado:
Animal pet = new Dog(); // Declarado como Animal, na verdade um Dog
pet.Speak(); // Chama o Speak() de Dog em tempo de execuçãoO compilador não sabe qual Speak() será executado - essa decisão acontece em tempo de execução com base no tipo real do objeto. É isso que torna o polimorfismo baseado em herança tão poderoso para construir sistemas flexíveis e extensíveis.
Desafio
FácilVamos construir um sistema conversor que demonstra os dois tipos de polimorfismo em ação. Você criará uma classe que usa sobrecarga de métodos (polimorfismo de tempo de compilação) juntamente com uma hierarquia de herança que usa métodos virtual/override (polimorfismo de tempo de execução).
Você organizará seu código em quatro arquivos:
Converter.cs: Defina uma classeConverterno namespaceConversion. Esta classe demonstra o polimorfismo de tempo de compilação através da sobrecarga de métodos. Crie três métodosConvertsobrecarregados:Convert(int value)retorna"Integer: {value}"Convert(double value)retorna"Double: {value}"Convert(string value)retorna"String: {value}"
Shape.cs: Defina uma classe baseShapeno namespaceConversion. Esta classe possui uma propriedadeName(string) e um construtor que a define. Inclua um métodovirtualchamadoDescribe()que retorna"This is a {Name}".Circle.cs: Defina uma classeCircleno namespaceConversionque herda deShape. Adicione uma propriedadeRadius(double). O construtor aceita um raio e passa"Circle"para o construtor base. SobrescrevaDescribe()para retornar"This is a Circle with radius {Radius}".Program.cs: No seu arquivo principal, demonstre ambos os tipos de polimorfismo. Primeiro, crie umConvertere chame todos os três métodos sobrecarregados com valores de entrada. Em seguida, crie umCircle, armazene-o em uma variávelShapee chameDescribe()para mostrar o polimorfismo de tempo de execução.
Você receberá quatro entradas:
- Um valor inteiro
- Um valor double
- Um valor string
- Um raio de círculo (double)
Imprima a saída neste formato:
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}Por exemplo, se as entradas forem 42, 3.14, Hello e 5.5, a saída deve ser:
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5Observe a diferença fundamental: o compilador decide qual método Convert chamar com base nos tipos de argumentos em tempo de compilação, enquanto o método Describe() é resolvido em tempo de execução com base no tipo real do objeto, mesmo que ele esteja armazenado em uma variável Shape!
Folha de consulta
Polimorfismo significa "muitas formas" - a capacidade de o mesmo código se comportar de maneira diferente dependendo do contexto.
Polimorfismo de tempo de compilação (polimorfismo estático) é resolvido pelo compilador antes de o programa ser executado. A sobrecarga de métodos é o principal exemplo:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // O compilador escolhe a versão int
calc.Add(5.0, 3.0); // O compilador escolhe a versão doubleO compilador examina os tipos de argumentos e seleciona o método correto em tempo de compilação.
Polimorfismo de tempo de execução (polimorfismo dinâmico) é resolvido enquanto o programa é executado usando métodos virtual e override:
Animal pet = new Dog(); // Declarado como Animal, na verdade um Dog
pet.Speak(); // Chama o Speak() do Dog em tempo de execuçãoO tipo real do objeto determina qual método é executado em tempo de execução, não o tipo de variável declarado.
Experimente você mesmo
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// Ler as entradas
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO: Demonstrar Polimorfismo de Tempo de Compilação
// Criar um objeto Converter e chamar todos os três métodos Convert sobrecarregados
Console.WriteLine("Compile-time Polymorphism:");
// Chamar Convert com intValue, doubleValue e stringValue
// TODO: Demonstrar Polimorfismo de Tempo de Execução
// Criar um objeto Circle e armazená-lo em uma variável Shape
// Em seguida, chamar Describe() na variável Shape
Console.WriteLine("Runtime Polymorphism:");
// Criar Circle e chamar Describe()
}
}
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