Herencia en .NET (con C#)

Concepto de herencia en .NET, clases base y clases derivadas, el método new, métodos virtuales, métodos de reemplazo y de acceso protegido, interfaces.

 

Fecha: 12/Ago/2005 (10/08/2005)
Autor: Serge Valsse (svalsse@hotmail.com)

 



Concepto de herencia

El concepto de herencia tiende a confundir a muchos programadores ya que pretenden entenderlo desde el punto de vista del significado de la misma palabra, cuando la herencia en programación se refiere a la "clasificación"; es una relación entre clases, además es importante tener claro que la herencia es una relación de clases más que una relación de objetos.

Un ejemplo del concepto de herencia es por ejemplo que un empleado es una persona. Entonces podemos hacer un modelo de ello en .NET creando dos clases, una llamada Empleado y otra llamada Persona, y declarar que Empleado hereda de Persona. La herencia nos dice que hay una relación y establece el hecho de que todos los empleados son personas.

        class ClaseDerivada : ClaseBase
        {
            ...
        }


Clases base y clases derivadas

La sintaxis para declarar que una clase hereda de otra es:

La clase System.Object es la clase raíz para todas las clases, eso quiere decir que cuando declaramos una clase que no deriva explícitamente de otra, entonces el compilador define como clase base a System.Object.

Una clase puede tener como mucho una sola clase base. La clase derivada puede entonces llamar desde su constructor al constructor de su clase base.

        class  Empleado : Persona
        {
            public Empleado(string nombre) : base(nombre)    // base(nombre) llama a Persona(nombre)
            {
                ...
            }
            ...
        }


El método new (palabra clave new)

Aunque lo recomendable sería no tener métodos con la misma signatura (el nombre del método y número y tipo de sus parámetros) entre una clase derivada y su clase base, se puede lograr evitar el aviso del compilador que indica que hay ocultación de métodos utilizando la palabra clave new. Se debe considerar que los métodos aunque tengan el mismo nombre no guardan ninguna relación entre si, aun usando new.

Por ejemplo si tenemos un método con el mismo nombre en nuestra clase base Persona y la clase derivada Empleado llamado Nombre, entonces para evitar el ocultamiento del método deberá usarse la palabra clave new como sigue:

        class Persona
        {
            ...
            public string Nombre() {...}
        }

        class  Empleado : Persona
        {
            ...
            new public string Nombre() {...}
        }



Como hemos mencionado anteriormente el uso de new no evita el ocultamiento, únicamente evita el aviso del compilador. Siguiendo el ejemplo anterior cualquier llamada al método Nombre() aun desde una instancia de la clase Empleado se ejecutará el código del método Nombre() de la clase Persona ya que los dos métodos no guardan ninguna relación y se ocultan efectivamente uno del otro.


Métodos virtuales (palabra clave virtual)

Siguiendo con el ejemplo que venimos manejando digamos que realmente lo que queremos es implementar el mismo método más de una vez. Para ello recurrimos al poliformismo que significa literalmente "muchas formas", logrando conectar los métodos Nombre() de las clases base y derivada y declarar que son dos implementaciones del mismo método. Hay que usar explícitamente la palabra clave virtual para activar el poliformismo para un método dado. Así:

        class Persona
        {
            ...
            public virtual string Nombre() {...}
        }



De esa manera se indica que es la primera implementación del método Nombre(). En C# un métodos no es virtual por defecto.


Métodos de reemplazo (palabra clave override)

Luego que hemos declarado un método como virtual en la clase base, entonces podemos usar la palabra clave override en la clase derivada para declarar otra implementación de ese método.

        class  Empleado : Persona
        {
            ...
            public override string Nombre() {...}
        }



Utilizando esta combinación de declaraciones (virtual y override) logramos entonces que el compilador llame a la implementación "más derivada" de Persona.Nombre() que para el siguiente ejemplo sería la de la instancia de Empleado.


Métodos de acceso protegido (palabra clave protected)

La pablara clave protected nos permite definir una relación de la clase derivada a la clase base, todo eso gracias a la conexión de las clases definida por la herencia. En términos generales lo que nos permite protected es acceder a un miembro protegido de una clase base desde una clase derivada de esta. Es decir que un miembro protegido de una clase base es en realidad público para la clase derivada. Por lo contrario una clase que no es derivada no puede acceder a un miembro protegido de una clase o dicho en otras palabras, dentro de una clase que no es una clase derivada, un miembro protegido de una clase es en realidad privado.


Interfaces

Utilizar la herencia de clases en nuestros proyectos de desarrollo es de por sí una práctica importante y son muy tangibles los beneficios que como desarrolladores logramos, pero la verdadera razón de que exista la herencia son las interfaces. Una interfaz permite separar completamente el nombre de un método de la implementación de ese método. La interfaz solamente dice cuál es el nombre del método, como se implementa exactamente el método no es asunto de la interfaz.

Una interfaz especifica un contrato sintáctico y semántico al cual se deben adherir todas las clases derivadas. Específicamente, una interfaz describe la parte del qué del contrato, y las clases que implementan la interfaz describen la parte del cómo del mismo.

Vamos a declarar entonces una interfaz que defina que toda clase que implemente dicha interfaz debe tener un método Nombre().

        interface IPersona
        {
            // Define el "que"
            string Nombre();
        }



Ahora implementemos esta interfaz declarando una clase que herede de la interfaz y que implemente para este caso el método Nombre().

        class Persona : IPersona
        {
            ...

            public string Nombre()
            {
                // Define el "como"
                ...
            }
        }



Finalmente como hemos visto antes si es necesario reemplazar la implementación de un método de una clase en otra clase derivada de ella, se debe declarar que ese método de clase es virtual. Nuestro ejemplo quedaría así:

        interface IPersona
        {
            // Define el "que"
            string Nombre();
        }

        class Persona : IPersona
        {
            ...

            public virtual string Nombre()
            {
                // Define el "como" para la clase base Persona.
                ...
            }
        }

        class Empleado : Persona
        {
            ...
        
            public override string Nombre()
            {
                // Define el "como" para la clase derivada Empleado.
                ...
            }
        }

Aquí termino con este resumen sobre Herencia en .NET dejando la inquietud para los lectores sobre las clases abstractas, métodos abstractos y clases selladas, que son otros tópicos que son necesarios conocer para poder obtener el máximo beneficio del uso de la herencia, espero tratarlos en otro artículo pronto.

Por favor no olvides calificar el artículo en la caja de PanoramaBox que se muestra al inicio de la página, te lo agradecería mucho.

Serge Valsse svalsse@hotmail.com


Espacios de nombres usados en el código de este artículo:

System

 


ir al índice