csharp-delegados

Qué son y cómo usar los delegados en C#

Un delegado es un tipo que representa una referencia a una función o método. Los delegados permiten que los métodos se pasen como parámetros, se asignen a variables y se ejecuten dinámicamente en tiempo de ejecución.

Para ello, el delegado debe coincidir con la firma (parámetros recibidos) por la función, y con su retorno específico.

En términos simples, un delegado puede considerarse como una especie de puntero a función, pero con la seguridad y la robustez del sistema de tipos de C#.

Si quieres aprender más sobre Referencia a Funciones
consulta el Curso de Introducción a la Programación leer más ⯈

Declaración de un delegado

Para declarar un delegado en C#, se utiliza la palabra clave delegate, seguida de la firma del método que el delegado representará.

delegate TipoRetorno MiDelegado(Tipo parametro1...);

Siendo,

  • TipoRetorno, el tipo de retorno de la función que queremos delegar
  • Tipo parametro…, los parámetros de la función que queremos delegar

Por ejemplo, si queremos declarar un delegado que represente un método que no devuelve ningún valor y toma dos parámetros enteros, la declaración se vería así:

Ejemplo básico

Por ejemplo, si quisiéramos definir un delegado que pueda apuntar a cualquier función que tome un string como parámetro y no retorne ningún valor (void), podríamos hacerlo así:

public delegate void MiDelegado(string mensaje);

Y si queremos declarar un delegado que referencia una función que tome dos parámetros int y devuelva un double la declaración se vería así:

delegate void MiDelegado(int parametro1, int parametro2);

Uso de delegados

Asignación de un método a un delegado

Una vez declarado el delegado, podemos crear una instancia del delegado y asignarle un método que coincida con su firma.

Para ello podemos crearlo usando su constructor

MiDelegado delegado = new MiDelegado(FuncionReferenciada);

O simplemente asignarlo

MiDelegado delegado = FuncionReferenciada;

Veámoslo con un ejemplo. Supongamos que tenemos la función MostrarMensaje(string ...).

// Método que queremos referenciar
public static void MostrarMensaje(string mensaje)
{
	Console.WriteLine("Delegado:" + mensaje);
}

// Definición del delegado
public delegate void MiDelegado(string mensaje);

public static void Main(string[] args)
{
	// Ahora creamos un MiDelegado que apunte a MostrarMensaje
	MiDelegado delegado = MostrarMensaje;
}

Aquí hemos,

  • Definido un delegado MiDelegado, que coincide con la forma de MostrarMensaje.
  • Creado una nueva instancia de MiDelegado, llamada delegado
  • Asignado la instancia delegado a MostrarMensaje.

Invocando un delegado

Una vez que hemos creado definido el delegado, credo un delegado, y referenciada una función con él, podemos invocarlo simplemente haciendo () como haríamos con la función original.

En el ejemplo anterior, podríamos invocar delegado simplemente así.

// Invocación del método a través del delegado
delegado("Hola, Mundo!");
}

Por lo que se mostraría en pantalla

Delegado: Hola, Mundo!

Delegados genéricos: Func y Action

Para simplificar el uso de delegados, C# dispone de delegados genéricos predefinidos. Los más comunes son Action y Func .

  • Action: Representa un método que no retorna un valor.
  • Func<TResult>: Representa un método que retorna un valor de tipo TResult.

Uso de Action

Action<string> mostrar = mensaje => Console.WriteLine(mensaje);

mostrar("Hola con Action!");

Uso de Func

Func<int, int, int> sumar = (a, b) => a + b;
int resultado = sumar(3, 4);

Console.WriteLine("Resultado de la suma: " + resultado);

En general, esta forma es la que usaréis normalmente, frente a definir el delegado explícitamente. Sobre todo si es un uso temporal, que no merece la pena definir de forma tradicional.

Delegados Multicast

Un delegado Multicast es un delegado que puede tener más de un método en su invocación. En realidad, todos los delegados de C# son Multicast

Lo vemos en esta entrada leer más ⯈

Uso de delegados con eventos

Los delegados son la base de los eventos en C#. Un evento es una notificación enviada por un objeto para señalar la ocurrencia de una acción. Los delegados permiten suscribir métodos que serán llamados cuando se dispara el evento.

Lo vemos en esta entrada leer más ⯈

Ejemplos prácticos

Delegado que no devuelve nada

Aquí tenemos un ejemplo de un delegado que no devuelve nada (void).

// Declaración del delegado que no devuelve nada
public delegate void MostrarMensajeDelegate(string mensaje);

// Método que coincide con la firma del delegado
public static void MostrarMensaje(string mensaje)
{
    Console.WriteLine(mensaje); // Imprimir el mensaje
}

// Uso
MostrarMensajeDelegate mostrarMensaje = MostrarMensaje; // Asignar el método al delegado
mostrarMensaje("Hola, Mundo!"); // Invocar el delegado

Delegado que devuelve un valor

Aquí hay un ejemplo de un delegado que devuelve un valor. En el ejemplo, el delegado toma dos enteros como parámetros y devuelve un entero.

// Declaración del delegado que devuelve un valor
public delegate int SumarDelegate(int a, int b);

// Método que coincide con la firma del delegado
public static int Sumar(int a, int b)
{
    return a + b; // Retornar la suma de los dos números
}

// Uso
SumarDelegate sumar = Sumar; // Asignar el método al delegado
int resultado = sumar(3, 5); // Invocar el delegado y obtener el resultado
Console.WriteLine($"Resultado de la suma: {resultado}"); // Imprimir el resultado

Delegado genérico Action

Veamos un ejemplo que usa un delegado genérico Action<T> para representar un método que no devuelve un valor y que puede tomar parámetros.

// Método que coincide con la firma de Action
public static void ImprimirMensaje(string mensaje)
{
    Console.WriteLine(mensaje); // Imprimir el mensaje
}

// Uso
Action<string> imprimir = ImprimirMensaje; // Asignar el método al delegado Action
imprimir("Este es un mensaje usando Action."); // Invocar el delegado

Delegado Genérico Func

Ahora un ejemplo de delegado genérico Func<T que puede representar un método que devuelve un valor y que puede tomar parámetros.

// Método que coincide con la firma de Func
public static double CalcularAreaCirculo(double radio)
{
    return Math.PI * radio * radio; // Calcular y retornar el área del círculo
}

// Uso
Func<double, double> calcularArea = CalcularAreaCirculo; // Asignar el método al delegado Func

double area = calcularArea(5.0); // Invocar el delegado y obtener el resultado
Console.WriteLine($"Área del círculo: {area}"); // Imprimir el área del círculo

Delegado como parámetro en un Método

En este ejemplo veremos como pasar un delegado como parámetro a otro método,

// Declaración del delegado que no devuelve nada
public delegate void OperacionDelegate(int x, int y);

// Método que usa un delegado como parámetro
public static void RealizarOperacion(int a, int b, OperacionDelegate operacion)
{
    operacion(a, b); // Invocar el delegado
}

// Métodos que coinciden con la firma del delegado
public static void Sumar(int a, int b)
{
    Console.WriteLine($"Suma: {a + b}");
}

public static void Multiplicar(int a, int b)
{
    Console.WriteLine($"Producto: {a * b}");
}

// Uso
OperacionDelegate operacionSuma = Sumar; // Asignar el método Sumar al delegado
OperacionDelegate operacionMultiplicacion = Multiplicar; // Asignar el método Multiplicar al delegado

RealizarOperacion(3, 4, operacionSuma); // Pasar el delegado al método
RealizarOperacion(3, 4, operacionMultiplicacion); // Pasar el delegado al método