跳到主要内容

C# Lambda 捕获变量

介绍

在 C# 中,Lambda 表达式是一种简洁的匿名函数表示方式,常用于简化委托和 LINQ 查询。Lambda 表达式可以捕获其外部作用域中的变量,这种行为称为“变量捕获”或“闭包”。理解 Lambda 捕获变量的机制对于编写高效且可维护的代码至关重要。

本文将逐步讲解 Lambda 捕获变量的概念,并通过代码示例和实际应用场景帮助你掌握这一知识点。


什么是 Lambda 捕获变量?

Lambda 捕获变量是指 Lambda 表达式在其定义的作用域中访问外部变量。这些变量可以是局部变量、参数或类的字段。Lambda 表达式会“捕获”这些变量的值或引用,并在其执行时使用它们。

备注

捕获的变量在 Lambda 表达式中是“按引用”捕获的,这意味着如果外部变量的值发生变化,Lambda 表达式中的值也会随之变化。


捕获变量的基本示例

以下是一个简单的示例,展示了 Lambda 表达式如何捕获外部变量:

csharp
int multiplier = 2;
Func<int, int> multiply = x => x * multiplier;

Console.WriteLine(multiply(5)); // 输出:10

multiplier = 3;
Console.WriteLine(multiply(5)); // 输出:15

在这个示例中,Lambda 表达式 x => x * multiplier 捕获了外部变量 multiplier。当 multiplier 的值从 2 变为 3 时,Lambda 表达式的行为也随之改变。


捕获变量的作用域

Lambda 表达式捕获的变量的作用域与其定义的作用域相同。如果捕获的变量在 Lambda 表达式执行时已经超出作用域,可能会导致意外行为。例如:

csharp
Func<int> CreateCounter()
{
int count = 0;
return () => ++count;
}

var counter = CreateCounter();
Console.WriteLine(counter()); // 输出:1
Console.WriteLine(counter()); // 输出:2

在这个示例中,count 是一个局部变量,但 Lambda 表达式 () => ++count 捕获了它。即使 CreateCounter 方法已经返回,count 仍然可以通过 Lambda 表达式访问和修改。


捕获变量的生命周期

捕获的变量的生命周期可能会超出其原始作用域。以下示例展示了这一点:

csharp
Action CreateAction()
{
string message = "Hello";
return () => Console.WriteLine(message);
}

var action = CreateAction();
action(); // 输出:Hello

在这个示例中,message 是一个局部变量,但 Lambda 表达式 () => Console.WriteLine(message) 捕获了它。即使 CreateAction 方法已经返回,message 仍然可以通过 Lambda 表达式访问。


捕获变量的实际应用场景

1. 事件处理

Lambda 表达式常用于事件处理程序中,以捕获上下文中的变量。例如:

csharp
Button button = new Button();
string buttonText = "Click Me";

button.Click += (sender, e) => Console.WriteLine(buttonText);
button.PerformClick(); // 输出:Click Me

在这个示例中,Lambda 表达式捕获了 buttonText 变量,并在按钮点击时输出其值。

2. LINQ 查询

在 LINQ 查询中,Lambda 表达式经常捕获外部变量以过滤或排序数据。例如:

csharp
int threshold = 50;
var numbers = new List<int> { 10, 20, 30, 40, 50, 60 };

var filteredNumbers = numbers.Where(n => n > threshold);
foreach (var number in filteredNumbers)
{
Console.WriteLine(number); // 输出:60
}

在这个示例中,Lambda 表达式 n => n > threshold 捕获了 threshold 变量,用于过滤列表中的数字。


捕获变量的注意事项

警告
  1. 性能影响:捕获变量可能会导致额外的内存分配,尤其是在循环中频繁创建 Lambda 表达式时。
  2. 意外行为:如果捕获的变量在 Lambda 表达式执行时被修改,可能会导致意外结果。
  3. 线程安全:在多线程环境中,捕获的变量可能会引发竞态条件。

总结

Lambda 捕获变量是 C# 中一个强大的特性,它允许 Lambda 表达式访问和修改外部作用域中的变量。理解捕获变量的机制对于编写高效且可维护的代码至关重要。通过本文的示例和实际应用场景,你应该已经掌握了这一概念。


附加资源与练习

练习

  1. 编写一个 Lambda 表达式,捕获一个局部变量并在循环中修改它。
  2. 创建一个事件处理程序,使用 Lambda 表达式捕获事件参数并输出相关信息。

进一步学习

  • 阅读 C# 官方文档 中关于 Lambda 表达式和闭包的更多内容。
  • 尝试在 LINQ 查询中使用捕获变量,观察其行为。