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.
Un delegado define un tipo que representa referencias a métodos que tienen una lista de parámetros determinada y un tipo de valor devuelto. Un método (estático o de instancia) que tenga una lista de parámetros y un tipo de valor de retorno coincidentes se puede asignar a una variable de ese tipo; luego puede llamarse directamente (con los argumentos adecuados) o pasarse como argumento a otro método para luego ser llamado. El siguiente ejemplo demuestra el uso de delegados.
using System;
using System.Linq;
public class Program
{
public delegate string Reverse(string s);
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Reverse rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
- La
public delegate string Reverse(string s);
línea crea un tipo delegado de un método que toma un parámetro de cadena y, a continuación, devuelve un parámetro de cadena. - El
static string ReverseString(string s)
método , que tiene exactamente la misma lista de parámetros y tipo de valor devuelto que el tipo de delegado definido, implementa el delegado. - La
Reverse rev = ReverseString;
línea muestra que puede asignar un método a una variable del tipo delegado correspondiente. - En la
Console.WriteLine(rev("a string"));
línea se muestra cómo usar una variable de un tipo delegado para invocar al delegado.
Para simplificar el proceso de desarrollo, .NET incluye un conjunto de tipos delegados que los programadores pueden reutilizar y no tener que crear nuevos tipos. Estos tipos son Func<>
, Action<>
y Predicate<>
, y se pueden usar sin tener que definir nuevos tipos de delegado. Existen algunas diferencias entre los tres tipos que tienen que ver con la manera en que se han diseñado para usarse:
-
Action<>
se usa cuando es necesario realizar una acción mediante los argumentos del delegado. El método que encapsula no devuelve un valor. -
Func<>
se usa normalmente cuando se tiene una transformación a mano, es decir, es necesario transformar los argumentos del delegado en un resultado diferente. Las proyecciones son un buen ejemplo. El método que encapsula devuelve un valor especificado. -
Predicate<>
se usa cuando es necesario determinar si el argumento cumple la condición del delegado. También se puede escribir como ,Func<T, bool>
lo que significa que el método devuelve un valor booleano.
Ahora podemos tomar nuestro ejemplo anterior y reescribirlo utilizando el delegado Func<>
en vez de un tipo personalizado. El programa seguirá ejecutándose exactamente igual.
using System;
using System.Linq;
public class Program
{
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Func<string, string> rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
En este ejemplo sencillo, tener un método definido fuera del Main
método parece un poco superfluo. .NET Framework 2.0 introdujo el concepto de delegados anónimos, que permiten crear delegados "insertados" sin tener que especificar ningún tipo o método adicionales.
En el ejemplo siguiente, un delegado anónimo filtra una lista solo a los números par y, a continuación, los imprime en la consola.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(
delegate (int no)
{
return (no % 2 == 0);
}
);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
Como puede ver, el cuerpo del delegado es simplemente un conjunto de expresiones, como cualquier otro delegado. Pero en lugar de ser una definición independiente, la hemos introducido ad hoc en la llamada al List<T>.FindAll método .
Sin embargo, incluso con este enfoque, todavía hay mucho código que podemos tirar. Aquí es donde entran en juego las expresiones lambda . Las expresiones lambda, o simplemente "lambdas" para abreviar, se introdujeron en C# 3.0 como uno de los bloques fundamentales de Language Integrated Query (LINQ). Constituyen una sintaxis más cómoda para el uso de delegados. Declaran una lista de parámetros y un cuerpo del método, pero no tienen una identidad formal propia, a menos que se asignen a un delegado. A diferencia de los delegados, se pueden asignar directamente como lado izquierdo del registro de eventos, o bien en distintas cláusulas y métodos de LINQ.
Dado que una expresión lambda es simplemente otra manera de especificar un delegado, deberíamos poder volver a escribir el ejemplo anterior para usar una expresión lambda en lugar de un delegado anónimo.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(i => i % 2 == 0);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
En el ejemplo anterior, la expresión lambda usada es i => i % 2 == 0
. De nuevo, es solo una sintaxis cómoda para usar delegados. Lo que sucede en segundo plano es similar a lo que sucede con el delegado anónimo.
De nuevo, las expresiones lambda son solo delegados, lo que significa que se pueden usar como controlador de eventos sin problemas, como se muestra en el siguiente fragmento de código.
public MainWindow()
{
InitializeComponent();
Loaded += (o, e) =>
{
this.Title = "Loaded";
};
}
El +=
operador de este contexto se usa para suscribirse a un evento. Para obtener más información, consulte Cómo suscribirse a eventos y cancelar la suscripción.