Utilizar delegados

Utilizar delegados (Guía de programación de C#) Visual Studio 2013 Otras versiones Un delegado es un tipo que encapsula

Views 138 Downloads 3 File size 272KB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

Utilizar delegados (Guía de programación de C#) Visual Studio 2013 Otras versiones

Un delegado es un tipo que encapsula de forma segura un método, similar a un puntero de función en C y C++. A diferencia de los punteros de función de C, los delegados están orientados a objetos, proporcionan seguridad de tipos y son seguros. El tipo de un delegado se define por el nombre del delegado. En el ejemplo siguiente se declara un delegado denominado Del que puede encapsular un método que toma una cadena como argumento y devuelve void: C# Copiar public delegate void Del(string message);

Normalmente, un objeto delegado se construye con el nombre del método que el delegado encapsulará o con un método anónimo. Una vez que se instancia un delegado, una llamada al método hecha al delegado será pasada por el delegado a ese método. Los parámetros pasados al delegado por el autor de la llamada se pasan a su vez al método, y el valor devuelto desde el método, si lo hubiera, es devuelto por el delegado al autor de la llamada. Esto se conoce como invocar al delegado. Un delegado con instancias se puede invocar como si fuera el propio método encapsulado. Por ejemplo: C# Copiar // Create a method for a delegate. public static void DelegateMethod(string message) { System.Console.WriteLine(message); }

C# Copiar // Instantiate the delegate. Del handler = DelegateMethod;

// Call the delegate. handler("Hello World");

Los tipos de delegado se derivan de la clase Delegate en .NET Framework. Los tipos de delegados son sealed —no se puede derivar— y no es posible derivar clases personalizadas de Delegate. Dado que el delegado con instancias es un objeto, puede pasarse como parámetro o asignarse a una propiedad. De este modo, un método puede aceptar un delegado como parámetro y llamar al delegado en algún momento posterior. Esto se conoce como devolución de llamada asincrónica y es un método común para notificar a un llamador que un proceso largo ha finalizado. Cuando se utiliza un delegado de esta manera, el código que usa al delegado no necesita ningún conocimiento de la implementación del método empleado. La funcionalidad es similar a la encapsulación que proporcionan las interfaces. Otro uso común de devoluciones de llamada es definir un método de comparación personalizado y pasar ese delegado a un método de ordenación. Permite que el código del llamador se convierta en parte del algoritmo de ordenación. En el siguiente método de ejemplo se usa el tipo Del como parámetro: C# Copiar public void MethodWithCallback(int param1, int param2, Del callback) { callback("The number is: " + (param1 + param2).ToString()); }

Luego puede pasar el delegado creado anteriormente a ese método: C# Copiar MethodWithCallback(1, 2, handler);

Y recibir los siguientes resultados en la consola: The number is: 3 Si se usa el delegado como abstracción, no es necesario que MethodWithCallback llame directamente a la consola —no tiene que estar diseñado pensando en una consola—. Lo que MethodWithCallback hace es simplemente preparar una cadena y pasarla a otro método. Esto es especialmente eficaz, puesto que un método delegado puede utilizar cualquier número de parámetros.

Cuando se crea un delegado para encapsular un método de instancia, el delegado hace referencia tanto a la instancia como al método. Un delegado no tiene conocimiento del tipo de instancia —aparte del método al que encapsula—, por lo que un delegado puede hacer referencia a cualquier tipo de objeto siempre que haya un método en ese objeto que coincida con la signatura del delegado. Cuando se crea un delegado para encapsular un método estático, solo hace referencia al método. Considere las siguientes declaraciones: C# Copiar public class MethodClass { public void Method1(string message) { } public void Method2(string message) { } }

Junto con el DelegateMethod estático mostrado anteriormente, ahora tenemos tres métodos que se pueden encapsularse mediante una instancia de Del. Un delegado puede llamar a más de un método cuando se invoca. Esto se conoce como multidifusión. Para agregar un método adicional a la lista de métodos del delegado —la lista de invocación—, simplemente es necesario agregar dos delegados mediante los operadores de adición o asignación y suma ('+' o '+='). Por ejemplo: C# Copiar MethodClass obj = new MethodClass(); Del d1 = obj.Method1; Del d2 = obj.Method2; Del d3 = DelegateMethod; //Both types of assignment are valid. Del allMethodsDelegate = d1 + d2; allMethodsDelegate += d3;

En este momento allMethodsDelegate contiene tres métodos en su lista de invocación: Method1, Method2 y DelegateMethod. Los tres delegados originales, d1, d2 y d3, permanecen sin cambios. Cuando se invoca allMethodsDelegate, todos los métodos se llaman en orden. Si el delegado usa parámetros de referencia, la referencia se pasa secuencialmente a cada uno de los tres métodos por turnos, y cualquier cambio que realice un método es visible para el siguiente método. Cuando alguno de los métodos produce una excepción que no se captura dentro del método, esa excepción se pasa al llamador del delegado y no se llama a los métodos siguientes de la lista de invocación. Si el delegado tiene un valor devuelto o los parámetros de salida, devuelve el valor devuelto y los parámetros del último método invocado. Para quitar un método de la lista de invocación,

utilice el operador de decremento o el operador de asignación de decremento ('-' o '-= '). Por ejemplo: C# Copiar //remove Method1 allMethodsDelegate -= d1; // copy AllMethodsDelegate while removing d2 Del oneMethodDelegate = allMethodsDelegate - d2;

Dado que los tipos de delegado se derivan de System.Delegate, los métodos y propiedades definidas por esa clase se pueden llamar en el delegado. Por ejemplo, para buscar el número de métodos en la lista de invocación de un delegado, se puede escribir: C# Copiar int invocationCount = d1.GetInvocationList().GetLength(0);

Los delegados con más de un método en su lista de invocación derivan de MulticastDelegate, que es una subclase de System.Delegate. El código anterior funciona en ambos casos porque las dos clases admiten GetInvocationList. Los delegados de multidifusión se utilizan mucho en el control de eventos. Los objetos de origen de evento envían notificaciones de evento a los objetos de destinatario que se han registrado para recibir ese evento. Para suscribirse a un evento, el destinatario crea un método diseñado para controlar el evento; a continuación, crea a un delegado para dicho método y pasa el delegado al origen de eventos. El origen llama al delegado cuando se produce el evento. Después, el delegado llama al método que controla los eventos en el destinatario y entrega los datos del evento. El origen del evento define el tipo de delegado para un evento determinado. Para más información, vea Eventos (Guía de programación de C#). La comparación de delegados de dos tipos distintos asignados en tiempo de compilación generará un error de compilación. Si las instancias de delegado son estáticamente del tipo System.Delegate, entonces se permite la comparación, pero devolverá false en tiempo de ejecución. Por ejemplo: C# Copiar delegate void Delegate1(); delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e, System.Delegate f) { // Compile-time error. //Console.WriteLine(d == e); // OK at compile-time. False if the run-time type of f // is not the same as that of d. System.Console.WriteLine(d == f); }

Vea también Referencia Delegados (Guía de programación de C#) Utilizar varianza en delegados (C# y Visual Basic) Usar la varianza para los delegados genéricos Func y Action (C# y Visual Basic) Eventos (Guía de programación de C#) Conceptos Guía de programación de C# Varianza en delegados (C# y Visual Basic)

Adiciones de comunidad •Declaración de un evento Para declarar un evento dentro de una clase, primero debe declararse un tipo delegado para el evento si aún no existe uno declarado. public delegate void ChangedEventHandler(object sender, EventArgs e); El tipo delegado define el conjunto de argumentos que se pasan al método que realiza el tratamiento del evento. Varios eventos pueden compartir el mismo tipo delegado, de modo que este paso sólo es necesario si aún no se ha declarado ningún tipo delegado.

A continuación, se declara el propio evento.

public event ChangedEventHandler Changed;

Un evento se declara como un campo de tipo delegado, con la diferencia de que la palabra clave event precede a la declaración de evento, a continuación de los modificadores. Los eventos se suelen declarar como públicos, pero se permite cualquier otro modificador de accesibilidad.

•Llamada a un evento Una vez que una clase derivada ha declarado un evento, puede tratar el evento simplemente como un campo del tipo delegado indicado. El campo puede tomar el valor null, si ningún cliente ha enlazado un delegado al evento, o bien un valor que hace referencia al delegado que se debería utilizar cuando se produce el evento. De este modo, la llamada a un evento se realiza generalmente comprobando primero el valor null y después llamando al evento. if (Changed != null) Changed(this, e); La llamada a un evento sólo se puede hacer desde dentro de la clase que declaró el evento. •Enlace a un evento Desde fuera de la clase que lo declaró, un evento es similar a un campo, pero el acceso a ese campo es muy restringido. Las únicas cosas que se pueden hacer son: •Componer un nuevo delegado sobre ese campo. •Quitar un delegado de un campo (posiblemente compuesto). Esto se hace mediante los operadores += y -=. Para empezar a recibir llamadas a eventos, el código del cliente crea primero un delegado del tipo del evento que hace referencia al método que se debería invocar desde el evento. A continuación, adjunta ese delegado, mediante el operador +=, a cualquier otro delegado con el que el evento deba estar conectado. // Add "ListChanged" to the Changed event on "List": List.Changed += new ChangedEventHandler(ListChanged); Cuando el código del cliente termina de recibir llamadas al evento, quita su delegado del evento mediante el operador -=. // Detach the event and delete the list: List.Changed -= new ChangedEventHandler

/w EPDw ULLTEw



Inicio



Articulos y noticias



Programacion



Bases de datos



Foros



Internet



Servicios



Contratacion



Mapa



Delegados en C#. Introducción. Uno de los temas que más suelen confundir a los nuevos programadores de la plataforma .NET, al menos a mí, son los delegados. Para los programadores de C++ el concepto es muy similar al de los punteros a funciones, los programadores de JavaScript tienen una funcionalidad parecida a través de objetos Function (que rara vez se utilizan), pero para el resto es totalmente nuevo. Lo primero que vamos a hacer es intenter definir que son los delegados. Una buena definición seria: Los delegados son un tipo que representa a una función con una determinada declaración. Vamos a analizar esta definición poco a poco, ya que dice mucho en muy pocas palabras ... 

Los delegados son un TIPO.



Los delegados representan a una función.



La función debe tener una determinada declaración. Si un delegado es un tipo, entonces puedo crear variables de ese tipo. También hemos dicho que un delegado representa a una función, por lo que una variable creada de un tipo delegado es la representación de una determinada función. Por úlitmo hemos dicho que esta función debe tener una determinada declaración.

Un ejemplo sencillo. Veamos un ejemplo que es como mejor se ven las cosas:

using System; namespace Devjoker.Ejemplos { delegate int myDelegate(int arg) ; } En este caso hemos declarado un tipo delegado, al que hemos llamado myDelegate que representa a una función que devuelve un entero y que recibe como argumento también un entero. Como es un tipo no es necesario definirlo dentro de una clase. Como resultado vamos a poder declarar variables del tipo myDelegate. La siguiente pregunta seria,¿Como declaro variables de tipo myDelegate? ¿y como asigno "valores" a esa variable?. Los delegados se declaran como cualquier otro objeto en .NET, recordemos que todo en .NET son objetos y los delegados no son una excepción. Para asignar un delegado, no debemos asignar un "valor", sino un método, normalmente una funcion. El método al que asignemos el delegado no puede ser una función cualquiera, debe cumplir con la declaración del delegado. La forma de asignarlo es a través del constructor del delegado, pasando como argumento la función. A continuacion mostramos dos posibles funciones que cumplen con la declaración del delegado:

private int delegateFunction1(int a) { Console.WriteLine("Ejecutando ... delegateFuntion1"); return ++a; } private int delegateFunction2(int a) { Console.WriteLine("Ejecutando ... delegateFuntion2"); return a; }

Como podemos ver tanto delegateFuntion1 como delegateFunction2 son functiones que devuelven un tipo int y reciben como parametro un int, que es la forma en la que hemos declarado el delegado. La declaración de una variable con el tipo delegado, es la siguiente, como cualquier otra variable en C#:

myDelegate variableDelegado; Ahora vamos a ver como asignar un delegado. Hemos dicho que para asignar un delegado necesitamos utilizar el constructor del mismo. En el siguiente ejemplo vamos a ver un pequeño programa completo que declara y ejecuta un delegado: using System; namespace Devjoker.Ejemplos { delegate int myDelegate(int arg) ; class MainClass { public static void Main(string[] args) { MainClass instancia = new MainClass(); myDelegate variableDelegado; variableDelegado = new myDelegate(instancia.delegateFunction1); int a = variableDelegado(5); System.Console.WriteLine("El resultado obtenido es ... {0}",a); } private int delegateFunction1(int a) { Console.WriteLine("Ejecutando ... delegateFuntion1"); return ++a; } private int delegateFunction2(int a) { Console.WriteLine("Ejecutando ... delegateFuntion2"); return a; } } } En este ejemplo declaramos una variable variableDelegado y la asignamos a la función delegateFunction1. Debido a que el método Main es estático, pero delegateFunction1 no lo es, necesitamos una instancia de la clase MainClass para referiros a la función de forma concreta. Por úlitmo ejecutamos nuestra variable (con un parámetro fijo 5) y mostramos el resultado en pantalla. El programa genera una salida similar a la siguiente:

Ejecutando ... delegateFuntion1 El resultado obtenido es ... 6 Presione una tecla para continuar . . .

Seguro que alguno de vosotros está pensando en que esto mismo podría hacerse invocando a la funcion sin más, y no andar con tanto lio. Por supesto, pero este es un ejemplo muy sencillo y no pretende mostrar los casos complicados, recordemos que queremos aprender que son y para que sirven los delegados. Un caso más complejo podría ser el siguiente, Imaginemos un control creado dinamicamente por el programa, sobre el que queramos programar su click ... La forma de hacerlo es con delegados. La función a la que asignamos el delegado no debe pertenecer a una clase en concreto, ni siquiera a un assemblie en particular, solo debe cumplir la declaración del delegado. Vamos ahora a ver un ejemplo algo más practico.

Un ejemplo practico. El siguiente ejemplo, sin pretender entrar en mucha complejidad, es un sencillo programa que realiza su log de proceso bien por pantalla o en un fichero. El programa tiene tres clases: 

MainClass, que es la clase que contiene el método main.



ProgramClass, que es la clase que contiene el programa principal.



FormasLog, que es la clase que contiene las diferentes formas para realizar el log del proceso. El programa tiene además un delegado, que vamos a utilizar para realizar el log del proceso.Tienbe la siguiente forma:

delegate void logDelegate(string arg); Veamos cada una de estas clases. Empezaremos con la clase FormasLog, que tiene dos métodos (que coinciden con la declaración del delegado), ConsoleLog y FileLog. ConsoleLog muestra un mensaje en pantalla y FileLog, escribe el mensaje en un fichero (ubicado en la misma ruta que el assemblie). El codigo de la clase es el siguiente:

class FormasLog { private static StreamWriter fw; public static void ConsoleLog(string texto) { System.Console.WriteLine(texto); } public static void FileLog(string texto) { try { bool append = true; if (fw == null) { append = false; } fw = new StreamWriter( "ficherolog.log",append); fw.WriteLine(texto); fw.Close(); } catch (IOException e) { FormasLog.ConsoleLog(e.Message); } } }

La clase ProgramClass tiene los siguientes métodos y miembros: 

logFunction, que es un miembro de tipo logDelegate y es la función encargada de realizar el log



Run, que es el método principal de la clase, unicamente comprueba que logFunction está asignada e itera 100 veces llamando al método Log.



SetLogMethod, este método es el más importante de todos, ya que recibe un parámetro de tipo logDelegate y lo asigna a logFunction para que pueda hacer su trabajo de forma correcta.



Log, este método ejecuta logFunction. El código completo de la clase se muestra a continuacion:

class ProgramClass { logDelegate logFunction; public void Run() { if (logFunction == null)

{ logFunction = new logDelegate(FormasLog.ConsoleLog); } int i = 0; do { logFunction( "Este es el log! en la iteracion " + i.ToString()); } while (i++