C # - OOP - Herança (mais um pouco sobre ela)


Vamos rever alguns conceitos sobre herança na linguagem C#.

Se você acha que sabe tudo sobre o assunto pode sair do artigo senão acompanhe...

Herança

  1. Herança é um conceito chave usado na programação orientada a objetos para descrever uma relação entre as classes;
  2. Por meio da herança uma classe copia ou herda todas as propriedades, atributos e métodos de uma outra classe, podendo assim estender sua funcionalidade;
  3. A classe que cede os membros para a outra classe é chamada superclasse, classe pai ou classe base;
  4. A classe que herda os membros da outra classe é chamada subclasse ou classe derivada;
  5. A herança permite a reutilização de código e especifica um relacionamento de especialização/generalização do tipo "é um";
  6. Embora seja recurso poderoso a herança deve ser usada somente quando necessário pois pode levar a um acoplamento forte entre as classes do seu projeto dificultando a sua reusabilidade e a manutenção;
  7. Existe a herança de implementação onde uma classe derivada herda da classe base e a herança de interface onde uma classe pode herda de uma interface, neste caso apenas as assinaturas dos métodos são herdados sendo que os métodos devem ser implementados;

Sintaxe:

Classe Base = A (classe Pai)

        class A
        {
              public int a;
              public A() { Console.WriteLine("construtor A"); }
              public void M(string nome) { Console.WriteLine("sou o " + nome); }
        }

SubClasse = B (classe filha, classe derivada)

B : A (herda de A, estende A)

        class B : A
        {
               int b;
               public B() { Console.WriteLine("construtor B"); }
               public void J() { Console.WriteLine("sou J "); }
        }
  1. A subclasse B herda a variável a e o método M(string nome) e inclui a variável b e o método J();
  2. O construtor sem parâmetros A() da classe base A é herdado;
  3. O construtor da subclasse sempre chama o construtor da classe base e depois executa o seu próprio código;
  4. Uma classe somente pode herdar de uma classe; Não existe herança múltipla;
  5. Uma classe pode implementar múltiplas interfaces;
  6. Toda a classe é derivada da classe Object; A classe Object não deriva de nenhuma classe; Ela é a classe Base (Pai) de todas as classes;
  7. Uma classe somente pode herdar de outra classe; Uma classe não pode herdar de uma struct;
  8. Os métodos herdados de uma classe Base podem ser sobrescritos, para isso eles devem ser definidos com a palavra-chave virtual;
  9. Para sobrepor um método da classe base use a palavra-chave override;
  10. O modificador de acesso protected torna a variável de uma classe base somente acessível as suas classes derivadas; (outras classes não acessam a variável)
Métodos herdados de System.Object
Equals – Testa se dois objetos são iguais
GetHashCode – Retorna o código de hash para o objeto
GetType – Retorna informação sobre a classe do objeto
ToString – Retorna o objeto como string

Vamos executar o código abaixo para ver o resultado:

    class Program
    {
        static void Main(string[] args)
        {
            B b = new B();
            b.M("Macoratti");
            Console.WriteLine("Executei Main");
            Console.ReadKey();
        }
    }
 

Observe que antes de executar o seu construtor, o construtor da classe A foi executado.

Executando o construtor com parâmetros da classe base

Vamos definir um construtor com parâmetros na classe base A e mostrar como executá-lo na classe derivada.

Classe base A:

        class A
        {
               public int a;

               //construtor da classe base
               public A() { Console.WriteLine("construtor A"); }

            
  //construtor com parâmetros da classe base
               public A(string nome) { Console.WriteLine("Sou o construtor com parâmetros da classe A " + nome); }
       
               public void M(string nome) { Console.WriteLine("sou o " + nome); }
        }

classe derivada B:

        class B : A
        {
                   int b;
                  
//construtor da classe derivada
                    public B() { Console.WriteLine("construtor B"); }

 
                 //declara o construtor da classe derivada herdando da classe base
                   public B(string nome) : base(nome) { }
       
                   public void J() { Console.WriteLine("sou J "); }
        }

Executando o construtor da classe base:

  class Program
    {
        static void Main(string[] args)
        {
            B b = new B();
            //executa o construtor com parâmetros da classe base
            B b1 = new B("Macoratti");

            b.M("Macoratti");
            Console.WriteLine("Executei Main");
            Console.ReadKey();
        }
    }
 

Verificando e alterando o tipo de um objeto

O operador is é usado para verificar se um objeto é de uma determinada classe ou descendente desta classe, retornando verdadeiro ou falso;

O operador as é utilizado para alterar o tipo da referência de um objeto. Quando não é possível a alteração é retornado null.

Vamos definir mais uma classe chamada C que herda da classe B conforme o código a seguir:

    class C :  B
    {
        public int c;
    
   //construtor da classe base
        public C() { Console.WriteLine("construtor C"); }
      
 //método X() da classe C
        public void X(string nome) { Console.WriteLine("Classe C: sou o " + nome); }
    }

Temos então que C herda de B que herda de A logo C herda de A.

Quando instanciamos um objeto da classe C poderemos ver no Intellisense que ele herda :

Vamos utilizar os operadores is e as para verificar e alterar o tipo de um objeto executando o código abaixo:

  class Program
    {
       
             C c = new C();
            //verificando se um objeto é de uma determinada classe
            //ou descendente desta classe(operador is)
            A a = new A();
         
  if (a is A) { Console.WriteLine(" a é um tipo " + a.GetType()); }

            a = new B();
           
if (a is B) { Console.WriteLine(" a é um tipo " + a.GetType()); }
           
            a = new C();
          
 if (a is C) { Console.WriteLine(" a é um tipo " + a.GetType()); }

            /
/ alterando o tipo da referência do objeto 'a' para classe A
            // e usando o método M (operador as)

           
(a as A).M("Macoratti");
          
 // alterando o tipo da referência do objeto 'a' para classe B
            // e usando o método J (operador as)

           
(a as B).J();
           
            Console.WriteLine("Executei Main");
            Console.ReadKey();
    }
 

Sobrescrevendo um método da classe base

Para sobrescrever um método da classe base o mesmo deve ser declarado com a palavra-chave virtual na classe base.

Na classe derivada o método sobrescrito deve usar a palavra-chave override.

Definindo o método virtual classe base B:

        class B : A
        {
                   int b;
                   //construtor da classe derivada
                    public B() { Console.WriteLine("construtor B"); }

                   //declara o construtor da classe derivada herdando da classe base
                   public B(string nome) : base(nome) { }

                   //método virtual
                   public virtual int calculaB(int a, int b) { return a + b; }
       
                   public void J() { Console.WriteLine("sou J "); }
        }

Sobrescrevendo o método calculaB() na classe derivada C usando a palavra-chave override:

    class C :  B
    {
        public int c;
        //construtor da classe base
        public C() { Console.WriteLine("construtor C"); }
        //método X() da classe C
        public void X(string nome) { Console.WriteLine("Classe C: sou o " + nome); }

       //sobrescrevendo o método CalculaB()
        public override int calculaB(int a, int b)
        {
            return a * b;
        }

    }

Executando o código para verificação:

  class Program
    {
        static void Main(string[] args)
        {
   
        C c = new C();
            Console.WriteLine(" 5 * 5 = " + c.calculaB(5, 5));
   

            Console.WriteLine("Executei Main");
            Console.ReadKey();
        }
    }
 

Note que o método calculaB(5,5) retornou o valor da multiplicação definido no método sobrescrito na classe C e não o método da classe base B.

Observe que como C herda de B que herda A foi executado o construtor da classe A e da classe B antes do construtor da classe C.

Escondendo métodos da classe base

Se um método da classe derivada possuir a mesma assinatura de um método da classe base mas não for definido com a palavra-chave override ele irá esconder o método da classe base.

Esse comportamento pode ser o que você realmente deseja ter e a classe será compilada sem erros mas o compilador irá emitir um aviso de alerta perguntando se este é o comportamento esperado.

Para esconder um método da classe base sem emitir o aviso do compilador você deve usar a palavra-chave new no método da classe derivada.

Classe Base = A (classe Pai)

        class A
        {
              public int a;
              public A() { Console.WriteLine("construtor A"); }
              public void M(string nome) { Console.WriteLine("sou o " + nome); }
            
  public void Y() { Console.WriteLine("sou o método Y da classe base A"); }
        }

SubClasse = B (classe filha, classe derivada)

B : A (herda de A, estende A)

        class B : A
        {
               int b;
               public B() { Console.WriteLine("construtor B"); }
               public void J() { Console.WriteLine("sou J "); }
               public new void Y() { Console.WriteLine("sou o método Y da classe derivada B"); }
        }

A classe derivada B possui o método Y() que irá esconder o método Y() da classe base A. Nesta caso estamos usando a palavra-chave new no método da classe derivada.

Se a palavra new não for usada a classe será compilada mas o aviso abaixo será emitido pelo compilador:

Warning 4 'C_heranca1.B.Y()' hides inherited member 'C_heranca1.A.Y()'. Use the new keyword if hiding was intended.

Aguarde mais artigos sobre conceitos da programação orientada a objetos.

"Ele, porém, respondendo, disse: Toda a planta, que meu Pai Celestial não plantou, será arrancada." Mateus 15:13

Referências:


José Carlos Macoratti