sábado, 5 de marzo de 2022

Curso avanzado de C#. Sobrecarga de Métodos

Un método es definido por su modificador, el tipo de retorno, el nombre, el número y el tipo de los argumentos. Un método también tiene una firma. La firma de un método, es lo que identifica de forma única el método de cualquier otro método que tenga el mismo nombre. Cuando llamamos a un método en nuestro código, el compilador busca un método con la firma correcta. La firma consiste en el nombre del método, el tipo de datos y la cantidad de parámetros en el método. El tipo o parámetro en un método, además  puede ser un tipo pasado por valor, un tipo pasado por referencia o un parámetro de salida. El tipo de retorno, por tanto no es el componente único de una firma de método.

Sobrecarga de Métodos



Puede que nos preguntemos por la necesidad de crear más de un método con el mismo nombre. ¿No es esto una forma de complicar el código y hacerlo más difícil de leer? Hay que tener en cuenta, que es posible que necesitemos tener el mismo nombre de método en función de la acción que realice. Pero que tenga una funcionalidad interna diferente dependiendo de los datos enviados por parámetro al método. Por ejemplo, puede que  queramos calcular el área de varias formas geométricas y para cada una de ellas utilizar su una fórmula correspondiente. A continuación se presentan dos métodos de muestra para calcular el área de un círculo y un rectángulo: 

// calcula el área de un circulo

public double calcArea(double radio)
    {
        double area = Math.Pi * (radio * radio); return area;
    }
// calcula el área de un rectángulo
public double calcArea(double longitud, double anchura)
    {
        double area = longitud * anchura;
        return area;
    }

Arriba, se han definido dos métodos con el mismo nombre calcArea. El nombre explica el propósito del método de calcular el área. La diferencia entre los dos métodos está en la firma. En el primero, se configura para aceptar un solo tipo doble para representar el radio de un círculo, luego se realiza el cálculo correcto para determinar el área del círculo cuyo radio se pasó como argumento. El segundo método también se llama calcArea () pero acepta dos argumentos para la longitud ancho de un rectángulo y realiza el cálculo apropiado para determinar el área de un rectángulo. Esto es un ejemplo de métodos sobrecargados. La sobrecarga significa que se crean múltiples métodos con el mismo nombre, pero cada uno con una firma diferente, con la intención de realizar alguna acción específica para la funcionalidad deseada.

Otro uso común de los métodos sobrecargados es en los constructores de las clases. La sobrecarga nos proporciona la oportunidad de inicializar variables miembro de forma selectiva. Hay que recordar que un constructor es un método sin un tipo de retorno.  A continuación crearemos una clase Estudiante con métodos sobrecargados, crearemos la clase Estudiante con múltiples constructores diseñados para inicializar diferentes variables miembro. 

A menudo, cuando trabajemos con clases en un programa, proporcionaremos varias formas de inicializar la clase. El fundamento es que en el momento de la creación de un  programa, queremos proporcionar cierta flexibilidad al los usuarios y solo requieren cierta información conocida. Un ejemplo sería completar un formulario en un aplicación para establecer nuevos alumnos en un sistema. En ese momento, el instructor puede no saber la clase o el estudiante y no  necesariamente tendrá una calificación. La sobrecarga del constructor es el mecanismo ideal para esto. Crearemos la clase Estudiante con las siguientes variables miembro.

nombre de tipo  string
apellidos de tipo  string
curso de tipo  int
nombre_colegio de tipo  string

Creamos tres constructores sobrecargados. Primero crearemos un  constructor por defecto,   este no requiere argumentos y no inicializa ninguna variable  miembro. Crearemos un segundo constructor que acepte valores para los nombres y apellidos de los alumnos. Asignaremos estos a las variables miembro en el constructor, utilizaremos el último  constructor para aceptar valores para todas las variables miembro. Una vez que hayamos creado la clase, utilizando el método principal en nuestra aplicación de consola, crearemos tres nuevos objetos alumno. Llamaremos a un constructor diferente en cada objeto, si observamos los desplegables de IntelliSense de Visual Studio, veremos cómo  se enumeran los diferentes constructores. También proporcionaremos los valores necesarios a cada objeto Estudiante utilizando los constructores. 

Solución.

class Estudiante
    {
        public string nombre;
        public string apellidos;
        public int curso;
        public string nombre_colegio;

        public Estudiante()
        {
        }
        public Estudiante(string nom, string apell)
        {
            this.nombre = nom;
            this.apellidos = apell;
        }
        public Estudiante(string nom, string apell, int curso, string colegio)
        {
            this.nombre = nom;
            this.apellidos = apell;
            this.curso = curso;
            this.nombre_colegio = colegio;
        }
    }
    class Programa
    {
        static void Main(string[] args)
        {
            Estudiante Estudiante1 = new Estudiante();
            Estudiante Estudiante2 = new Estudiante("Juan", "González");
            Estudiante Estudiante3 = new Estudiante("Antonio", "Puertas", 5, "Los_Sauces");
        }
    }


Como se puede ver en el código anterior, la sobrecarga de métodos permite implementar alguna funcionalidad en los constructores de la clase. En este ejemplo, el constructor tiene tres versiones diferentes. Cada versión se diferencia por el número de parámetros, esto proporciona un poco de flexibilidad en la forma de crear objetos a través de código al permitir asignar valores en el momento de crear un objeto, o aplazar las asignaciones  más adelante. 

Métodos abstractos (abstract) y anulados (overriden)


Otro aspecto clave en OOP es el uso de métodos abstractos y anulados. Los están relacionados. Un método abstracto declara una firma pero no una implementación. También se conoce como método virtual, lo que significa que no se considera un método real porque no tiene implementación, ¿qué utilidad puede tener si no dispone de una implementación? La respuesta es muy simple, las clases derivadas deberán implementar la funcionalidad en su código. Y aquí es también donde entran en juego los métodos anulados u overriden. Una clase derivada debe anular las clases abstractas dentro de su implementación. 

// método abstracto dentro de una clase
    public abstract class Estudiante
    {
        public abstract void muestraDetalles();
    }


El método se declara con la palabra clave abstract y  termina con un punto y coma. No tiene detalles de implementación. En otras palabras, los desarrolladores que deriven una clase como esta, deberán proporcionar su propia implementación del método muestra Detalles.

ADVERTENCIA No se puede incluir una declaración de un método abstracto en una
clase no abstracta.

El siguiente ejemplo utiliza la misma clase abstracta  Estudiante y el método realizado anteriormente, pero muestra el uso de una clase derivada que invalida el método abstracto:

// método abstracto dentro de una clase publica abstracta
public abstract class Estudiante
{
public abstract void muestraDetalles();
}

public class UniversidadEstudiante : Estudiante
{
    public string Nombre;
    public string Apellidos;
    public string Instituto;
    public double Calificación;
    public override void muestraDetalles()
    {
        Console.WriteLine("El estudiante " + Nombre + " " + Apellidos + " viene del instituto " + Instituto + " con una calificación global de " + Calificación);
    }
}

Estos ejemplos son sencillos para demostrar los conceptos sin agregar  demasiada complejidad. En este ejemplo, la clase Estudiante se convierte en una clase abstracta que sirve como clase base para otras clases de tipo Estudiante. En este caso, hemos creado una nueva clase para representar un estudiante universitario. El nombres de la declaración de la clase UniversidadEstudiante viene seguido por : y el nombre de la la clase Estudiante, lo que indica que la clase UniversidadEstudiante hereda de la clase  Estudiante.

Las clases heredadas adquieren las características de sus clases padre, pero en este caso, solo hemos proporcionado un método abstracto en la clase base. Esto significa que la clase derivada UniversidadEstudiante deberá implementar esa funcionalidad, 
anulando este método abstracto. El método simplemente concatena las variables miembro en una cadena de salida de consola. Crear una clase abstracta con métodos abstractos, permite imponer una estructura específica en las clases que se derivan de la clase base abstracta pero que dejan los detalles de implementación a cada clase derivada. clase. Eso significa que cada clase derivada es libre de implementar el método derivado (en este caso muestraDetalles) de forma que tenga más sentido para la clase.

ADVERTENCIA: No se puede crear una instancia de una clase abstracta. Si intentamos  hacerlo, Visual Studio generará un error y su código no compilará. Las clases abstractas solo sirven como clases base platilla para las clases derivadas.


Métodos de extensión


Los métodos de extensión permiten extender una clase o un tipo existentes agregando un nuevo  método o métodos sin modificar la clase o tipo original y sin volver a compilar esa clase o tipo: El motivo por que el que podemos querer hacer esto,  es para agregar funcionalidad a un tipo ya existente  sin extender la clase entera. 

Los métodos de extensión se pueden aplicar a nuestros propios tipos o incluso a tipos existentes en .NET. Un ejemplo podría se agregar alguna funcionalidad a la clase de Matemáticas que  ya incluye .NET. Esta clase matemática ya proporciona mucha funcionalidad, pero no cubre todas las matemáticas.

Pueden existir funciones o procedimientos que deseamos tener en nuestra aplicación, sin  necesidad de crear una nueva clase matemática ni heredar de la clase matemática existente. Para ello, podemos utilizar métodos de extensión. En este ejemplo extenderemos el tipo int .NET para incluir un método para hacer raíces cuadradas.

Para ello se nos puede ocurrir crear una nueva clase liviana con los métodos que necesitamos y llamarlos desde nuestro código. Podemos pensar que en realidad no hay necesidad de crear métodos de extensión y si lo deseamos lo anterior es correcto también. Sin embargo, .NET utiliza métodos de extensión para el estándar LINQ.

Para crear un métodos de extensión, necesitamos incluirlo en una clase estática pública, por lo que antes crearemos esa clase. Una vez creada, definimos el método en esa clase y convertimos el método en un método de extensión con la simple adición de la palabra clave this.  Se trata de la palabra clave que se refiere a la instancia específica de la clase en la que aparece. A continuación mostramos cómo podemos crear un método de extensión para el tipo int .NET: 

public static class MisMetodosExtendidos
        {
            public static int raizCuadrada(this int num)
            {
                int resultado = 0;
                resultado = num * num;
                return resultado;
            }
        }

El método también debe ser estático. Si no lo declaramos como estático, no se mostrará  en la ventana de IntelliSense y no estará disponible para ser llamado. 

Métodos extendidos en C#


Ahora en el cuadro de método de la ventana IntelliSense se indica que efectivamente se ha extendido el tipo int para incluir un método para hacer raíces cuadradas de enteros.

Si creamos un método de extensión para un tipo, y este tipo ya tiene un método con el mismo nombre que su método de extensión, el compilador utilizará solo el tipo original del  método y no nuestro método de extensión. Debemos de nombrar nuestros  métodos de extensión con cuidado. 

ADVERTENCIA La creación de métodos de extensión en el tipo Object da como resultado la extensión, el método está disponible para cada tipo en el marco.
Nuestra implementación debe ser cuidadosamente pensada para permitir el correcto funcionamiento en los diferentes tipos a los que se aplique. 

Parámetros con nombre


Normalmente, al llamar a un método que contiene varios parámetros, debemos  pasar los 
argumentos a los parámetros en el orden en el que existen los parámetros. Los parámetros son conocidos como posicionales. IntelliSense ayuda mucho y permite ver la firma del método desde la perspectiva de los parámetros y su orden. Los argumentos con nombre permiten indicar explícitamente a qué parámetro está destinado el argumento
para ser utilizado. Los argumentos con nombre van de la mano con los parámetros con nombre. En otras palabras, el parámetro debe ser nombrado primero; entonces el argumento que pasamos puede usar ese nombre de parámetro. Pero todos los parámetros se nombran independientemente, por lo que la mitad de la solución ya está presente.

Como ejemplo, consideramos calcular el área de un rectángulo. El método incluye parámetros para el largo y ancho del rectángulo. Realizaremos el cálculo del área y devolvemos el resultado. La llamada al método utiliza argumentos con nombre. 

public class programa
    {
        static void Main(string[] args)
        {
            double area = rectArea(largo: 35.0, ancho: 25.5);
            Console.WriteLine(area);
        }

        public static double rectArea(double largo, double ancho)
        {
            return largo * ancho;
        }

 }

En la llamada al método rectArea (), podemos pasar los dos argumentos necesarios, pero utilizaremos argumentos nombrados largo y ancho. Al agregar los dos puntos al final del nombre del argumento da como resultado una advertencia del compilador. Desafortunadamente la advertencia no es intuitiva, y simplemente indica el requisito de poner un paréntesis de cierre. Esto es así simplemente porque el compilador no está muy seguro de cuáles son las intenciones de poner una sin los dos puntos.

Otra ventaja clave del uso de argumentos con nombre está en la legibilidad de nuestro código. Mirando al ejemplo de código, podemos decir qué valor es el largo y cuál es el ancho del rectángulo del que queremos calcular el área. También es una forma rápida de depuración porque nos ayuda ha asegurarnos de pasar los valores adecuados. 

ADVERTENCIA Los argumentos nombrados pueden ser argumentos posicionales, pero los argumentos posicionales no pueden ser argumentos nombrados. 

Parámetros opcionales


Los parámetros opcionales son una característica de los métodos que nos permiten solicitar solo algunos argumentos desde un método, mientras que los otros son opcionales. llamar a un método con parámetros opcionales. significa que la persona que llama debe proporcionar los parámetros requeridos pero puede omitir los parámetros opcionales. Los parámetros requeridos son aquellos que no se declaran opcionales.

Se permiten parámetros opcionales en métodos, constructores, indexadores y delegados. Cada parámetro opcional también incluye un valor predeterminado que se utilizará en el caso de que no se proporcione un valor en una llamada a ese método, los valores por defecto pueden ser:

-Una expresión constante.

-Tipos de valor que incluyen la creación de nuevos tipos con una nueva palabra clave.

Los parámetros opcionales se colocan al final de la lista de parámetros en la firma del método. Todos  Los parámetros requeridos deben aparecer al principio en la firma del método. Además, si proporcionamos un valor para un parámetro opcional, cualquier parámetro opcional que preceda a este parámetro opcional también deberá ser provisto de un valor. 

// ejemplo de método con parámetros opcionales
        public void muestraNombre(string nombre, string iniciales = "", string ultimo = "")
        {
            Console.WriteLine(nombre + " " + iniciales + " " + ultimo);
        }

En este ejemplo, podemos ver un método que genera un nombre en la ventana de consola. Utiliza dos parámetros opcionales,  Las iniciales del nombre y el apellido. Son parámetros opcionales porque tienen valores por defecto asignados. Estos valores predeterminados son una cadena vacía porque no deseamos generar caracteres o cadenas arbitrarias,  si la persona que lo llama elige no proporcionar valores para estos. En este ejemplo, la persona que llama debe proporcionar un valor para el primer parámetro, pero puede excluir la inicial y último. Sin embargo, si la persona que llama al método proporcionase un valor para el último parámetro, también deberá proporcionar un valor para la inicial. Si se pasa un valor para la inicial, el valor para el apellido sigue siendo opcional. 

ADVERTENCIA Los parámetros requeridos no pueden existir después de un parámetro opcional en la firma de un método. 




No hay comentarios:

Publicar un comentario