C# - Herança : DownCasting e UpCasting (Operador is e as)
Hoje vamos recordar os conceitos de downcasting e upcasting relacionados com a herança na linguagem C#.

Na herança temos o conceito de classe base ou superclasse e classe filha ou subclasse que é a classe que herda da classe base ou superclasse.

A classe filha ou subclasse é um tipo especializado da classe pai ou superclasse que é um tipo geral.

Vamos considerar uma classe Forma como sendo a classe pai e a classe Circulo como sendo a classe Filha definidas com o código abaixo:

 Classe Forma -> classe pai -> classe geral  Classe Circulo -> classe filha -> classe especializada
    public class Forma
    {
        public virtual void Desenhar()
        {
            Console.WriteLine("Forma");
        }
    }

 

public class Circulo : Forma
{
        public override void Desenhar()
        {
            Console.WriteLine("Desenhar Circulo");
        }
        public void PintarCirculo()
        {
            Console.WriteLine("Pintando o círculo");
        }
}

Neste contexto o que vem a ser UpCasting e DownCasting  ?

1- UpCasting

A operação de UpCasting converte um objeto de um tipo especializado (classe filha) para um tipo mais geral (classe base)

Podemos entender o upcasting como a atribuição de objeto de uma classe filha para uma referência de uma classe base.

Esta operação é implícita e não é preciso ser feita de forma explicita, sendo sempre possível de ser realizada.

static void Main(string[] args)
{
     Forma circulo = new Circulo();  
     
circulo.Desenhar();
}

Podemos ainda fazer assim:

private static void UpCasting()
{
     Circulo circulo = new Circulo();
    Forma f = circulo;

  
 Console.WriteLine(f == circulo);

    f.Desenhar();

}

Note que embora f e circulo se refiram ao mesmo objeto , f tem uma visão mais restrita deste objeto pois pode acessar somente o método Desenhar.

A variável f é do tipo Forma mesmo se referindo a um objeto do tipo Circulo e por isso não acessa o método PintarCirculo().

Para poder acessar o método PintarCirculo() da classe Circulo temos que fazer o DownCast de Forma para Circulo.

2- DownCasting

A operação de DownCasting converte um objeto de um tipo geral (classe base) para um tipo mais especializado (classe filha).

Podemos entender o downcasting como a atribuição de um objeto da classe base para um objeto da classe derivada.

A operação de downcast deve ser feita de forma explícita pois pode falhar e lançar uma exceção, ou seja, é uma operação que nem sempre é possível de ser realizada.

        private static void DownCasting()
        {
             Circulo circulo = new Circulo();
             Forma f = circulo;  //upcasting -> implicita
             Circulo c = (Circulo)f;  //downcast -> explicita
            c.PintarCirculo();
           c.Desenhar();

           Console.WriteLine(c == f);
           Console.WriteLine(c == circulo);
      }

Foi feito o downcast da variável f que é do tipo Forma para Circulo.

Note que a variável c referencia tanto a variável f como a variavel circulo ou seja temos a mesma referência.

Assim a instância c pode acessar o método PintarCirculo() e também Desenhar().

Se o downcast falhar vai ocorrer uma exceção do tipo InvalidCastException.

Usando o operador as

O operador as é usado para executar a conversão entre tipos de referência compatíveis ou tipos anuláveis. Este operador retorna o objeto quando eles são compatíveis com o tipo especificado e retorna nulo se a conversão não for possível em vez de gerar uma exceção.

Assim ele realiza uma operação de downcast mas ao invés de lançar uma exceção avalia o resultado para null.

  private static void DownCasting_As()
  {
       Forma f = new Forma();
       Circulo c = f as Circulo;    //c será igual a null
       if (c != null)
      {
          c.PintarCirculo();
       }
       else
       {
         Console.WriteLine("Operação de downcast inválida (as)");
       }
    }

Usando o operador as para realizar o downcast vai retornar null se a operação falhar.

Assim podemos verificar se o resultado for diferente de null antes de tentar usar o objeto.

Usando o operador is

O operador is é usado para verificar se o tipo de um objeto é compatível com o tipo especificado ou não; ele retorna true se o objeto especificado for do mesmo tipo, caso contrário, retorne false. Ele também retorna false para objetos nulos.

Assim podemos usar o operador is para verificar se uma conversão de referência será bem sucedida ou não.

  private static void DownCasting_Is()
  {
        Circulo circulo = new Circulo();
        Forma f = circulo;      //upcasting -> implicita
        if (f is Circulo)      //verifica se o downcast é possível
       {
           ((Circulo)f).PintarCirculo();
       }
       else
       {
         Console.WriteLine("Operação de downcast inválida (is)");
       }
    }

Observe que verificamos se o tipo f é compatível com o tipo Circulo, e, se for, vai retornar true , e, então poderemos acessar o método PintarCirculo().  Como a operação é valida o método PintarCirculo será executado.

Apenas para concluir vamos listar as diferenças entre os operadores is e as :

- O operador is é usado para verificar se o tipo de um objeto (em tempo de execução) é compatível com o tipo especificado ou não, enquanto o operador as é usado para executar a conversão entre tipos de referência compatíveis ou tipos anuláveis.

- O operador is é do tipo booleano, enquanto o operador as não é do tipo booleano.

- O operador is retorna true se o objeto especificado for do mesmo tipo, enquanto o operador as retorna o objeto quando eles são compatíveis com o tipo especificado.

- O operador is retorna false se o objeto especificado não for do mesmo tipo, enquanto operador as retorna null se a conversão não for possível.

- O operador is é usado apenas para conversões de referência, boxe e unboxing, enquanto o operador as é usado apenas para conversões anuláveis, de referência e boxing

Pegue o código do projeto aqui:  UpDownCast.zip

Mas Deus escolheu as coisas loucas deste mundo para confundir as sábias; e Deus escolheu as coisas fracas deste mundo para confundir as fortes;
E Deus escolheu as coisas vis deste mundo, e as desprezíveis, e as que não são, para aniquilar as que são;
Para que nenhuma carne se glorie perante ele.

1 Coríntios 1:27-29

"Mas Deus escolheu as coisas loucas deste mundo para confundir as sábias; e Deus escolheu as coisas fracas deste mundo para confundir as fortes;
E Deus escolheu as coisas vis deste mundo, e as desprezíveis, e as que não são, para aniquilar as que são;
Para que nenhuma carne se glorie perante ele."

1 Coríntios 1:27-29

Referências:


José Carlos Macoratti