DELEGADOS EN C# .NET | PUNTEROS A METODOS

Delegados


Tal como hemos comentado, los eventos y los delegados están muy unidos en C#. De hecho no se pueden definir eventos si no definimos previamente un delegado; ya que por medio de ese delegado podremos crear o asignar el método que se encargará de interceptar el evento.
Por esa razón veremos primero que son los delegados para de esta forma comprender mejor todo lo que tenemos que hacer para definir eventos en nuestras clases y desde los objetos creados a partir de ellas asociarlos a un método.




Definición "formal" de delegado


Veamos algunas de las definiciones de la documentación de Visual Studio sobre los delegados:

"Los delegados habilitan escenarios que en otros lenguajes se han resuelto con punteros a función. No obstante, a diferencia de los punteros a función, los delegados están orientados a objetos y proporcionan seguridad de tipos."
"Un delegado es una clase que puede contener una referencia a un método. A diferencia de otras clases, los delegados tienen un prototipo (firma) y pueden guardar referencias únicamente a los métodos que coinciden con su prototipo."

De estas dos definiciones podemos sacar en claro que los delegados son clases especiales que pueden tener referencias a un método, y que ese método debe cumplir con el "prototipo" definido por el delegado. Y que esa referencia que contienen es como los punteros de otros lenguajes, pero que están enfocados a ser utilizados desde el punto de vista de .NET, es decir, a ser usados desde una perspectiva orientada a objetos.

Por tanto, cuando definimos un delegado, estamos definiendo la firma que debe tener una función (o método), con idea de que podamos crear un "puntero" a dicha función, pero de una forma "controlada" por el CLR de .NET. Por tanto, solo se admitirán "punteros" a funciones que concuerden con la definición que ha hecho el delegado.

Si retomamos la definición del delegado System.EventHandler definido por .NET para los eventos, veremos que ese delegado realmente está definiendo la "forma" que debemos declarar el método que intercepte un evento basado en ese delegado.

Analicemos con algo de detalle la definición tanto del delegado EventHandler como del evento TextChanged.

El delegado está definido de la siguiente forma:

public delegate void EventHandler(object sender, EventArgs e);

Si desechamos public delegate, nos quedamos con una definición de un método que bien podríamos incluir en una interfaz, ya que lo que hace es indicar que ese método debe ser de tipo void (no devuelve ningún valor), y que tiene dos parámetros, el primero de tipo object y el segundo de tipo EventArgs.

Ahora veamos la definición del evento TextChanged:

public event EventHandler TextChanged;

Si en esta ocasión también obviamos las dos primeras instrucciones, tendremos una declaración "típica" de C#, en la que el tipo de datos es EventHandler (el delegado, que al fin y al cabo es una clase de tipo especial), seguida de "la variable" que define el evento. Lo que notamos aquí es que no aparece por ningún lado los parámetros que hay que usar, y esa es una de las características de los delegados, y posiblemente lo que complica más su entendimiento.

Veamos ahora cómo asignamos el método al evento del control:

this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);

El evento lo conectamos por medio del constructor del delegado, el cual espera como parámetro un método, y lo que le pasamos es "un puntero al método", es decir, le indicamos dónde está ese método. Como hemos comentado antes, ese método debe cumplir con las especificaciones indicadas por el delegado, cosa que podemos comprobar si vemos el código del método que se utilizará cuando el evento se produzca:

private void textBox1_TextChanged(object sender, EventArgs e) {...}

Y tal como podemos ver, el método cumple a la perfección con la definición del delegado.

Utilizar un delegado para acceder a un método

Ahora veamos brevemente cómo usar los delegados, en este caso sin necesidad de que defina un evento.

Como hemos comentado, un delegado realmente es una clase que puede contener una referencia a un método, además define el prototipo del método que podemos usar como referencia. Sabiendo esto, podemos declarar una variable del tipo del delegado y por medio de esa variable acceder al método que indiquemos, siempre que ese método tenga la misma "firma" que el delegado. Parece complicado ¿verdad? Y no sólo lo parece, es que realmente lo es. Comprobemos esta "complicación" por medio de un ejemplo. En este código, que iremos mostrando poco a poco, vamos a definir un delegado, un método con la misma firma para que podamos usarlo desde una variable definida con el mismo tipo del delegado.

Definimos un delegado de tipo void que recibe un valor de tipo cadena:

delegate void Saludo(string nombre);

Definimos un método con la misma firma del delegado, el cual devolverá Hola, seguido del contenido del parámetro:

static private void mostrarSaludo(string elNombre)
{
    Console.WriteLine("Hola, " + elNombre);
}

Ahora vamos a declarar una variable para que acceda a ese método.
Para ello debemos declararla con el mismo tipo del delegado:

Saludo saludando;

La variable saludando es del mismo tipo que el delegado Saludo. La cuestión es ¿cómo o que asignamos a esta variable?

Primer intento:

Como hemos comentado, los delegados realmente son clases, por tanto podemos usar new Saludo y, según parece, deberíamos pasarle un nombre como argumento. Algo así:

saludando = new Saludo("Pepe");

Pero esto no funciona, entre otras cosas, porque hemos comentado que un delegado contiene (o puede contener) una referencia a un método, y "Pepe" no es un método ni una referencia a un método.

Segundo intento:

Por lógica y, sobre todo, por sentido común, máxime cuando hemos declarado un método con la misma "firma" que el delegado, deberíamos pensar que lo que debemos pasar a esa variable es el método, ya que un delegado puede contener una referencia a un método, por tanto podemos pensar que si creamos un nuevo objeto del tipo Saludo pasándole el nombre del método y el argumento...

saludando = new Saludo(mostrarSaludo("Pepe"));

Tampoco funciona, porque nos dice que espera un método.
Probemos entonces a indicarle sólo el nombre del método:

saludando = new Saludo(mostrarSaludo);

Ahora sí, al menos no da error de compilación, aunque, no muestra nada, cosa que es lógica porque en ningún sitio le hemos indicado la cadena que tiene que usar para el saludo.
¿Cómo podemos hacer que se muestre el saludo?

A ver, recapacitemos, tenemos una variable (saludando), que es del tipo del delegado (Saludo), y que "apunta" a un método (mostrarSaludo), que espera que le pasemos como argumento una cadena.

¿Podremos hacer esto?

saludando("Pepe");

¡Pues sí! Ahora muestra el saludo a "Pepe".

En realidad, cuando estamos usando la línea anterior, es como si estuviésemos llamando al método de esta forma:

mostrarSaludo("Pepe");

Qué forma de complicarnos la vida, ¿verdad?

Pero debemos pensar, que en este contexto funciona la llamada directa al método porque está definido en la misma clase que la variable declarada como el delegado.


EJEMPLO DEL VIDEO



CREAR EVENTOS EN CSHARP | C# .NET


 CREAR EVENTOS EN CSHARP | C# .NET


Eventos


En esta lección veremos cómo trabajar con los eventos producidos en una aplicación de Windows, con idea de que nos familiaricemos con ellos, y veamos la relación que tienen con los delegados; en la siguiente lección trataremos los delegados con más profundidad, para seguir con la forma de declararlos en nuestras clases.




Interceptar los eventos de los controles de un formulario


Debido a que aún no hemos visto el módulo dedicado a las aplicaciones de Windows, en las que se utilizan los "clásicos" formularios, no vamos a entrar en detalles sobre cómo crear un formulario ni como añadir controles, etc., todo eso lo veremos en el siguiente módulo. Para simplificar las cosas, veremos simplemente cómo definir un evento y asociarlo a un control e incluso al propio formulario, esto lo haremos de dos formas: usando el diseñador de Windows Forms, ya que esta será la manera más habitual de hacerlo, y la otra escribiendo el código directamente, ya que así empezaremos a adentrarnos en este mundo de los eventos y delegados, con idea de que lo que expliquemos a continuación sea más fácil de comprender.

Asociar un evento con un control


Cuando trabajamos con el diseñador de formularios, tenemos dos formas de asociar un evento con el código.

La primera es haciendo doble pulsación sobre el control en cuestión, de esta forma se creará un método que asociará el evento que esté predeterminado para ese control.

Por ejemplo, si hacemos doble-click en un botón, el evento predeterminado es el evento Click. Si esa misma acción la realizamos en una caja de textos, el evento predeterminado es TextChanged.

Si queremos asociar otros eventos con esos controles, tenemos que recurrir a la ventana de propiedades del diseñador de Windows Forms, que tal como podemos comprobar en la figura 2.12, nos muestra, entre otras cosas, un rayo el cual nos servirá para poder indicar los diferentes eventos que queramos interceptar en el control que actualmente tengamos seleccionado en el formulario.



En la figura 2.13 podemos ver algunos de los eventos que tenemos disponibles para un control TextBox.


Para crear un evento, simplemente haremos doble-click en el que nos interesa y el diseñador de formularios nos mostrará el método en el que podemos escribir nuestro código para cuando se produzca ese evento:


private void textBox1_TextChanged(object sender, EventArgs e)
{

}


Pero además de eso, el diseñador de formularios de Visual Studio .NET también ha añadido una línea de código que es la que realmente hace que se asocie un método con un evento, en el caso del evento TextChanged del control textBox1 que tenemos en el formulario, el código sería el siguiente:


this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);


Como vemos en los dos trozos de código, hay una relación entre el método que utilizaremos cuando se produzca el evento, el cual utiliza dos parámetros, que es la forma habitual de hacerlo en .NET. El primer parámetro es de tipo object y representa al control que ha producido el evento, el segundo parámetro es de una clase derivada de EventHandler que en algunos casos nos puede proporcionar información extra sobre el evento que se ha producido.

Para asociar el evento con ese método, en C# se utiliza el operador += seguido de la creación de un objeto al que se le pasa como argumento el nombre del procedimiento a usar para interceptar el evento.

La definición de la "clase" EventHandler que podemos encontrar en la ayuda de Visual C# .NET, es la siguiente:



public delegate void EventHandler(object sender, EventArgs e);


Como podemos apreciar realmente no es una clase normal, sino que es un "delegado". Y como también podemos ver, en ningún sitio se indica que reciba un solo parámetro. Aunque de esto nos ocuparemos más adelante, por ahora solo debemos saber que en C# los eventos siempre están ligados a los delegados.
Si seguimos buscando en la ayuda de Visual C# .NET, nos encontramos con esta definición del evento TextChanged:


public event EventHandler TextChanged;


Es decir, el evento TextChanged es del tipo del delegado EventHandler.
Para mantener un poco el suspense, dejemos aquí las explicaciones. Por ahora, simplemente debemos saber que para asociar un evento con un método, debemos usar el operador += para asignar el "manejador" de eventos al evento en cuestión, y lo que asignamos es un nuevo objeto basado en un tipo declarado como delegado al que le pasamos el nombre del procedimiento que se usará cuando se produzca el evento.

Asociar varios eventos a un mismo procedimiento



Algunas veces nos encontraremos en la situación de que estamos utilizando el mismo código para interceptar el mismo tipo de evento en controles diferentes, en esos casos nos puede interesar utilizar un solo método, en el que haremos el trabajo.

Asociarlos usando el diseñador de Windows Forms


La forma más fácil de asociar un mismo método con varios controles es hacerlo usando la ventana de propiedades, ver figura 2.13, en esa ventana de propiedades, además de crear nuevos métodos donde interceptar los eventos, podemos asociar un evento con un método ya existente.
Por ejemplo, si tenemos varios controles del tipo TextBox en los que nos interesa utilizar un solo método para seleccionar todo el texto cuando el control recibe el foco, podríamos escribir este código que inicialmente estaría asociado con el evento Enter del control textBox1:


private void textBox1_Enter(object sender, EventArgs e)
{
    ((TextBox)sender).SelectAll();
}


Lo que tenemos que hacer es lo siguiente:
-Seleccionamos el control al que queremos asociar ese mismo evento.
-En la ventana de propiedades, (con la opción de eventos seleccionada), buscamos el evento Enter, y en lugar de hacer doble pulsación, (porque si lo hacemos crearía un nuevo procedimiento), lo que tenemos que hacer es presionar el combo que es lo que se usa para indicar el método usado, de la lista desplegable que se muestra, (ver figura 2.14), seleccionamos el método que nos interese. En nuestro ejemplo, seleccionaríamos textBox1_Enter.





Como podemos apreciar en la figura 2.14, en la lista desplegable se muestran tres métodos, esto es así porque todos ellos tienen los parámetros adecuados al tipo de evento que hemos seleccionado.


Asociarlos usando código



Si en lugar de asociar un mismo método usando el diseñador de Windows Forms, queremos hacerlo por código, lo podemos añadir en el constructor del formulario después de la llamada al método InitializeComponent().
El IDE de Visual C# .NET nos ayuda a la hora de crear el manejador de eventos, tal como podemos ver en la figura 2.15:









Figura 2.15. Ayuda del IDE de C# para crear manualmente manejadores de eventos


Si presionamos la tecla TAB se añadirá automáticamente el código necesario para "ligar" ese evento con el método que el IDE de C# "cree" que debe ser, en este caso, sería textBox2_Enter, pero una vez que el IDE ha añadido el código podemos cambiarlo para que apunte al que realmente nos interesa.

En caso de que ese evento ya esté asociado con otro método, el IDE nos avisará de ese hecho.


EVENTOS Y DELEGADOS EN CSHARP | C# .NET


EVENTOS Y DELEGADOS EN CSHARP | C# .NET


Introducción

La forma que tienen nuestras clases y estructuras de comunicar que algo está ocurriendo, es por medio de eventos. Los eventos son mensajes que se lanzan desde una clase para informar al "cliente" que los utiliza de que está pasando algo.



Seguramente estaremos acostumbrados a usarlos, incluso sin tener una noción consciente de que se tratan de eventos, o bien porque es algo tan habitual que no le prestamos mayor atención, es el caso de las aplicaciones de escritorio, cada vez que presionamos un botón, escribimos algo o movemos el mouse se están produciendo eventos.

En Visual C# .NET podemos definir eventos en nuestras clases, pero como comprobaremos, estos están totalmente relacionados con los delegados.

En esta lección veremos que son los delegados y que relación tienen con los eventos, también veremos que podemos tener mayor control sobre cómo se interceptan los eventos e incluso cómo y cuándo se asocian los eventos en la aplicación cliente, aunque primero empezaremos viendo cómo declarar y utilizar eventos en nuestros tipos de datos.

Eventos y delegados

  • Eventos
    • Interceptar los eventos de los controles de un formulario
    • Asociar un evento con un control
    • Asociar varios eventos a un mismo procedimiento
      • Asociarlos usando el diseñador de Windows Forms
      • Asociarlos usando código
  • Delegados
    • Definición "formal" de delegado
    • Utilizar un delegado para acceder a un método
    • Cómo producir eventos
    • Los delegados y los métodos anónimos
    • ¿Qué ocurre cuando se asigna y se produce un evento?

TRY CATCH FINALLY EN CSHARP | C# .NET

Manejo de Excepciones en C# .NET

Es indiscutible que por mucho que nos lo propongamos, nuestras aplicaciones no estarán libres de errores, y no nos referimos a errores sintácticos, ya que, afortunadamente, el IDE (Integrated Development Envirnment, entorno de desarrollo integrado) de Visual Studio nos avisará de cualquier error sintáctico e incluso de cualquier asignación no válida, pero de lo que no nos avisará, como es lógico, será de los errores que se produzcan en tiempo de ejecución. Para estos casos, C# pone a nuestra disposición el manejo de excepciones, veamos pues cómo utilizarlo.


 


En Visual C# .NET se utiliza un tratamiento estructurado de excepciones, de esta forma podemos detectar los errores que se produzcan en nuestras aplicaciones de una forma más "ordenada".

Manejo de excepciones estructuradas


Las excepciones en C# las podemos controlar usando las instrucciones try / catch / finally. Estas instrucciones realmente son bloques de instrucciones, y por tanto estarán delimitadas con un par de llaves.
Cuando queramos controlar una parte del código que puede producir un error lo incluimos dentro del bloque try, si se produce un error, éste lo podemos detectar en el bloque catch, por último, independientemente de que se produzca o no una excepción, podemos ejecutar el código que incluyamos en el bloque finally.
Cuando creamos una estructura de control de excepciones no estamos obligados a usar los tres bloques, aunque el primero: try si es necesario, ya que es el que le indica al compilador que tenemos intención de controlar los errores que se produzcan. Por tanto, podemos crear un "manejador" de excepciones usando los tres bloques, usando try y catch o usando try y finally.
Veamos ahora con más detalle cada uno de estos bloques y que es lo que podemos hacer en cada uno de ellos.

Bloque try


En este bloque incluiremos el código en el que queremos comprobar los errores.
El código a usar será un código normal, es decir, no tenemos que hacer nada en especial, ya que en el momento que se produzca el error se usará (si hay) el código del bloque catch.

Bloque catch


Si se produce una excepción, ésta la capturamos en un bloque catch.
En el bloque catch podemos indicar que tipo de excepción queremos capturar, para ello usaremos una variable de tipo Exception, la cual puede ser del tipo de error específico que queremos controlar o de un tipo genérico.

Por ejemplo, si sabemos que nuestro código puede producir un error al trabajar con ficheros, podemos usar un código como éste:

try
{
    // código para trabajar con ficheros, etc.
}
catch(System.IO.IOException ex)
{
    // el código a ejecutar cuando se produzca ese error
}

Si nuestra intención es capturar todos los errores que se produzcan, es decir, no queremos hacer un filtro con errores específicos, podemos usar la clase Exception como tipo de excepción a capturar. La clase Exception es la más genérica de todas las clases para manejo de excepciones, por tanto, capturará todas las excepciones que se produzcan.

try
{
    // código que queremos controlar
}
catch(System.Exception ex)
{
    // el código a ejecutar cuando se produzca cualquier error
}

Aunque si no vamos usar la variable indicada en el bloque Catch, pero queremos que no se detenga la aplicación cuando se produzca un error, podemos hacerlo de esta forma:

try
{
    // código que queremos controlar
}
catch
{
    // el código a ejecutar cuando se produzca cualquier error
}

La variable indicada en el bloque catch la podemos usar para mostrar un mensaje al usuario o para obtener información extra sobre el error, pero no siempre vamos a hacer uso de esa variable, en ese caso podemos utilizar el código anterior, en el que no se usa una variable y tampoco se indica el tipo de error que queremos interceptar. Pero es posible que nuestra intención sea capturar errores de un tipo concreto sin necesidad de utilizar una variable, en ese caso podemos crear un bloque catch como el siguiente, en el que solo se indica el tipo de excepción:

try
{
    // código que queremos controlar
}
catch(FormatException)
{
    // interceptar los errores del tipo FormatException
}

Varias capturas de errores en un mismo bloque try/catch


En un mismo try/catch podemos capturar diferentes tipos de errores, para ello podemos incluir varios bloques catch, cada uno de ellos con un tipo de excepción diferente.

Es importante tener en cuenta que cuando se produce un error y usamos varios bloques catch, el CLR de .NET buscará la captura que mejor se adapte al error que se ha producido, pero siempre lo hará examinando los diferentes bloques catch que hayamos indicado empezando por el indicado después del bloque try, por tanto deberíamos poner las más genéricas al final, de forma que siempre nos aseguremos de que las capturas de errores más específicas se intercepten antes que las genéricas.

Aunque el propio compilador de C# detectará si hay capturas de errores genéricas antes que las más específicas, avisándonos de ese hecho.
En el siguiente código capturamos un error específico y también uno genérico, con idea de que tengamos siempre controlado cualquier error que se produzca:

try
{
    // código que queremos controlar
}
catch(FormatException)
{
    // captura de error de formato
}
catch(Exception ex)
{
    // captura del resto de errores
}

Bloque finally


En este bloque podemos indicar las instrucciones que queremos que se ejecuten, se produzca o no una excepción. De esta forma nos aseguramos de que siempre se ejecutará un código, por ejemplo, para liberar recursos, se haya producido un error o no.

En este código tenemos tres capturas de errores diferentes y un bloque finally que siempre se ejecutará, se produzca o no un error:

int i, j;
//
try
{
    Console.Write("Un numero ");
    i = Convert.ToInt32(Console.ReadLine());
    Console.Write("Otro numero ");
    j = Convert.ToInt32(Console.ReadLine());
 
    int r = i / j;
 
    Console.WriteLine("El resultado es: {0}", r);
}
catch (FormatException)
{
    Console.WriteLine("No es un número válido");
    // Salimos de la función, pero se ejecutará el finally
    return;
}
catch (DivideByZeroException)
{
    Console.WriteLine("La división por cero no está permitida.");
}
catch (Exception ex)
{
    // Captura del resto de excepciones
    Console.WriteLine(ex.Message);
}
finally
{
    // Este código siempre se ejecutará
    Console.WriteLine("Se acabó");
}

Nota:
Hay que tener en cuenta de que incluso si usamos return dentro de un bloque de control de errores, se ejecutará el código indicado en el bloque finally.


Captura de errores no controlados


Como es lógico, si no controlamos las excepciones que se puedan producir en nuestras aplicaciones, éstas serán inicialmente controladas por el propio runtime de .NET, en estos casos la aplicación se detiene y se muestra el error al usuario. Pero esto es algo que no deberíamos consentir, por tanto, siempre deberíamos detectar todos los errores que se produzcan en nuestras aplicaciones, pero a pesar de que lo intentemos, es muy probable que no siempre podamos conseguirlo.

Una forma de hacerlo es iniciando nuestra aplicación dentro de un bloque try/catch, de esta forma, cuando se produzca el error, se capturará en ese bloque catch, porque cuando el runtime de .NET se encuentra con una excepción, lo que hace es revisar "la pila" de llamadas y buscar algún try/catch, si lo encuentra, lo utiliza, y si no lo encuentra, se encarga de lanzar la excepción deteniendo el programa.

Esto es importante saberlo, no ya por detectar esos errores que no hemos tenido la previsión de controlar, sino porque es posible que, si un error se produce dentro de un método en el que no hay captura de errores, pero antes de llamar a ese método hemos usado un try/catch, el error será interceptado por ese catch, aunque posiblemente ni siquiera lo pusimos pensando que podía capturar errores producidos en otros niveles más profundos de nuestra aplicación.


*** EJEMPLOS DEL VIDEO ***

EJEMPLO 1



EJEMPLO 2



EJEMPLO 3




DELEGADOS EN C# .NET | PUNTEROS A METODOS

Delegados Tal como hemos comentado, los eventos y los delegados están muy unidos en C#. De hecho no se pueden definir eventos si no def...