Un delegado es una forma de puntero de función de tipo seguro que utiliza la Infraestructura de lenguaje común (CLI). Los delegados especifican un método al que llamar y, opcionalmente, un objeto en el que llamar al método. Los delegados se utilizan, entre otras cosas, para implementar devoluciones de llamadas y detectores de eventos . Un objeto delegado encapsula una referencia a un método. El objeto delegado se puede pasar a un código que puede llamar al método referenciado , sin tener que saber en tiempo de compilación qué método se invocará.
Un delegado de multidifusión es un delegado que apunta a varios métodos. [1] [2] La delegación de multidifusión es un mecanismo que proporciona funcionalidad para ejecutar más de un método. Existe una lista de delegados que se mantiene internamente y, cuando se invoca el delegado de multidifusión, se ejecuta la lista de delegados.
En C#, los delegados se utilizan a menudo para implementar devoluciones de llamadas en la programación basada en eventos. Por ejemplo, un delegado puede utilizarse para indicar qué método debe llamarse cuando el usuario hace clic en algún botón. Los delegados permiten al programador notificar a varios métodos que se ha producido un evento. [3]
Código para declarar un delegatetipo, llamado SendMessageDelegate, que toma a Messagecomo parámetro y devuelve void:
delegado void SendMessageDelegate ( Mensaje mensaje );
Código para definir un método que toma un delegado instanciado como argumento:
void SendMessage ( SendMessageDelegate sendMessageDelegateReference ) { // Llamar al delegado y a cualquier otro delegado encadenado sincrónicamente. sendMessageDelegateReference ( new Message ( "hola, este es un mensaje de muestra" )); }
El método implementado que se ejecuta cuando se llama al delegado:
void HandleSendMessage ( Message message ) { // La implementación de las clases Sender y Message no es relevante para este ejemplo. Sender.Send ( mensaje ) ; }
Código para llamar al método SendMessage, pasando un delegado instanciado como argumento:
EnviarMensaje ( nuevo SendMessageDelegate ( ManejarEnviarMensaje ));
delegado void Notificador ( cadena remitente ); // Firma de método normal con la palabra clave delegate Notificador saludame ; // Delegar variable void HowAreYou ( string remitente ) { Console . WriteLine ( "Cómo estás, " + remitente + '?' ); } saludame = nuevo notificador ( ¿Cómo estás ?);
Una variable delegada llama al método asociado y se llama de la siguiente manera:
greetingMe ( "Anton" ); // Llama a HowAreYou("Anton") e imprime "¿Cómo estás, Anton?"
Las variables delegadas son objetos de primera clase de la forma y pueden asignarse a cualquier método coincidente o al valor . Almacenan un método y su receptor sin ningún parámetro: [4]new DelegateType(obj.Method)
null
nuevo DelegateType ( funnyObj . HowAreYou );
El objeto funnyObj
puede ser this
y omitirse. Si el método es static
, no debería ser el objeto (también llamado instancia en otros lenguajes), sino la clase en sí. No debería ser abstract
, pero podría ser new
, override
o virtual
.
Para llamar a un método con un delegado con éxito, la firma del método debe coincidir DelegateType
con la misma cantidad de parámetros del mismo tipo ( ref
, out
, value
) con el mismo tipo (incluido el tipo de retorno).
Una variable delegada puede contener múltiples valores al mismo tiempo:
void HowAreYou ( string remitente ) { Console . WriteLine ( $"¿Cómo estás, {remitente}?" ); } void HowAreYouToday ( string remitente ) { Console . WriteLine ( $"¿Cómo estás hoy, {remitente}?" ); } Notificador saludame ; saludame = como estas ; saludame += comoestashoy ; GreetMe ( "Leonardo" ); // "¿Cómo estás, Leonardo?" // "¿Cómo estás hoy, Leonardo?" saludame -= comoestas ; GreetMe ( "Pereira" ); // "¿Cómo estás hoy, Pereira?"
Si el delegado de multidifusión es una función o no tiene out
parámetros, se devuelve el parámetro de la última llamada. [5]
Aunque las implementaciones internas pueden variar, las instancias de delegado pueden considerarse como una tupla de un objeto y un puntero de método y una referencia (posiblemente nula) a otro delegado. Por lo tanto, una referencia a un delegado es posiblemente una referencia a múltiples delegados. Cuando el primer delegado ha terminado, si su referencia de cadena no es nula, se invocará el siguiente, y así sucesivamente hasta que la lista esté completa. Este patrón permite que un evento tenga una sobrecarga que se escala fácilmente desde la de una sola referencia hasta la de envío a una lista de delegados, y se usa ampliamente en la CLI.
El rendimiento de los delegados solía ser mucho más lento que una llamada a un método virtual o de interfaz (de 6 a 8 veces más lento en los puntos de referencia de Microsoft de 2003), [6] pero, desde el CLR .NET 2.0 en 2005, es casi igual que las llamadas a la interfaz. [7] Esto significa que hay una pequeña sobrecarga adicional en comparación con las invocaciones de métodos directos.
Existen reglas muy estrictas para la construcción de clases delegadas. Estas reglas permiten a los compiladores optimizadores un amplio margen de maniobra a la hora de optimizar los delegados, garantizando al mismo tiempo la seguridad de los tipos. [ cita requerida ]