Métodos.

A lo largo del curso hemos hecho uso de diferentes métodos en los ejemplos que han ido acompañando a la teoría. Por ejemplo, cuando hacíamos una llamada a una de clases del Framework para producir una salida de texto en la consola o requerir una entrada en la misma:

System.Console.WriteLine("Ejemplo de texto de salida de la consola.");
System.Console.ReadKey();

Como ves, la primera línea vuelca el texto "Ejemplo de texto de salida de la consola." y la segunda línea interrumpe la ejecución hasta que se produce una entrada por teclado. Los dos métodos muy utilizados hasta ahora en los ejemplos, y son dos métodos incluidos en la clase 'Console' del Framework.

Cuando estudiábamos las clases recordarás que las definíamos como entidades independientes que poseen, principalmente, atributos y propiedades que los definen en esencia, y un conjunto de métodos mediante los cuáles muestran su comportamiento. Así pues, podríamos definir los métodos como los elementos de las clases que, trabajando con sus atributos, propiedades y otros elementos, producen resultados. También podríamos definirlos como un conjunto de sentencias que ejecutan tareas específicas.

Así como una clase puede contener sus propios atributos, es decir sus propias variables, los métodos también pueden tener sus propios atributos. Pero es importantísimo que tengas en cuenta que los atributos declarados dentro de un método única y exclusivamente van a ser visibles dentro de ese método, y nunca fuera de él. Al contrario que en el caso de las clases, en las que podemos definir un atributo como 'private' o 'public' según queramos que sea visible o no desde fuera de esa clase, en los métodos no podemos utilizar los modificadores de acceso en la declaración de un atributo, pues la visibilidad de estos queda circunscrita únicamente al ámbito del propio método. Por lo tanto, un atributo de un método no podrá ser visible desde otro método, ni siquiera aunque este otro método corresponda a la misma clase.

Además, los métodos pueden recibir parámetros. Es el caso de la primera línea del ejemplo anterior. El parámetro de entrada es la cadena de texto que queremos volcar en la consola. Pero también hay métodos que, recibiendo o no parámetros de entrada, tienen resultados de salida, resultados que pueden tener la forma de valor numérico, cadena de texto, colecciones, etc., o cualquier objeto del Framework. Un ejemplo de método con dos parámetros de entrada y un resultado sería el siguiente:

private double MathOperation(double number1, double number2)
{
    double result;
    result = number1 * number2;
    return result;
}

Habrás podido interpretar este código sin ninguna dificultad: El método 'MathOperation' recibe dos parámetros de entrada, en este caso dos variables 'number1' y 'number2', ambas de tipo 'double'; a continuación declara una variable 'result' de tipo 'double', y como valor le asigna el resultado de la operación de multiplicación de los dos parámetros de entrada; finalmente, el método vuelve el resultado de la variable 'result'. Evidentemente, y ahorrando código, el ejemplo debería haber quedado simplificado en esto:

private double MathOperation(double number1, double number2)
{
    return number1 * number2;
}

Ten en cuenta que las variables 'number1' y 'number2' son creadas en el método y, por lo tanto, su visibilidad se reduce exclusivamente a su entorno; es como si se crearan en el momento de la ejecución del método y, una vez finalizado éste, el compilador las destruye. Ahora vamos a aplicar el ejemplo anterior a un programa para su compilación:

public class Program
{
    public static void Main()
    {
        System.Console.WriteLine("Introduzca primer número: ");
        double firstNumber = double.Parse(System.Console.ReadLine());
        System.Console.WriteLine("Introduzca segundo número: ");
        double secondNumber = double.Parse(System.Console.ReadLine();
        System.Console.WriteLine("El resultado de la multiplicación es: {0}", MathOperation(firstNumber, secondNumber));
        System.Console.ReadKey();
    }
    private double MathOperation(double number1, double number2)
    {
        return number1 * number2;
    }
}

Observa que en la salida de la consola para mostrar el resultado, directamente hemos hecho una llamada al método 'MathOperation'. Para ello hemos incluido entre paréntesis los dos parámetros de entrada, las dos variables de tipo 'double' que hemos declarado anteriormente. El método 'MathOperation' se ejecuta y devuelve el resultado de la multiplicación al método 'Main' exactamente en el mismo punto de llamada del método.

Cuando no queramos que nuestros métodos devuelvan ningún resultado, simplemente serán declarados como 'void':

private void MySimpleMethod()
{
    System.Console.WriteLine("Este método sólo vuelca esta salida en la consola.");
}

Pensarás que los ejemplos anteriores realmente no hacen nada especial, pues no serían necesarios al poder reemplazarlos en su llamada por el resultado directo de la operaciónd e multiplicación de las variables. Sin embargo, en el ejeplo siguiente tienes un método con plena funcionalidad.

public class Program
{
    public static void Main()
    {
        System.Console.WriteLine("Suma=1, resta=2, multiplicación=3, división=4: ");
        byte operation = byte.Parse(System.Console.ReadKey());
        System.Console.WriteLine("Primer número: ");
        double firstNumber = double.Parse(System.Console.ReadLine());
        System.Console.WriteLine("Segundo número: ");
        double secondNumber = double.Parse(System.Console.ReadLine());
        System.Console.WriteLine("Resultado: {0}", MathOperation(operation, firstNumber, secondNumber));
    }
    private double MathOperation(byte op, double n1, double n2)
    {
        switch (op)
        {
            case 1:
                return n1 + n2;
                break;
            case 2:
                return n1 - n2;
                break;
            case 3:
                return n1 * n2;
                break;
            case 4:
                return n1 / n2;
                break;
    }
}

No creo que tengas problemas en la interpretación de este código fuente. En cualquier caso lo importante aquí es que entiendas que el método posee tres parámetros de entrada, uno de tipo 'byte' que condiciona la ejecución de los bloques de código de la sentencia 'switch' y dos de tipo 'double' sobre los que se realizan las operaciones matemáticas. El resultado del método es de tipo 'double', valor que se vevuelve al punto del método 'Main' en el que se hizo la llamada al método. Como ejercicio podrías modificar este programa para, por ejemplo, validar la entrada y así comprobar que no se introducen caracteres alfanuméricos que darían un error a la hora de realizar las conversiones, o implementar un bloque 'try-catch' para evitar la división por cero.

Sobrecarga de operadores.

En ocasiones es verdaderamente útil el diseño de diferentes métodos que posean el mismo nombre pero que reciban diferentes clases y/o número de parámetros de entrada. En este caso, se dice que el método está sobrecargado. Básicamente consiste en redefinir un método con el mismo nombre pero con diferentes parámetros. Por ejemplo, supongamos el siguiente método 'View' que muestra expresiones al estilo del método 'WriteLine' pero de una forma más sencilla:

public class Viewer
{
    public static void View(string text)
    {
        System.Console.WriteLine(text);
    }
    public static void View(long number)
    {
        System.Console.WriteLine(number);
    }
    public static void View(string text, long number)
    {
        System.Console.WriteLine(text + number);
    }
    public static void View(double number)
    {
        System.Console.WriteLine(number);
    }
    public static void View(string text, double number)
    {
        System.Console.WriteLine(text + number);
    }
}

Cuando el método 'View' sea llamado, será el compilador el que se encargará de ejecutar uno y sólo uno de los métodos con sobrecarga, y lo hará en función de los parámetros de entrada. Es por ello que todas y cada una de las sobrecargas del método deben ser diferentes, o en número de parámetros o en los tipos de los parámetros. Hay que señalar que si el compilador no encuentra una sobrecarga de un método con los tipos exactos de los parámetros de entrada, elegirá para la ejecución un método sobrecargado cuyos tipos de parámetros permitan conversiones válidas. Por ejemplo, en este caso, en una llamada al método 'View' podríamos indicar como parámetro de entrada una variable o un valor de tipo 'int', y el compilador no daría error puesto que podría realizar la conversión a, por ejemplo, tipo 'double', y así ejecutar la sobrecarga del método 'View' en la cual hay un parámetro de entrada de tipo 'double'.

Evidentemente, un método puede llamra otro método, y éste a otro, y así sucesivamente. Esto al principio puede causarte más de un dolor de cabeza, pero el código fuente de programas avanzados será una característica muy común. En realidad, no hay problema ninguno de comprensión de un código fuente que contine llamadas a métodos desde otros métodos. Simplemente hay que tener presente que, cuando un bloque de instrucciones de un método se ha ejecutado, el curso de ejecución del compilador vuelve exactamente al punto en el que se produjo dicha llamada, independientemente si la llamada se produjo en un nivel profundo de llamadas a métodos desde otros métodos.

Parámetros por referencia.

Hasta ahora hemos usado métodos que reciben valores en sus parámetros, valores almacenados en variables propias del método y que, al finalizar la ejecución del método, dichas variables quedan destriudas. Sin embargo es posible establecer un tipo de parámetro que sea modificado dentro del método y el nuevo valor del mismo pueda ser siendo útil al finalizar la ejecución del método. Esto es posible con la palabra clave 'ref'. Veamos un ejemplo:

public class Program
{
    public static void Main()
    {
        int number = 1234;
        Increase(ref number);
        System.Console.WriteLine(number);
        System.Console.ReadKey();
    }
    private static void Increase(ref int param)
    {
        param += 1;
    }
}

El método 'Main' define una variable 'number' de tipo 'int' y le asigna el valor 1234. Después invoca al método 'Increase' pasándole como argumento una referencia a esa variable, lo que supone declarar su parámetro 'param' como una referencia a un tipo 'int'. De esta manera, todos los cambios realizados a la variable 'param' se volcarán en el variable 'number' y, como consecuencia, el nuevo valor de la variable que se volcará en a consola será 1235. Puesto que nuestra intención ha sido la modificación del valor de la variable original 'number', no es necesario establecer una salida del parámetro y, por lo tanto, este queda declarado en su definición con la palabra 'void'.

Parámetros de salida.

Un parámetro de salida es una variable calificada con la palabra 'out'. La palabra clave 'out' produce argumentos que se van a pasar por referencia, es decir, 'out' es similar a 'ref', excepto que 'ref' requiere que se inicie la variable antes de pasarla.

public class Program
{
    public static void Main()
    {
        int number;
        SetValue(out number);
        System.Console.WriteLine(number);
        System.Console.ReadKey();
    }
    private static void SetValue(out int i)
    {
        i = 10;
    }
}

Aunque las variables que se pasan como argumentos 'out' no tienen que inicializarse antes de pasarse, es necesario que el método llamado asigne un valor antes de que el método devuelva un resultado. La salida por la consola será el valor de la variable 'number', que ha sido establecida en 10.