sábado, maio 13, 2006

New como modificador de métodos em C#

Apesar de já ter ouvido sobre o assunto, enquanto estava a escrever algumas linhas de código, fiquei na dúvida sobre o que o modificador new fazia quando aplicado a um método, então resolvi fazer uma pequena pesquisa e tirar as minhas conclusões.
Em C# existem 6 modificadores de métodos: static, extern, abstract, virtual, override e new. Passando rapidamente pelos três primeiros, static tem o objectivo de definir um método de tipo e não de instância, extern apesar de nunca o ter usado serve para indicar que um método é definido externamente por exemplo numa DLL e abstract tem como objectivo não dar implementação ao método, obrigando que a derivada da classe lhe dê implementação, para se ter métodos abstract é necessário que a classe também seja marcada como abstract (a classe é encarada como uma interface).
Os atributos virtual e override são os que considero mais importantes pois são os que efectivamente possibilitam o polimorfismo, por isso darei um pouco mais de atenção mais a frente. Analisando o código seguinte fica a pergunta, qual a diferença entre o funcionamento dos dois métodos?
     
public class Base { public void Met1() { Console.WriteLine("Base.Met1"); } public void Met2() { Console.WriteLine("Base.Met2"); } } public class Derivation1 : Base { public void Met1() { Console.WriteLine("Derivation1.Met1"); } public new void Met2() { Console.WriteLine("Derivation1.Met2"); }
public static void Main() { Base bas = new Derivation1(); Derivation1 der1 = new Derivation1(); bas.Met1(); der1.Met1(); Console.WriteLine(); bas.Met2(); der1.Met2(); Console.ReadLine(); } }
A resposta é nenhuma, a referência para base como esta tem implementação dos métodos irá chamar os seus métodos e a referência para a classe derivada como seria espectável irá chamar a sua implementação.
Base.Met1
Derivation1.Met1
Base.Met2
Derivation1.Met2
Então para que serve o new? O seu objectivo é simplesmente forçar a ideia que se está a esconder um método não virtual da classe base, o que na prática simplesmente vai evitar um warning do tipo “'Derivation1.Met1()' hides inherited member 'Base.Met1()'. Use the new keyword if hiding was intended”. Creio que o motivo do new existir em C# vem dele não existir em C++, ou seja, como em C++ o programador inadvertidamente poderia esconder métodos e só o iria detectar em runtime porque o comportamento não seria o esperado, em C# pelo menos tem um warning em compile time.
Complicando mais um pouco, o código seguinte mostra a interacção entre o virtual, o override e o new.
public class Base{
  public virtual void Met1() { Console.WriteLine("Base.Met1"); }
  public virtual void Met2() { Console.WriteLine("Base.Met2"); }
  public virtual void Met3() { Console.WriteLine("Base.Met3"); }  
}  
public class Derivation1 : Base { public override void Met1() { Console.WriteLine("Derivation1.Met1"); } public new virtual void Met2(){Console.WriteLine("Derivation1.Met2");} public override void Met3() { Console.WriteLine("Derivation1.Met3"); }
public class Derivation2 : Derivation1 { public override void Met1() { Console.WriteLine("Derivation2.Met1"); } public override void Met2() {Console.WriteLine("Derivation2.Met2");} public new virtual void Met3(){Console.WriteLine("Derivation2.Met3");}
public static void Main() { Base b = new Derivation2(); Derivation1 d1 = new Derivation2(); Derivation2 d2 = new Derivation2();
b.Met1(); d1.Met1(); d2.Met1(); Console.WriteLine(); b.Met2(); d1.Met2(); d2.Met2(); Console.WriteLine();
b.Met3(); d1.Met3(); d2.Met3(); Console.ReadLine(); } }
Na chamada ao Met1, apesar de ter diferentes referências, como estou a construir um objecto Derivation2 desde que os métodos sejam polimórficos vou chamar o Met1 de Derivation2. Na chamada a Met2, tendo como referência a Base, o que vai acontecer é que se vai chamar o Met2 da Base, isto porque o new na Derivation1 está a criar um novo método com a mesma assinatura, logo está a isolar as suas classes base das suas derivadas. O raciocínio para o Met3 será o mesmo, o output será o seguinte:
Derivation2.Met1
Derivation2.Met1
Derivation2.Met1

Base.Met2
Derivation2.Met2
Derivation2.Met2
Derivation1.Met3
Derivation1.Met3
Derivation2.Met3

2 comentários:

Anónimo disse...

Excelente artigo, simples, porém abordando um assunto relativamente complexo. Parabéns!

Ricardo S. Lazarin
PR - Brasil (Brazil)

Anónimo disse...

Espectacular artigo. Ajudou-me bastante. Obrigado