Supondo que quiséssemos promover novas implementações para métodos de uma classe base, na classe derivada, ou variações destes. Outra questão interessante oriunda do processo de herança é a compatibilidade de tipos de classe base de derivada. Algumas regras devem ser consideradas.
O tipo da classe base é compatível com o da classe derivada, mas somente podemos atribuir objetos do tipo derivada a um objeto do tipo base. O código abaixo exemplifica esse conceito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ... class Base { ... } class Derivada: Base { ... } class MinhaClasse { Static void Main() { Base B = new Base(); Derivada D = new Derivada() B = D; // Atribuição válida D = B; // Atribuição inválida } } ... |
Tendo isso claro, vejamos como se comportam métodos na atribuição de classe de tipos compatíveis. Um caso a ser resolvido é muito típico: é o de métodos com mesmo nome. Veja um exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | using System; public class Base { public void EscreveNome() { Console.WriteLine("Base"); } } public class Derivada1: Base { public void EscreveNome() { Console.WriteLine("Derivada1"); } } public class Derivada2: Base { public void EscreveNome() { Console.WriteLine("Derivada2"); } } public class MinhaClassePolimorfismo { static void Main() { Derivada1 minhaDerivada1 = new Derivada1(); Derivada2 minhaDerivada2 = new Derivada2(); Base minhaBase; string valor; Console.WriteLine("Digite um valor"); valor = Console.ReadLine(); if (valor == "1") minhaBase = minhaDerivada1; else minhaBase = minhaDerivada2; minhaDerivada1.EscreveNome(); minhaDerivada2.EscreveNome(); minhaBase.EscreveNome(); } } |
Compile e veja a saída no console:
O código compila, mas gera alguns avisos. Isso ocorre porque implementamos de maneira forçada método de mesmo nome da classe base na classe derivada. O jeito fácil de resolver o problema é preceder a declaração do método com o operados “new”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | using System; public class Base { public void EscreveNome() { Console.WriteLine("Base"); } } public class Derivada1: Base { new public void EscreveNome() { Console.WriteLine("Derivada1"); } } public class Derivada2: Base { new public void EscreveNome() { Console.WriteLine("Derivada2"); } } public class MinhaClassePolimorfismo { static void Main() { Derivada1 minhaDerivada1 = new Derivada1(); Derivada2 minhaDerivada2 = new Derivada2(); Base minhaBase; string valor; Console.WriteLine("Digite um valor"); valor = Console.ReadLine(); if (valor == "1") minhaBase = minhaDerivada1; else minhaBase = minhaDerivada2; minhaDerivada1.EscreveNome(); minhaDerivada2.EscreveNome(); minhaBase.EscreveNome(); } } |
Ao compilar os avisos não são emitidos:
Mas ainda temos um problema. Repare que tanto no primeiro resultado como neste último, apesar do valor informado ser 1, a última mensagem impressa se refere à classe base, apesar de termos atribuído à variável “minhaBase” a variável “minhaDerivada1”. Por que então não foi impressa a mensagem “Derivada1” ao invés de “Base”?
Tudo bem, classe base é base e ponto final, certo? Quase. A característica de polimorfismo nos permite “muitas formas” como já foi dito anteriormente. Através de um tipo ancestral, podemos obter diferentes comportamentos condicionalmente. Como fazer para que isso aconteça?
Para redefinir um método na classe derivada de forma que ele seja entendido por um objeto de classe base, estes métodos devem ser “virtuais”. A palavra chave “virtual” deve ser utilizada na declaração do método na classe base e a palavra chave “override” na declaração do método de mesmo nome da classe derivada. Veja a implementação de código a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | using System; public class Base { public virtual void EscreveNome() { Console.WriteLine("Base"); } } public class Derivada1: Base { public override void EscreveNome() { Console.WriteLine("Derivada1"); } } public class Derivada2: Base { <strong>public override void EscreveNome()</strong> { Console.WriteLine("Derivada2"); } } public class MinhaClassePolimorfismo { static void Main() { Derivada1 minhaDerivada1 = new Derivada1(); Derivada2 minhaDerivada2 = new Derivada2(); Base minhaBase; string valor; Console.WriteLine("Digite um valor"); valor = Console.ReadLine(); if (valor == "1") minhaBase = minhaDerivada1; else minhaBase = minhaDerivada2; minhaDerivada1.EscreveNome(); minhaDerivada2.EscreveNome(); minhaBase.EscreveNome(); } } |
Veja a saída após compilado o programa:
Note que tornamos a saída condicional. Dependendo do valor digitado, a atribuição muda e o objeto de classe base entende a implementação do objeto de classe derivada. Sim, pois uma classe derivada também é uma classe base. Se fizermos uma analogia aos conceitos apresentados no início deste capítulo, poderemos chamar a classe base de “Animal” e as classes derivadas de “Cachorro” e “Gato”, por exemplo. Adaptando os conceitos aos exemplos, podemos dizer que o cachorro e gato são animais. Veja a o código implementado sob esta ótica:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | using System; public class Animal { public virtual void EscreveNome() { Console.WriteLine("Sou um Animal"); } } public class Cachorro: Animal { public override void EscreveNome() { Console.WriteLine("Sou um Cachorro"); } } public class Gato: Animal { public override void EscreveNome() { Console.WriteLine("Sou um Gato"); } } public class minhaClassePolimorfismo { static void Main() { Cachorro meuCachorro = new Cachorro(); Gato meuGato = new Gato(); Animal meuAnimal; string valor; Console.WriteLine("Digite um valor"); valor = Console.ReadLine(); if (valor == "1") meuAnimal = meuCachorro; else meuAnimal = meuGato; meuCachorro.EscreveNome(); meuGato.EscreveNome(); meuAnimal.EscreveNome(); } } |
Veja a saída no console:
Este exemplo deixa mais claro o conceito de implementação, uso e entendimento de polimorfismo.
Veremos agora um uso mais prático de métodos virtuais. Uma das implementações sobrecarregadas no método Console.WriteLine recebe um object como parâmetro. Dessa forma, podemos passar qualquer objeto já que qualquer tipo herda de object e é automaticamente compatível com este. Quando se passa um object para este método, ele automaticamente faz uma chamada ao método “ToString” da classe object. O método “ToString” é declarado como “virtual” e portanto pode ser sobrescrito usando a palavra chave “override”. Veja um exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using System; class Polegadas { decimal valorMetros; public Polegadas(decimal v) { valorMetros = v * 100/2.54M; } public void ConverteMetros(decimal v) { valorMetros = v * 100/2.54M; } public override string ToString() { return "O valor em metros é: " + valorMetros.ToString(); } } class MinhaClasse { static void Main() { Polegadas p = new Polegadas(100M); Console.WriteLine(p.ToString()); } } |
Veja a saída no console:
Se mesmo com uma nova implementação (“override”) quisermos ter acesso ao método da classe base, é possível faze-lo através da palavra reservada “base”, porém, seu uso é diferenciado dos construtores. Veja um exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | using System; public class Animal { public virtual void EscreveNome() { Console.WriteLine("Sou um Animal"); } } public class Cachorro: Animal { public override void EscreveNome() { Console.WriteLine("Sou um Cachorro"); } } public class Gato: Animal { // redefine o método EscreveNome public override void EscreveNome() { Console.WriteLine("Sou um Gato"); //chama o método EscreveNome da classe base Base.EscreveNome(); } } |










