sábado, 9 de julio de 2022

Curso avanzado de C#. Creación e implementación de jerarquías de clases

Heredar de una clase base La sección "Creación de tipos de referencia", explica cómo crear clases. El siguiente código muestra la definición de una clase Persona simple y debe ser familiar para  nosotros

Public Class Persona

{

       Public String Nombre { Get; Set; }

       Public String Apellido { Get; Set; }

       Public Persona(String nombre, String apellido)

       {

             // Valida el nombre y el apellido

             If (( nombre == null) || (nombre .Length < 1))

                    Throw New ArgumentOutOfRangeException(

                    "Nombre", nombre,

                    "El nombre no puede ser nulo o quedar en blanco.");

             If ((apellido == null) || (apellido .Length < 1))

                    Throw New ArgumentOutOfRangeException(

                    "Apellido", apellido,

                    "El  apellido no puede ser nulo o quedar en blanco.");

                    // Guarda el nombre y el apellido.

                    Nombre = nombre;

                    Apellido = apellido;

       }

}

La clase Persona contiene dos propiedades de cadena implementadas automáticamente: Nombre y Apellido. (Una clase utilizada por una aplicación real probablemente tendría muchas más propiedades para contener información como como la dirección postal, números de teléfono y direcciones de correo electrónico). Nuestro constructor toma los nombres y apellidos como parámetros, realiza alguna validación y guarda los valores en las propiedades Nombre y Apellido.

Curso avanzado de C#. Creación e implementación de jerarquías de clases
Fotos similares a esta aquí


Ahora supongamos que queremos  crear una clase de Empleado que tenga las propiedades Nombre, Apellido y NombreDepartamento. Podemos construir esta clase desde cero, pero necesitaremos las mismas propiedades que las de la clase Persona y necesitaremos las mismas validaciones, por lo que crearlo desde cero requeriría duplicar  todo ese código. Una mejor solución es derivar la clase Empleado de la clase Persona para que herede de la clase padre los campos, propiedades, métodos y eventos. Un empleado es una especie de persona, por lo que tiene sentido que la clase Empleado derive desde la clase Persona. No hay ninguna razón por la que el mismo código dentro de la clase Persona no funcionara también para los objetos  de la clase Empleado. 

Terminología de clases

Hay mucha terminología en torno a las jerarquías de clases. Cuando derivamos una clase de otra, la nueva clase hereda todo el código incluido en la clase original. En este caso, la clase original se llama padre clase, clase base o superclase. La nueva clase se llama clase derivada, hija, secundaria, o subclase. Derivar una clase de otra se llama subclasificación. 

_________________________________________________


Para derivar una clase de otra, simplemente hay que poner el nombre de la clase con dos puntos y luego el nombre de la clase principal. El siguiente código muestra una clase  empleado que se deriva de la clase persona. 

Public Class Empleado :  Persona

{

    Public String NombreDepartamento { Get; Set; }

}

En este ejemplo, la clase Empleado hereda los campos, propiedades, métodos y eventos definidos por la clase Persona. También agrega una nueva propiedad, Nombre Departamento. Aunque una clase secundaria hereda la mayoría del código en su clase padre, no hereda el constructor de la clase. En este punto, un programa podría utilizar el siguiente código para crear un objeto Empleado sin inicializar sus propiedades Nombre y Apellido:

Empleado empleado = new Empleado();

Dado que este código no inicializa las propiedades Nombre y Apellido del empleado, estas tienen los valores nulos, lo que anula el propósito del constructor de la clase Persona. La solución es hacer que los constructores de la clase secundaria llamen a los constructores de la clase principal. 

Llamar a constructores de clases principales 

Para asegurarse de que se llama al constructor de la clase Persona para validar el Nombre y Apellido del empleado. Debemos darle un constructor a la clase Empleado, con  una lista de argumentos del constructor seguida por dos puntos, la clase base y cualquier parámetro que deseemos pasar al constructor de la clase base.

En este ejemplo, el constructor Empleado debe pasar el nombre y apellido que recibe al Constructor de la clase de persona. El código resaltado muestra dónde el constructor llama al constructor de la clase base : 

Public Class Empleado :  Persona

{

      Public String NombreDepartamento { Get; Set; }

      Public Empleado(String Nombre, string Apellido,

      string nombredepartamento)

            :base(Nombre, Apellido)

      {

            // Valida NombreDepartamento.

            If ((nombredepartamento == null) || (nombredepartamento.Length < 1))

            Throw New ArgumentOutOfRangeException(

            "nombredepartmento", nombredepartmento,

            "NombreDepartamento no debe ser nulo o estar en vacío.");

            // Graba el Nombre del Departamento.

            NombreDepartmento = nombreDepartmento;

      }

}

Si la clase base tiene varios constructores, la clase secundaria puede usar la palabra clave base para invocar cualquiera de ellos. El programa usa los argumentos que siguen a la palabra clave base para averiguar qué constructor utilizar. 

Nota: Cuando un constructor utiliza la palabra clave base para invocar un constructor de la clase base, El constructor de la clase base se ejecuta antes que el cuerpo del hijo. 

Si tanto la clase principal como la secundaria tienen constructores, el constructor de la clase secundaria debe invocar uno de los constructores de la clase padre. Esto significa que la llamada al constructor en la clase base que hemos resaltado arriba, requiere un fragmento de código. Si eliminamos ese código, Visual Studio muestra el mensaje de error "JerarquiaPersona no contiene un constructor que tome 0 argumentos". (Aquí, JerarquiaPersona es el espacio de nombres que contiene la clase Persona.) El constructor de la clase Empleado está tratando implícitamente de acceder a un constructor de Persona que no toma parámetros y no puede encontrar uno. Una rareza de este sistema es que podemos crear una clase Empleado sin constructores aunque  permite al programa crear una instancia de la clase Empleado sin invocar un constructor de la clase Persona. Eso significa que la siguiente definición para la clase Empleado es legal: 

Public Class Empleado :  Persona

{

      Public String NombreDepartamento { Get; Set; }

}

Si deseamos evitar que el programa eluda los constructores de la clase principal, debemos dotar la clase secundaria de al menos un constructor.

Llamar a constructores de la misma clase 

A menudo, es conveniente dar a una clase varios constructores para realizar diferentes tipos de inicialización. Dependiendo de qué parámetros se pasen al constructor. En ese caso, varios constructores puede que necesiten realizar las mismas tareas. Por ejemplo, supongamos que la clase Persona tiene las propiedades Nombre y Apellido y queremos permitir que el programa cree un objeto Persona especificando solo el nombre o nombre y apellido. 

Class Persona

{

     Public String Nombre { Get; Set; }

     Public String Apellido { Get; Set; }

     // Constructor con nombre.

     Public Persona(String nombre)

     {

          Nombre = nombre;

     }

     // Constructor con Nombre y Apellido.

     Public Persona(String nombre, String apellido)

     {

          Nombre =  nombre;

          Apellido = apellido;

     }

}

El primer constructor toma un nombre como parámetro y lo almacena en la propiedad Nombre. El segundo constructor toma el nombre y el apellido como parámetros y guarda sus valores en las Propiedades Nombre y Apellido. En este código, el segundo constructor comienza realizando el mismo trabajo que el primer constructor hace cuando guarda el nombre. En un escenario mas complicado en el que los constructores realizan tareas más difíciles, esta repetición de código sería un problema. Significaría varias copias del mismo código para implementar, depurar y mantener. Una forma de evitar esta duplicación de código es hacer que un constructor llame a otro. En este podríamos reescribir el segundo constructor para que llame al primer constructor para que pueda manejar el parámetro de nombre. Para ello, hacemos que el constructor invoque a un segundo constructor de la misma manera que se invoca a un constructor de la  clase base, excepto que utiliza la palabra clave this en lugar de la palabra clave base. 

El siguiente código muestra cómo el segundo constructor de la clase Persona puede invocar a su primer constructor. Se resalta el código que invoca al primer constructor. 

// Constructor con Nombre y Apellido.

Public Persona(String nombre, String apellido)

      :this(nombre)

{

      Apellido = apellido;

}

Nota: Cuando un constructor utiliza la palabra clave this para invocar un segundo constructor en la misma clase, el segundo constructor se ejecuta antes de que se ejecute el cuerpo del primer constructor. 

Si derivamos la clase Empleado de la clase Persona y la clase Empleado incluye una propiedad NombreDepartamento. Es posible que queramos diferentes constructores de empleados que puedan tomar como parámetros nombre, nombre y apellido, o nombre y apellido y nombre de departamento. Estos constructores pueden utilizar la misma técnica que se muestra en esta versión de la clase Persona para hacer que los constructores más complicados invoquen a los más simples. Los constructores Empleado también pueden utilizar la palabra clave base descrita en la sección anterior para invocar a los constructores de la clase Persona. Por ejemplo, el constructor Empleado(nombre, apellido,nombredepartamento) podemos utilizar esta técnica para invocar al constructor Empleado( nombre, apellido) y ese constructor puede utilizar la palabra clave  base para invocar al constructor de Persona(nombre, apellido). 







No hay comentarios:

Publicar un comentario