Skip to main content

8.3 - Anonymous Methods

When working with delegates and events, you'll often create methods that are only used in one place - as event handlers or callback functions. Creating named methods for these simple, one-off operations can clutter your code. Anonymous methods solve this problem by allowing you to define methods inline, right where they're used.

What Are Anonymous Methods?

Anonymous methods are methods without a name that can be defined inline wherever a delegate type is expected. They provide a more concise way to create simple delegate instances without having to define a separate named method.

Basic Syntax

The syntax for an anonymous method uses the delegate keyword followed by a parameter list and a method body:

// Creating a delegate instance with an anonymous method
GameEvent gameStartEvent = delegate()
{
Console.WriteLine("Game is starting!");
InitializePlayer();
};

// Anonymous method with parameters
PlayerDamaged playerDamagedEvent = delegate(int damageAmount)
{
Console.WriteLine($"Player took {damageAmount} damage!");
UpdateHealthBar();
};

Using Anonymous Methods with Events

Anonymous methods are particularly useful when subscribing to events:

// Traditional approach with a named method
gameManager.OnGameOver += HandleGameOver;

private void HandleGameOver(object sender, EventArgs e)
{
ShowGameOverScreen();
SaveHighScore();
}

// Using an anonymous method instead
gameManager.OnGameOver += delegate(object sender, EventArgs e)
{
ShowGameOverScreen();
SaveHighScore();
};

The anonymous method approach is more concise and keeps the handler code close to where it's used, which can make the code easier to follow.

Capturing Variables

One powerful feature of anonymous methods is their ability to capture variables from the surrounding scope. This is known as creating a "closure":

public void SetupEnemyBehavior(Enemy enemy, int difficultyLevel)
{
// The anonymous method captures the difficultyLevel variable
enemy.OnPlayerSpotted += delegate(Player player)
{
float attackSpeed = 1.0f + (difficultyLevel * 0.2f);
enemy.Attack(player, attackSpeed);
};
}

In this example, the anonymous method captures the difficultyLevel variable from the containing method. Even after SetupEnemyBehavior has finished executing, the anonymous method still has access to the value of difficultyLevel that was in scope when it was created.

Variable Lifetime Extension

When an anonymous method captures a variable, the lifetime of that variable is extended to match the lifetime of the delegate instance. This can be both powerful and potentially problematic:

public void CreateEnemies()
{
for (int i = 0; i < 5; i++)
{
Enemy enemy = new Enemy();

// CAUTION: This captures the loop variable i
enemy.OnDestroyed += delegate
{
Console.WriteLine($"Enemy {i} was destroyed!");
};
}
}

In this example, all anonymous methods will end up using the final value of i (which is 5) because they capture the variable itself, not its value at the time of creation. To fix this, you can create a local copy inside the loop:

public void CreateEnemies()
{
for (int i = 0; i < 5; i++)
{
Enemy enemy = new Enemy();
int enemyIndex = i; // Local copy

// Now this captures enemyIndex, which has a separate value for each iteration
enemy.OnDestroyed += delegate
{
Console.WriteLine($"Enemy {enemyIndex} was destroyed!");
};
}
}

Practical Example: Timed Actions

Anonymous methods are great for implementing timed actions or delayed callbacks:

public class TimerSystem
{
public void DelayAction(Action action, float delayInSeconds)
{
// Start a coroutine that waits and then executes the action
StartCoroutine(DelayRoutine(action, delayInSeconds));
}

private IEnumerator DelayRoutine(Action action, float delayInSeconds)
{
yield return new WaitForSeconds(delayInSeconds);
action();
}
}

// Usage with anonymous method
public void SetupExplosion(Bomb bomb)
{
timerSystem.DelayAction(delegate
{
CreateExplosion(bomb.Position);
DamageNearbyEntities(bomb.Position, bomb.BlastRadius);
DestroyBomb(bomb);
}, bomb.FuseTime);
}

This approach keeps all the explosion logic together, making it clear what will happen when the timer expires.

Anonymous Methods vs. Lambda Expressions

Anonymous methods were introduced in C# 2.0, but in C# 3.0, lambda expressions were added as an even more concise way to define inline methods. While anonymous methods are still valid C#, lambda expressions are generally preferred in modern code:

// Anonymous method
button.onClick.AddListener(delegate
{
LoadNextLevel();
});

// Equivalent lambda expression
button.onClick.AddListener(() => LoadNextLevel());

Lambda expressions provide a more compact syntax and additional features. We'll explore them in detail in the next section.

When to Use Anonymous Methods

Despite being somewhat superseded by lambda expressions, anonymous methods still have their place:

  1. Simple event handlers: When you need a quick, one-off event handler
  2. Callback functions: For methods that will be called later
  3. Temporary delegates: When you need a delegate instance that won't be reused
  4. Readability: Sometimes the delegate keyword makes the intent clearer than a lambda expression

Anonymous Methods in Unity

In Unity, anonymous methods can be particularly useful for:

  1. UI event handlers: Responding to button clicks and other UI events
  2. Coroutine callbacks: Executing code after a coroutine completes
  3. Animation events: Responding to animation triggers
  4. Physics callbacks: Handling collision and trigger events
Unity Relevance

Unity's UI system and many third-party libraries accept delegates for callbacks. Anonymous methods provide a convenient way to define these callbacks inline, making your code more readable and maintainable.

Best Practices for Anonymous Methods

  1. Keep them short and focused: If an anonymous method grows beyond a few lines, consider using a named method instead.

  2. Be careful with variable capture: Be aware of which variables are being captured and their lifetime.

  3. Consider readability: Sometimes a named method with a descriptive name is clearer than an inline anonymous method.

  4. Prefer lambda expressions for simple cases: For very simple delegates, lambda expressions are more concise.

Conclusion

Anonymous methods provide a convenient way to define inline methods for delegates and events, reducing code clutter and keeping related functionality together. While they've been largely superseded by lambda expressions in modern C#, understanding anonymous methods is still valuable, especially when reading older code.

In the next section, we'll explore lambda expressions, which offer an even more concise syntax for defining inline methods and introduce additional functional programming capabilities to C#.