Delegados, Eventos y Lambdas
## Objetivos de Aprendizaje
- Comprender delegados
- Trabajar con eventos
- Dominar expresiones lambda
- Aprender métodos anónimos
## Delegados
### ¿Qué es un Delegado?
Un delegado es un tipo que representa referencias a métodos con una firma específica.
```csharp
// Declaración de delegado
delegate int Calculate(int a, int b);
// Métodos que coinciden con la firma
class MathOperations
{
public static int Add(int a, int b) => a + b;
public static int Multiply(int a, int b) => a * b;
}
// Uso
Calculate calc = MathOperations.Add;
int result = calc(5, 3); // 8
calc = MathOperations.Multiply;
result = calc(5, 3); // 15
```
### Delegados Genéricos (Incorporados)
```csharp
// Func - devuelve un valor
Func add = (a, b) => a + b;
Func double = x => x * 2;
Func getName = () => "Alice";
// Action - devuelve void
Action print = Console.WriteLine;
Action multiply = (a, b) => Console.WriteLine(a * b);
// Predicate - devuelve bool
Predicate isEven = x => x % 2 == 0;
```
## Eventos
### Patrón Publicar-Suscribir
```csharp
class Button
{
// Evento público
public event EventHandler? Clicked;
public void Click()
{
Console.WriteLine("Botón clickeado");
// Lanzar evento si hay suscriptores
Clicked?.Invoke(this, EventArgs.Empty);
}
}
// Suscriptor
class Program
{
static void Main()
{
Button button = new Button();
// Suscribirse al evento
button.Clicked += OnButtonClicked;
button.Click();
}
static void OnButtonClicked(object? sender, EventArgs e)
{
Console.WriteLine("¡El botón fue clickeado!");
}
}
```
### EventHandler
### Args de Evento Personalizado
```csharp
class TemperatureEventArgs : EventArgs
{
public double Temperature { get; }
public TemperatureEventArgs(double temp) => Temperature = temp;
}
class Thermometer
{
public event EventHandler? TemperatureChanged;
public void Measure(double temp)
{
if (temp != lastTemp)
{
lastTemp = temp;
TemperatureChanged?.Invoke(this, new TemperatureEventArgs(temp));
}
}
}
// Uso
Thermometer t = new Thermometer();
t.TemperatureChanged += (sender, e) => Console.WriteLine($"Temp: {e.Temperature}");
```
## Expresiones Lambda
### Sintaxis
```csharp
// Sintaxis completa
(int x, int y) => { return x + y; }
// Cuerpo de expresión (sin return, sin llaves)
x => x * 2;
// Múltiples parámetros
(a, b) => a + b;
// Sin parámetros
() => Console.WriteLine("Hola");
```
### Ejemplos
```csharp
// Con Func
Func square = x => x * x;
Func concat = (a, b) => a + b;
// Con Action
Action print = msg => Console.WriteLine(msg);
// Con Predicate
Predicate isPositive = x => x > 0;
// Lógica compleja
Func grade = score =>
{
if (score >= 90) return "A";
if (score >= 80) return "B";
return "C";
};
```
## Métodos Anónimos
### Antes de Lambdas
```csharp
// Método anónimo
delegate int Calculate(int a, int b);
Calculate calc = delegate(int a, int b) { return a + b; };
// Con eventos
button.Clicked += delegate(object? sender, EventArgs e)
{
Console.WriteLine("¡Clickeado!");
};
```
## Closures
### Capturar Variables
```csharp
int multiplier = 10;
Func multiply = x => x * multiplier;
Console.WriteLine(multiply(5)); // 50
multiplier = 20;
Console.WriteLine(multiply(5)); // 200 (¡captura por referencia!)
```
### Error Común
```csharp
var actions = new List();
// Incorrecto - todos capturan la misma variable
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var a in actions)
a(); // Imprime: 3, 3, 3
// Correcto - capturar valor
var actions2 = new List();
for (int i = 0; i < 3; i++)
{
int captured = i; // Capturar valor
actions2.Add(() => Console.WriteLine(captured));
}
foreach (var a in actions2)
a(); // Imprime: 0, 1, 2
```
## Combinar Delegados (Multicast)
### Combinar Delegados
```csharp
delegate void Notify();
Notify notify1 = () => Console.WriteLine("Manejador 1");
Notify notify2 = () => Console.WriteLine("Manejador 2");
Notify combined = notify1 + notify2;
combined(); // Llama ambos
Notify notify3 = () => Console.WriteLine("Manejador 3");
combined += notify3;
combined(); // Llama los tres
// Remover
combined -= notify2;
combined(); // Manejador 1 y 3
```
### Valores de Retorno con Multicast
```csharp
delegate int Calculate(int x);
Calculate calc = x =>
{
Console.WriteLine($"Primero: {x}");
return x + 1;
};
calc += x =>
{
Console.WriteLine($"Segundo: {x}");
return x + 2;
};
int result = calc(5);
// ¡Solo el último valor de retorno se mantiene!
// Salida:
// Primero: 5
// Segundo: 5
// result = 7
```
## Patrones de Eventos Incorporados
### Firma Estándar de Evento
```csharp
// Tipos de delegado estándar (ya definidos en .NET)
public delegate void EventHandler(object? sender, EventArgs e);
public delegate void EventHandler(object? sender, TEventArgs e);
// Patrón común
public event EventHandler? MyEvent;
public event EventHandler? SpecializedEvent;
```
## Covarianza y Contravarianza
### Con Delegados
```csharp
// Covarianza - tipo de retorno puede ser más derivado
delegate TextWriter WriterFactory();
WriterFactory factory = () => new StreamWriter("file.txt");
// Contravarianza - tipo de parámetro puede ser menos derivado
Action writer = (StreamWriter sw) => sw.WriteLine("test");
Action textWriter = writer; // OK - TextWriter es base de StreamWriter
```
## Resumen
- Delegado: referencia a método tipo-seguro
- Func: devuelve valor
- Action: devuelve void
- Predicate: devuelve bool
- Evento: patrón publicador-suscriptor
- Lambda: métodos anónimos concisos
- Los closures capturan variables por referencia
- Los delegados pueden combinarse (+, -)
- Los eventos siguen el patrón estándar de .NET
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →