C# -  Compreendendo Virtual , Override e new - Conceitos


Vou começar este artigo falando de polimorfismo, um importante conceito das linguagens que seguem o paradigma da orientação a objetos.

Polimorfismo significa muitas formas; na orientação a objetos você pode enviar uma mesma mensagem para diferentes objetos e fazê-los responder da maneira correta.

 

Você pode enviar uma mensagem mover para cada objeto semelhante a um veiculo e cada um vai se comportar de maneira diferente para atender a sua solicitação.Quando uma mesma mensagem pode ser processada de diferentes formas temos um exemplo de polimorfismo.

 

"Polimorfismo é o princípio pelo qual duas ou mais classes derivadas de uma mesma superclasse podem invocar métodos que têm a mesma identificação (assinatura) mas comportamentos distintos, especializados para cada classe derivada, usando para tanto uma referência a um objeto do tipo da superclasse"
 

Usando polimorfismo podemos :

  1. Invocar métodos da classe derivada através da classe base em tempo de execução;
  2. Permitir que classes forneçam diferentes implementações de métodos que são chamados com o mesmo nome;

Existem dois tipos básicos de polimorfismo:

  1. Polimorfismo em tempo de compilação (Overloading/Sobrecarga);
  2. Polimorfismo em tempo de execução (Overriding/Sobrescrita);

O Polimorfismo é um conceito das linguagens OOP que incluem a sobrecarga e sobrescrita de métodos.

 

Assim as palavras chaves Virtual e Override são usadas neste contexto para sobrescrever/substituir um método e a palavra chave new é usada para ocultar um método.

 

Vamos dar uma espiada com mais atenção nas palavras Overide, Virtual e new da linguagem C# e entender melhor o significado de cada uma delas. 

Herança de classe

Considere abaixo a hierarquia de classe A, B e C, onde :

  1. A é a super classe ou classe base;

  2. A classe B é derivada da classe A;

  3. A classe C é derivada da classe C;

using System;
namespace Polimorfismo
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Teste(); // saída --> "A::Teste()"
            B b = new B();
            b.Teste(); // saída --> "A::Teste()"
            C c = new C();
            c.Teste(); // saída --> "A::Teste()"
            Console.ReadKey();
        }
    }
    public class A
    {
        public void Teste()
        { 
            Console.WriteLine("A::Teste()"); 
        }
    }
    public class B : A { }
    public class C : B { }
}

Vamos supor agora que todas essas classes possuam o método Teste() conforme código abaixo:

using System;

namespace Polimorfismo1
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            B b = new B();
            C c = new C();

            a.Teste();    // saida --> "A::Teste()"
            b.Teste();    // saida --> "B::Teste()"
            c.Teste();    // saida --> "C::Teste()"

            a = new B();
            a.Teste();    // saida --> "A::Teste()"

            b = new C();
            b.Teste();    // saida --> "B::Teste()"

            Console.ReadKey();
        }
    }

    public class A
    {
        public void Teste() { Console.WriteLine("A::Teste()"); }
    }

    public class B : A
    {
        public void Teste() { Console.WriteLine("B::Teste()"); }
    }

    public class C : B
    {
        public void Teste() { Console.WriteLine("C::Teste()"); }
    }

}

 

Quando executarmos o programa acima ele será executando exibindo a saída conforme mostrada mas também veremos dois alertas na janela Error List:

Vemos que o compilador gera os alertas pois a linguagem C# também suporta a ocultação de método.

Para ocultar/esconder o método da classe base da classe derivada basta declarar o método da classe derivada com a palavra chave new.

Vamos então reescrever o código conforme a seguir:

using System;
namespace Polimorfismo2
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            B b = new B();
            C c = new C();
            a.Teste();     // saída --> "A::Teste()"
            b.Teste();     // saída --> "B::Teste()"
            c.Teste();     // saída --> "C::Teste()"
            a = new B();
            a.Teste();     // saída --> "A::Teste()"
            b = new C();
            b.Teste();     // saída --> "B::Teste()"
            Console.ReadKey();
        }
    }
    public class A
    {
        public void Teste() { Console.WriteLine("A::Teste()"); }
    }
    public class B : A
    {
        public new void Teste() { Console.WriteLine("B::Teste()"); }
    }
    public class C : B
    {
        public new void Teste() { Console.WriteLine("C::Teste()"); }
    }
}

Analisando o código acima com certeza você deve estar surpreso com o resultado obtido pelo trecho de código abaixo:

   a = new B();
   a.Teste();     // saída --> "A::Teste()"
   b = new C();
   b.Teste();     // saída --> "B::Teste()"

Você deveria estar esperando que a saída fosse:

// saída --> "B::Teste()"

// saída --> "C::Teste()"

Afinal os objetos a e b são referenciados pelos objetos B e C.

Para obter esse resultado temos que sobrescrever o método Teste() da classe base na classe derivada declarando o método da classe base como virtual e o método da classe derivada como override conforme mostra o código abaixo:

using System;
namespace Polimorfismo3
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            B b = new B();
            C c = new C();
            a.Teste();      // saida --> "A::Teste()"
            b.Teste();      // saida --> "B::Teste()"
            c.Teste();      // saida --> "C::Teste()"
            //---------------------------------
            a = new B();
            a.Teste();      // saida --> "B::Teste()"
            b = new C();
            b.Teste();      // saida --> "C::Teste()"
            Console.ReadKey();
        }
    }
    class A
    {
        public virtual void Teste() { Console.WriteLine("A::Teste()"); }
    }
    class B : A
    {
        public override void Teste() { Console.WriteLine("B::Teste()"); }
    }
    class C : B
    {
        public override void Teste() { Console.WriteLine("C::Teste()"); }
    }
}

Agora sim temos o resultado esperado pois usando as palavras chave virtual e override ocultamos o método Teste() da classe base nas classes derivadas.

Você também pode misturar a ocultação de métodos com a sobreposição de métodos usando as palavra chaves virtual e new visto que os método da classe derivada podem ser virtual e new ao mesmo tempo.

Este procedimento é necessário quando você quer sobrescrever o método da classe derivada em um próximo nível conforme mostrado a seguir:

using System;
namespace Polimorfismo4
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            B b = new B();
            C c = new C();
            a.Teste(); 	// saida --> "A::Teste()"
            b.Teste(); 	// saida --> "B::Teste()"
            c.Teste(); 	// saida --> "C::Teste()"
            a = new B();
            a.Teste(); 	// saida --> "A::Teste()"
            b = new C();
            b.Teste(); 	// saida --> "C::Teste()"
            Console.ReadKey();
        }
    }
    class A
    {
        public void Teste() { Console.WriteLine("A::Teste()"); }
    }
    class B : A
    {
        public new virtual void Teste() { Console.WriteLine("B::Teste()"); }
    }
    class C : B
    {
        public override void Teste() { Console.WriteLine("C::Teste()"); }
    }
}

Concluindo :

  1. A palavra chave virtual é usada para modificar um método, propriedade, indexador ou evento declarado na classe base e permitir que eles sejam sobrescritos na classe derivada;
  2. A palavra chave override é usada para estender ou modificar um método  virtual/abstrato, propriedade, indexador ou evento da classe base na classe derivada;
  3. A palavra chave new é usada para esconder um método, propriedade, indexador ou evento da classe base na classe derivada;

E estamos conversados.

Pegue o projeto completo aqui:  Polimorfismo.zip

Romanos 7:7 Que diremos pois? É a lei pecado? De modo nenhum. Contudo, eu não conheci o pecado senão pela lei; porque eu não conheceria a concupiscência, se a lei não dissesse: Não cobiçarás.

Romanos 7:8 Mas o pecado, tomando ocasião, pelo mandamento operou em mim toda espécie de concupiscência; porquanto onde não há lei está morto o pecado.

Veja os Destaques e novidades do SUPER DVD Visual Basic 2013 (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??


    Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

 

Referências:


José Carlos Macoratti