My kemenworld…

To mock or not to mock…

Clases y herencia en C# (new, virtual y override)

Posted by kementeus en agosto 28, 2007

C# es un lenguaje relativamente nuevo, se caracteriza por ser un lenguaje «managed» de objetos orientado a componentes fuertemente tipificado (esa es mi definición personal de C#). Muchos de los que aprendimos o por lo menos «creímos aprender» C# hemos pasado ya por diferentes paradigmas de programación y por uno que otro lenguaje orientado a objetos. Quizás por esto es cuando encontramos conceptos como «herencia» damos por sentado varios conceptos que aprendimos de lenguajes de objetos anteriores, claro, muchos de ellos son similares sino iguales y casi que repetimos las líneas del primer libro de programación orientada a objetos que leímos.

Cuando pasaba el tiempo y aprendía los conceptos «básicos» de C# me topé con muchos de ellos, pero me sorprendió encontrarme con algunos totalmente nuevos. Espero en esta serie de posts que estaré publicando aclarar algunas dudas o simplemente «desahogarme» de algunas cosas que con el pasar el tiempo aprendí de C#. Comenzaremos por algo sencillo, los operadores virtual, new y override.

Como recordaran en C# la clase hija heredará los métodos de su clase padre y podrá hacer uso de ellos siempre y cuando los modificadores de acceso (public, protected) se lo permitan. Veamos un simple ejemplo:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4:  
   5: namespace HerenciaConstructores
   6: {
   7:     public class A 
   8:     {
   9:         public void Metodo()
  10:         {
  11:             Console.WriteLine("hola metodo en A");
  12:         }
  13:     }
  14:  
  15:     public class B : A 
  16:     {   
  17:     }
  18:  
  19:     class Program
  20:     {
  21:         static void Main(string[] args)
  22:         {
  23:             A a = new A();
  24:             B b = new B();
  25:  
  26:             a.Metodo();
  27:             b.Metodo();
  28:         }
  29:     }
  30: }

Bien, como es de esperar, al ejecutar la aplicación nos responderá:

hola metodo en A
hola metodo en A

Esto demuestra que los métodos en C# son virtuales. Bien, ahora queremos implementar un método en B, hagámoslo sencillo, modifiquemos la clase A:

   1: public class B : A 
   2: {
   3:     public void Metodo()
   4:     {
   5:         Console.WriteLine("hola metodo en B..");
   6:     }
   7: }

Al ejecutar ahora el programa ahora encontraremos el mensaje:

hola metodo en A
hola metodo en B

Pero si observamos bien el compilador no nos dio un error sino una «advertencia»

Warning	'HerenciaConstructores.B.Metodo()' hides inherited member 'HerenciaConstructores.A.Metodo()'.
Use the new keyword if hiding was intended.

Bien, hagamos lo que dice el compilador y agreguemos «new» al método en cuestión, así dejaremos totalmente feliz al compilador, modifiquemos el método B.Metodo() para que se vea de la siguiente manera:

   1: public class B : A 
   2: {
   3:     public new void Metodo()
   4:     {
   5:         Console.WriteLine("hola metodo en B..");
   6:     }
   7: }

Bien, con esto sigue produciendo la misma salida que el ejemplo anterior pero sin la «Warning». El modificador new indica al compilador que esta «versión» del método es una «nueva» versión del mismo. Por lo tanto si hay una versión «anterior» prefiera esta sobre ella. A este mecanismo se le llama «hiding» ya que estamos «ocultando» la versión anterior del método padre (OJO, uso la palabra versión indicando la implementación, no tiene nada que ver con el versioning en la .Net framework).

Bien, aclarado esto veamos ahora que hace «override», cambiemos new por override en el método B.Metodo(), el compilador nos dirá que tenemos problemas ya que estamos tratando de «override» un método que no ha sido marcado como abstract, virtual u override en la clase padre. Hagamos feliz al compilador y marquemos el método A.Metodo() como virtual. De esta manera ahora nuestras clases quedaran algo así:

   1: public class A 
   2: {
   3:     public virtual void Metodo()
   4:     {
   5:         Console.WriteLine("hola metodo en A");
   6:     }
   7: }
   8:  
   9: public class B : A 
  10: {
  11:     public override void Metodo()
  12:     {
  13:         Console.WriteLine("hola metodo en B..");
  14:     }
  15: }

Bien, como es de esperar la salida de este programa es igual a la salida del anterior, hasta aquí podriamos confundirnos y decir que override tiene un efecto muy similar al de new, con la diferencia que para que funcione override hay que marcar en la clase padre como virtual. Bien, para esclarecer las dudas hagamos una clase más y modifiquemos un poco el programa:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4:  
   5: namespace HerenciaConstructores
   6: {
   7:     public class A
   8:     {
   9:         public virtual void Metodo()
  10:         {
  11:             Console.WriteLine("hola metodo en A");
  12:         }
  13:     }
  14:  
  15:     public class B : A
  16:     {
  17:         public override void Metodo()
  18:         {
  19:             Console.WriteLine("hola metodo en B");
  20:         }
  21:     }
  22:  
  23:     public class C : A
  24:     {
  25:         public new void Metodo()
  26:         {
  27:             Console.WriteLine("hola metodo en C");
  28:         }
  29:     }
  30:  
  31:     class Program
  32:     {
  33:         static void Main(string[] args)
  34:         {
  35:             A a = new A();
  36:             B b = new B();
  37:             C c = new C();
  38:  
  39:             a.Metodo();
  40:             b.Metodo();
  41:             c.Metodo();
  42:         }
  43:     }
  44: }

La salida de esta versión retornará simplemente una línea más:

hola metodo en A
hola metodo en B
hola metodo en C

 Como recordaran, podemos «castear» una clase derivada en su clase padre, esto significa que podemos cambiar un poco el método Main para que se vea de esta manera:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         A ab = new B();
   6:         A ac = new C();
   7:  
   8:         ab.Metodo();
   9:         ac.Metodo();
  10:     }
  11: }

Al ejecutar ahora nuestro programita nos topamos con algo totalmente diferente:

hola metodo en B
hola metodo en A

Es ahora cuando notamos la diferencia entre new y override. Efectivamente new «oculta» la implementación padre del método, esto lo notamos al instanciar C en A, como encontramos que la implementación de C es «nueva» entonces recurrimos a la versión «original» del método (ya que será una instancia de A después de todo). En el caso de instanciar B sobre A nos damos cuenta que «override» indica que siempre debemos usar esa versión ya que modifica efectivamente a la implementación original. Podriamos decir entonces que mientras «override» modifica la implementación original, new «reimplementa» el método. Morajela, al castear hacia abajo en la cadena de implementación el CLR buscará y preferirá las implementaciones «override».

Internamente lo que sucede es un poco más complejo, se necesita examinar un poco el código generado en ILASM, no es el motivo de este post profundizar en el código intermedio generado por el compilador ni como lo interpreta el CLR, pero digamos que intente explicar un poco la diferencia entre nuestros nuevos amigos virtual, new y override 😛 (pueden examinar el código intermedio generado usando MSIL Disassembler que viene como Tool en el .Net SDK y así quitarse la duda).

Espero que este post les sea de utilidad, saludos y hasta la siguiente entrega. 😀

9 respuestas to “Clases y herencia en C# (new, virtual y override)”

  1. carloslone said

    Excelente articulo, Muchas gracias por compartir tus conocimientos con la comunidad !

    Saludos,

    Carlos A. Lone
    http://carloslone.wordpress.com

  2. estuardogt said

    No es un comentario, es una pregunta.
    Tengo un problema, tengo un grid, dentro de este tengo itemTemplate que contiene un dropDownList, el cual su dataSource es un metodo del programa, el problema es que ahora necesito que la informacion del dataSource del dropDouwnList cambie, dependiendo de otro dropDownList que esta fuea del grid, le paso el parametro y si trae la informacion, pero no actualiza la informacion del dropDownList que tengo en el grid.
    Que puedo hacer?

  3. Miguel said

    Muy buen articulo. Ando yo peleandome ahora con estas cosas

  4. […] de herencia existente y a eso le sumamos el poder de los modificadores de herencia (sino revisense https://kementeus.wordpress.com/2007/08/28/clases-y-herencia-en-c-new-virtual-y-override/ y de paso visiten https://kementeus.wordpress.com/2008/01/25/clases-y-constructores-estticos/), […]

  5. DeathMetal said

    Ahora que estoy aprendiendo C# dominando anteriormente otros lenguajes como C++, artículos como éste se agradecen.

    Muchas Gracias!! 😉

  6. Cesar said

    buen articulo

    ahorita dentro de 1 hr tengo examen de herencia y creo q me va a servir lo que acabo de leer en este articulo.

  7. lefunes said

    Muy buen articulo 🙂

  8. Lourdes Marcela said

    ¡Muchas Gracias!
    Me respondiste una duda que me estaba bloqueando y me aclaraste un poco más como utilizar las herencias.

    Gracias Gracias 🙂

  9. ECDundy said

    Tu post me ha ayudado mas que 5 meses dando lo mismo con lo mismo en la universidad.
    lo entendi todo.

Replica a Clases y estructuras bien formadas, Parte I « My kemenworld… Cancelar la respuesta