C# from Basics to Mastery — Part 5
Passing actions through delegates, events, and lambdas
The articles written on this blog were created with the goal of teaching. Also, the information in the article may be incorrect, so please be careful.
Methods can be called directly, but the action itself to be executed later can also be passed as a value. Delegates, events, and lambdas in C# express this flow.
delegate
int Double(int value) => value * 2;
Func<int, int> operation = Double;
int result = operation(10);
Action<string> logger = message => Console.WriteLine(message);
logger("Battle started");
Func<int, int> is an action that receives an int and returns an int, and Action is an action that has no return value. The calling code does not need to know which method is actually being executed.
event
public sealed class Player
{
public event Action<int>? HealthChanged;
public int Health { get; private set; } = 100;
public void TakeDamage(int amount)
{
Health = Math.Max(0, Health - amount);
HealthChanged?.Invoke(Health);
}
}
player.HealthChanged += health => healthBar.SetValue(health);
Events notify stateful objects of changes and cause other objects, such as UI or sounds, to respond. From the outside, you can only subscribe and unsubscribe, and events cannot be called at will.
Subscription Lifetime Management
void OnEnable()
{
player.HealthChanged += UpdateHealth;
}
void OnDisable()
{
player.HealthChanged -= UpdateHealth;
}
If a short-lived object continues to subscribe to events of a long-lived object, memory and calls may remain. The registered location and the corresponding release location must be determined together.
lambda capture
int bonus = 5;
Func<int, int> addBonus = damage => damage + bonus;
If a lambda uses an external local variable, an object can be created to store the value. Continuing to create capture lambdas in code that repeats itself every frame may result in unnecessary allocations.
Delegates are types of actions, and events create boundaries for change notifications. Replacing all calls with events for reasons of convenience makes it difficult to track the execution order, so it should be used where loose coupling is needed.