Cnoceito de Polimorfismo
O polimorfismo é um pilar fundamental da programação orientada a objetos que permite que uma única ação ou método se comporte de maneiras diferentes dependendo do objeto ou contexto em que é aplicado. Em termos simples, representa o princípio de "uma interface, múltiplas implementações".
Em C#, o polimorfismo pode ser classificado em duas categorias principais: estático (resolvido em tempo de compilação) e dinâmico (resolvido em tempo de execução). Como exemplo prático no mundo real, pense em um sistema de processamento de pagamentos: ao chamar o método Processar(), o comportamento exato dependerá se a instância atual é um CartaoDeCredito, Pix ou Boleto.
Polimorfismo Estático
Também conhecido como ligação antecipada (early binding), ocorre quando o compilador determina qual método ou operador deve ser invocado antes da execução do programa. Em C#, a sobrecarga de métodos é a principal forma de alcançar esse tipo de polimorfismo.
Sobrecarga de Métodos
A sobrecarga permite que uma classe possua múltiplos métodos com o mesmo nome, desde que suas assinaturas (tipo, quantidade ou ordem dos parâmetros) sejam diferentes. O tipo de retorno, isoladamente, não é suficiente para diferenciar as sobrecargas.
O exemplo abaixo demonstra uma classe que calcula impostos utilizando a sobrecarga do método Calcular com diferentes combinações de parâmetros:
using System;
namespace PolimorfismoEstatico
{
public class CalculadoraImpostos
{
public decimal Calcular(decimal valor)
{
return valor * 0.10m; // Alíquota padrão de 10%
}
public decimal Calcular(decimal valor, decimal taxaPersonalizada)
{
return valor * taxaPersonalizada;
}
public decimal Calcular(decimal valor, decimal taxa, decimal desconto)
{
return (valor * taxa) - desconto;
}
}
class Program
{
static void Main()
{
var calc = new CalculadoraImpostos();
Console.WriteLine($"Imposto padrão: {calc.Calcular(100m)}");
Console.WriteLine($"Imposto customizado: {calc.Calcular(100m, 0.15m)}");
Console.WriteLine($"Com desconto: {calc.Calcular(100m, 0.15m, 5m)}");
}
}
}
A seguir, um exemplo de sobrecarga utilizado para formatar e exibir diferentes tipos de dados na console:
using System;
namespace PolimorfismoEstatico
{
public class FormatadorDados
{
public void Exibir(int numero)
{
Console.WriteLine($"Valor inteiro formatado: {numero}");
}
public void Exibir(double valor)
{
Console.WriteLine($"Valor decimal formatado: {valor:F2}");
}
public void Exibir(string texto)
{
Console.WriteLine($"Texto formatado: [{texto}]");
}
static void Main()
{
var formatador = new FormatadorDados();
formatador.Exibir(42);
formatador.Exibir(3.14159);
formatador.Exibir("C# Polimorfismo");
}
}
}
Polimorfismo Dinâmico
O polimorfismo dinâmico ocorre em tempo de execução e é implementado principalmente através de herança, utilizando classes abstratas e métodos virtuais. Isso permite que classes derivadas forneçam implementações específicas para comportamentos definidos na classe base.
Classes Abstratas
Uma classe abstrata serve como um modelo base e não pode ser instanciada diretamente. Ela pode conter métodos abstratos (sem implementação) que as classes derivadas são obrigadas a implementar. Regras importantes sobre classes abstratas em C#:
- Não é possível criar uma instância de uma classe abstrata.
- Métodos abstratos só podem ser declarados dentro de classes abstratas.
- Uma classe abstrata não pode ser declarada como
sealed(fechada para herança).
O código a seguir ilustra o uso de uma classe abstrata Veiculo e suas implementações concretas:
using System;
namespace PolimorfismoDinamico
{
public abstract class Veiculo
{
public string Modelo { get; set; }
public Veiculo(string modelo)
{
Modelo = modelo;
}
public abstract void Acelerar();
}
public class Carro : Veiculo
{
public Carro(string modelo) : base(modelo) { }
public override void Acelerar()
{
Console.WriteLine($"O carro {Modelo} está acelerando suavemente.");
}
}
public class Moto : Veiculo
{
public Moto(string modelo) : base(modelo) { }
public override void Acelerar()
{
Console.WriteLine($"A moto {Modelo} está acelerando rapidamente.");
}
}
class Program
{
static void Main()
{
Veiculo meuCarro = new Carro("Sedan X");
Veiculo minhaMoto = new Moto("Esportiva Y");
meuCarro.Acelerar();
minhaMoto.Acelerar();
}
}
}
Métodos Virtuais
Diferente dos métodos abstratos, métodos virtuais possuem uma implementação padrão na classe base, mas podem ser sobrescritos nas classes derivadas usando a palavra-chave override. A resolução de qual método chamar é feita dinamicamente em tempo de execução.
No exemplo abaixo, uma classe base Notificador define um método virtual que é sobrescrito por diferentes canais de comunicação, permitindo o tratamento uniforme em uma coleção:
using System;
using System.Collections.Generic;
namespace PolimorfismoDinamico
{
public class Notificador
{
public virtual void Enviar(string mensagem)
{
Console.WriteLine("Preparando envio de notificação genérica...");
}
}
public class NotificadorEmail : Notificador
{
public override void Enviar(string mensagem)
{
Console.WriteLine($"Enviando E-mail: {mensagem}");
base.Enviar(mensagem);
}
}
public class NotificadorSMS : Notificador
{
public override void Enviar(string mensagem)
{
Console.WriteLine($"Enviando SMS: {mensagem}");
base.Enviar(mensagem);
}
}
public class NotificadorPush : Notificador
{
public override void Enviar(string mensagem)
{
Console.WriteLine($"Enviando Push: {mensagem}");
base.Enviar(mensagem);
}
}
class Program
{
static void Main()
{
var notificadores = new List<Notificador>
{
new NotificadorEmail(),
new NotificadorSMS(),
new NotificadorPush()
};
foreach (var notificador in notificadores)
{
notificador.Enviar("Sistema atualizado com sucesso!");
}
}
}
}
Por fim, um exemplo demonstrando o cálculo de rendimentos financeiros utilizando métodos virtuais para permitir que diferentes tipos de contas bancárias definam suas próprias regras de cálculo:
using System;
namespace PolimorfismoDinamico
{
public class ContaBancaria
{
protected decimal Saldo { get; set; }
public ContaBancaria(decimal saldoInicial)
{
Saldo = saldoInicial;
}
public virtual decimal CalcularRendimento()
{
Console.WriteLine("Calculando rendimento padrão...");
return 0;
}
}
public class ContaPoupanca : ContaBancaria
{
public ContaPoupanca(decimal saldo) : base(saldo) { }
public override decimal CalcularRendimento()
{
Console.WriteLine("Rendimento da Conta Poupança:");
return Saldo * 0.05m; // 5%
}
}
public class ContaInvestimento : ContaBancaria
{
public ContaInvestimento(decimal saldo) : base(saldo) { }
public override decimal CalcularRendimento()
{
Console.WriteLine("Rendimento da Conta Investimento:");
return Saldo * 0.12m; // 12%
}
}
public class GerenciadorContas
{
public void ExibirRendimento(ContaBancaria conta)
{
decimal rendimento = conta.CalcularRendimento();
Console.WriteLine($"Valor do rendimento: {rendimento:C}\n");
}
}
class Program
{
static void Main()
{
var gerenciador = new GerenciadorContas();
ContaBancaria poupanca = new ContaPoupanca(1000m);
ContaBancaria investimento = new ContaInvestimento(1000m);
gerenciador.ExibirRendimento(poupanca);
gerenciador.ExibirRendimento(investimento);
}
}
}