C# 委托与匿名方法
文章目录
- 1.委托的基本概念
- 1.1 声明和使用委托
- 1.2 委托的创建与调用
- 1.3 委托的多播功能
- 2. 委托的应用场景
- 2.1 事件处理
- 2.2 回调函数
- 2.3 策略模式
- 3.匿名方法
- 3.1 定义匿名方法
- 3.2 匿名方法的应用
- 4.委托与匿名方法的实际应用示例
- 4.1 使用委托实现回调函数
- 4.2 使用匿名方法实现事件处理
- 4.3 使用匿名方法作为回调
- 5.委托与匿名方法的注意事项
- 5.1 委托的类型安全
- 5.2 委托的生命周期
- 5.3 匿名方法的作用域
1.委托的基本概念
委托是一种引用类型,可以用来封装一个或多个方法,适合用于事件处理和回调的场景。委托的声明形式类似于方法签名,但它不是方法,而是方法的引用。
1.1 声明和使用委托
在 C# 中定义委托的语法如下:
delegate 返回类型 委托名(参数列表);
例如,定义一个返回 int、接收两个 int 参数的委托:
delegate int MathOperation(int a, int b);
1.2 委托的创建与调用
创建委托实例时,可以将一个符合签名的方法赋给它。以下是一个委托的基本使用示例:
using System;
class Program
{
// 定义委托
delegate int MathOperation(int a, int b);
// 定义符合委托签名的方法
static int Add(int x, int y) => x + y;
static int Multiply(int x, int y) => x * y;
static void Main()
{
// 创建委托实例,并传入方法
MathOperation addOperation = Add;
MathOperation multiplyOperation = Multiply;
// 调用委托
Console.WriteLine(addOperation(3, 4)); // 输出: 7
Console.WriteLine(multiplyOperation(3, 4)); // 输出: 12
}
}
MathOperation 委托可以引用任意符合 int (int, int) 签名的方法。
1.3 委托的多播功能
委托还可以引用多个方法,这种特性被称为多播委托(Multicast Delegate)。可以通过 += 运算符将多个方法绑定到一个委托实例上,执行时会按顺序依次调用这些方法。
using System;
class Program
{
delegate void PrintMessage();
static void Hello() => Console.WriteLine("Hello");
static void World() => Console.WriteLine("World");
static void Main()
{
PrintMessage message = Hello;
message += World;
message(); // 输出: Hello \n World
}
}
在调用 message() 时,Hello 和 World 方法都会被依次调用。
2. 委托的应用场景
2.1 事件处理
在 GUI 编程中,用户操作(如点击按钮)会触发事件。事件处理器通常使用委托进行实现,允许动态绑定处理方法。
2.2 回调函数
在异步编程或长时间运行的任务中,回调函数非常有用。我们可以将回调函数定义为委托,以便任务完成后调用。
2.3 策略模式
在设计模式中,策略模式允许我们在运行时动态选择算法。可以通过委托实现不同的算法,并在需要时进行切换。
3.匿名方法
在 C# 2.0 及以上版本中,匿名方法 为委托提供了更加简洁的语法。匿名方法是一种不需要命名的方法,可以在创建委托实例时直接定义,减少代码量。
3.1 定义匿名方法
匿名方法的语法是在 delegate 关键字后直接提供方法实现,而不需要方法名。
using System;
class Program
{
delegate void Greet(string name);
static void Main()
{
Greet greet = delegate (string name)
{
Console.WriteLine("Hello, " + name);
};
greet("Alice"); // 输出: Hello, Alice
}
}
匿名方法 delegate (string name) { … } 实现了 Greet 委托所需的方法签名。
3.2 匿名方法的应用
- 事件处理:为事件动态绑定方法。
- 回调函数:实现简单的回调逻辑。
- LINQ 查询:匿名方法在 LINQ 表达式中很常见。
4.委托与匿名方法的实际应用示例
4.1 使用委托实现回调函数
假设我们有一个执行某种操作的长时间任务,任务完成后希望调用回调函数,可以使用委托实现。
using System;
using System.Threading;
class Program
{
// 定义回调委托
delegate void TaskCompletedCallback(string message);
static void Main()
{
// 传递回调方法作为参数
PerformTask("Sample Task", TaskCompleted);
}
static void PerformTask(string taskName, TaskCompletedCallback callback)
{
Console.WriteLine($"Starting {taskName}...");
Thread.Sleep(2000); // 模拟耗时操作
// 任务完成后调用回调
callback($"{taskName} completed successfully.");
}
static void TaskCompleted(string message)
{
Console.WriteLine(message);
}
}
在上述示例中,当 PerformTask 完成时会调用 TaskCompleted 方法,输出任务完成消息。
4.2 使用匿名方法实现事件处理
在事件处理中,匿名方法可以使代码更加简洁。例如,创建一个按钮点击事件的处理器:
using System;
using System.Windows.Forms;
class Program
{
static void Main()
{
Button button = new Button();
button.Text = "Click Me";
// 使用匿名方法绑定事件处理器
button.Click += delegate (object sender, EventArgs e)
{
Console.WriteLine("Button clicked!");
};
Application.Run(new Form { Controls = { button } });
}
}
在该例子中,button.Click 事件使用匿名方法直接定义了事件处理逻辑,无需创建单独的方法。
4.3 使用匿名方法作为回调
在需要临时回调逻辑的场景下,匿名方法可以简化代码结构。以下是一个简单的计时器回调示例:
using System;
using System.Timers;
class Program
{
static void Main()
{
Timer timer = new Timer(1000);
// 使用匿名方法定义回调
timer.Elapsed += delegate (object sender, ElapsedEventArgs e)
{
Console.WriteLine("Timer ticked at: " + e.SignalTime);
};
timer.Start();
Console.ReadLine();
timer.Stop();
}
}
timer.Elapsed 事件使用匿名方法实现,简化了代码逻辑。
5.委托与匿名方法的注意事项
5.1 委托的类型安全
委托类型必须与它引用的方法完全匹配,包括返回类型和参数列表,否则会导致编译错误。因此,定义委托时需要确保签名的一致性。
5.2 委托的生命周期
在多播委托中,委托引用的多个方法顺序执行。如果在多播委托执行过程中发生异常,后续方法不会继续执行。因此,在实现多播委托时,需考虑异常处理的问题。
5.3 匿名方法的作用域
匿名方法可以访问其声明作用域中的变量,允许捕获局部变量。这种特性在某些情况下会产生闭包(Closure)现象。需要注意,捕获的变量在委托生命周期内始终保持引用,因此可能导致预期外的行为。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(delegate { Console.WriteLine(i); });
}
foreach (var action in actions)
{
action(); // 输出的值可能不符合预期,因为 i 是引用捕获
}
}
}
由于匿名方法捕获了 i 的引用,而不是值,因此最后输出的都是循环结束后的 i 值。