Polimorfismo em C#: Implementações Estáticas e Dinâmicas

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);
        }
    }
}

Tags: CSharp Polimorfismo programacao-orientada-a-objetos sobrecarga-metodos classes-abstratas

Publicado em 7-21 18:43