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)); // 输出: 7Console.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 值。