Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
System.Delegate y la
En este artículo se describen las clases de .NET que admiten delegados y cómo se asignan a la delegate
palabra clave .
Definición de tipos de delegado
Comencemos con el término "delegate", ya que mayormente eso es lo que utilizará al trabajar con delegados. El código que genera el compilador cuando se utiliza la palabra clave delegate
se asignará a las llamadas a métodos que invocan a miembros de las clases Delegate y MulticastDelegate.
Defina un tipo delegado mediante la sintaxis similar a la definición de una firma de método. Solo tiene que agregar la delegate
palabra clave a la definición.
Vamos a seguir usando el método List.Sort() como ejemplo. El primer paso es crear un tipo para el delegado de comparación:
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
El compilador genera una clase, derivada de System.Delegate
que coincide con la firma usada (en este caso, un método que devuelve un entero y tiene dos argumentos). El tipo de ese delegado es Comparison
. El Comparison
tipo delegado es un tipo genérico. Para obtener más información, vea Clases y métodos genéricos.
Observe que la sintaxis puede aparecer como si declarara una variable, pero realmente declara un tipo. Puede definir tipos delegados dentro de clases, directamente dentro de espacios de nombres o incluso en el espacio de nombres global.
Nota:
No se recomienda declarar tipos delegados (u otros tipos) directamente en el espacio de nombres global.
El compilador también genera controladores de adición y eliminación para este nuevo tipo para que los clientes de esta clase puedan agregar y quitar métodos de la lista de invocación de una instancia. El compilador aplicará que la firma del método que se va a agregar o quitar coincide con la firma usada al declarar el método.
Declarar instancias de delegados
Después de definir el delegado, puede crear una instancia de ese tipo. Al igual que todas las variables de C#, no se pueden declarar instancias de delegado directamente en un espacio de nombres o en el espacio de nombres global.
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
El tipo de la variable es Comparison<T>
, el tipo de delegado definido anteriormente. El nombre de la variable es comparator
.
Ese fragmento de código anterior declaró una variable miembro dentro de una clase. También puede declarar variables de delegado que son variables locales o argumentos para los métodos.
Invocar delegados
Se invocan los métodos que se encuentran en la lista de invocación de un delegado llamando a ese delegado. Dentro del Sort()
método , el código llamará al método de comparación para determinar el orden de colocar objetos:
int result = comparator(left, right);
En la línea anterior, el código invoca el método adjunto al delegado. La variable se trata como un nombre de método y se invoca mediante la sintaxis de llamada al método normal.
Esa línea de código hace una suposición arriesgada: no hay ninguna garantía de que se haya agregado un objetivo al delegado. Si no se han adjuntado destinos, la línea anterior generaría una excepción NullReferenceException
. Las expresiones usadas para solucionar este problema son más complicadas que una comprobación de valores NULL simple y se tratan más adelante en esta serie.
Asignar, agregar y quitar destinos de invocación
Así es como se define un tipo de delegado y cómo se declaran e invocan las instancias de delegado.
Los desarrolladores que quieran usar el List.Sort()
método deben definir un método cuya firma coincida con la definición del tipo delegado y asignarla al delegado usado por el método de ordenación. Esta asignación agrega el método a la lista de invocación de ese objeto delegado.
Supongamos que quería ordenar una lista de cadenas por su longitud. La función de comparación puede ser la siguiente:
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
El método se declara como un método privado. Esto es correcto, Es posible que no desee que este método forme parte de la interfaz pública. Todavía se puede usar como método de comparación cuando se adjunta a un delegado. El código de llamada tendrá este método asociado a la lista de destino del objeto delegado y puede acceder a él a través de ese delegado.
Para crear esa relación, pase ese método al List.Sort()
método :
phrases.Sort(CompareLength);
Observe que se usa el nombre del método, sin paréntesis. El uso del método como argumento indica al compilador que convierta la referencia del método en una referencia que se puede usar como destino de invocación de delegado y adjunte ese método como destino de invocación.
También podría haber sido explícito declarando una variable de tipo Comparison<string>
y realizando una asignación:
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
En usos donde el método utilizado como destino de delegado es pequeño, es habitual usar la sintaxis de expresión lambda para realizar la asignación.
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
El uso de expresiones lambda para destinos delegados se trata más en una sección posterior.
El ejemplo Sort() normalmente asocia un único método de destino al delegado. Sin embargo, los objetos delegados admiten listas de invocación que tienen varios métodos de destino asociados a un objeto delegado.
Clases de Delegate y MulticastDelegate
La compatibilidad con idiomas descrita anteriormente proporciona las características y la compatibilidad que normalmente necesitará para trabajar con delegados. Estas características se basan en dos clases del marco de .NET Core: Delegate y MulticastDelegate.
La System.Delegate
clase y su única subclase directa, System.MulticastDelegate
, proporcionan la compatibilidad del marco para crear delegados, registrar métodos como destinos delegados e invocar todos los métodos registrados como destino delegado.
Interesantemente, las System.Delegate
clases y System.MulticastDelegate
no son tipos delegados por sí mismos. Proporcionan la base para todos los tipos de delegado específicos. Ese mismo proceso de diseño del lenguaje estableció que no se puede declarar una clase que derive de Delegate
o MulticastDelegate
. Las reglas del lenguaje C# lo prohíben.
En su lugar, el compilador de C# crea instancias de una clase derivada de MulticastDelegate
cuando se usa la palabra clave del lenguaje C# para declarar tipos delegados.
Este diseño tiene sus raíces en la primera versión de C# y .NET. Un objetivo para el equipo de diseño era garantizar que el lenguaje aplicara la seguridad de tipos al usar delegados. Esto significaba asegurarse de que los delegados fueran invocados con el tipo y número correctos de argumentos. Además, cualquier tipo de valor devuelto se indicó correctamente en tiempo de compilación. Los delegados formaban parte de la versión 1.0 de .NET, que era anterior a los genéricos.
La mejor manera de aplicar esta seguridad de tipos era que el compilador creara las clases de delegados concretas que representaban la firma del método que se usa.
Aunque no puede crear clases derivadas directamente, usará los métodos definidos en estas clases. Veamos los métodos más comunes que usará al trabajar con delegados.
El primer hecho más importante que debe recordar es que cada delegado con el que trabaja se deriva de MulticastDelegate
. Un delegado de multidifusión significa que se puede invocar más de un destino de método al invocar a través de un delegado. El diseño original consideró la distinción entre delegados en los que solo se podía adjuntar e invocar un método de destino, y delegados en los que se podían adjuntar e invocar varios métodos de destino. Esa distinción resultó ser menos útil en la práctica de lo que se pensaba originalmente. Las dos clases diferentes ya fueron creadas y han estado en el marco desde su lanzamiento público inicial.
Los métodos que usará más con delegados son Invoke()
y BeginInvoke()
/ EndInvoke()
.
Invoke()
invocará todos los métodos que se han asociado a una instancia de delegado determinada. Como ha visto anteriormente, normalmente se invocan delegados mediante la sintaxis de llamada de método en la variable de delegado. Como verá más adelante en esta serie, hay patrones que funcionan directamente con estos métodos.
Ahora que has visto la sintaxis del lenguaje y las clases que admiten delegados, examinemos cómo se utilizan, crean e invocan los delegados con tipo estricto.