C# 委托类型与使用详解
委托在C#中类似于函数指针,用来封装方法,允许将方法作为参数传递或者异步回调。比如Action、Func,还有事件和匿名方法。
需要掌握声明委托的关键字delegate、内置的Action和Func
需要注意多播委托,可以组合多个方法,按顺序执行。比如用+=添加方法,用-=移除,注意返回值的问题,如果有多个方法带返回值,最后执行的结果会被返回,前面的可能会被覆盖,所以通常多播委托用于void返回类型的方法。
事件和委托的关系也很重要,事件是基于委托的,但事件有封装,更安全。比如只能在声明事件的类中触发,而委托可以在外部调用。
匿名方法和Lambda表达式也是委托的一部分,可以简化代码。比如用delegate关键字或者lambda来直接写方法体。这会让代码更简洁,特别是在LINQ查询或者事件处理中常用。
常见问题,比如空委托需要检查是否为null,或者多播委托的执行顺序问题。还有协变和逆变,委托参数类型的灵活性,比如参数可以是基类或者派生类,返回类型可以是派生类或者基类。
委托是什么
在 C# 中,委托(Delegate) 是一种类型安全的函数指针机制,用于封装方法(函数),允许将方法作为参数传递、回调或异步调用。它是实现事件驱动编程和回调模式的核心技术。
委托的概念
定义:委托是一种引用类型,定义了方法的签名(参数类型和返回值类型)。
作用:将方法动态绑定到变量,实现方法的间接调用。
特点:
类型安全(编译时检查方法签名)。
支持多播(通过 += 和 -= 组合多个方法)。
可以匿名使用(通过 Lambda 表达式或匿名方法)
委托的声明与使用
声明委托类型
// 定义一个委托类型,不带参数返回 void 的方法
public delegate void MyDelegate();
// 定义一个委托类型,带参数和返回值的委托
public delegate int CalculateDelegate(int a, int b);实例化委托
// 普通方法的示例
public void SayHello() => Console.WriteLine("Hello!");
public int Add(int a, int b) => a + b;
// 实例化委托并绑定方法
MyDelegate del1 = new MyDelegate(SayHello);
CalculateDelegate del2 = Add;
// 简化写法
MyDelegate del3 = SayHello;
CalculateDelegate del4 = Add;调用委托
del1(); // 输出 "Hello!"
int result = del2(3, 5); // result = 8多播委托(Multicast Delegate)
通过 += 和 -= 运算符组合多个方法。
调用时按添加顺序依次执行方法。
适用场景:通常用于返回值为 void 的方法(非 void 返回值的方法会被覆盖)
public delegate void LoggerDelegate(string message);
public void LogToConsole(string message) => Console.WriteLine($"Console: {message}");
public void LogToFile(string message) => File.WriteAllText("log.txt", message);
// 组合多个方法
LoggerDelegate logger = LogToConsole;
logger += LogToFile;
// 调用时依次执行 LogToConsole 和 LogToFile
logger("Something happened!");
// 移除方法
logger -= LogToFile;内置的委托类型
C# 提供了内置的泛型委托,无需手动定义
Action:无返回值的方法
Action action = () => Console.WriteLine("Action");
Action<int, string> actionWithParams = (x, s) => Console.WriteLine($"{x}, {s}");Func:有返回值的方法
Func<int> func = () => 42;
Func<int, int, int> add = (a, b) => a + b;Predicate<T>:返回 bool 的方法(用于条件判断)
Predicate<int> isEven = x => x % 2 == 0;匿名方法与 Lambda 表达式
匿名方法(C# 2.0)
MyDelegate del = delegate() { Console.WriteLine("Anonymous method"); };Lambda 表达式(C# 3.0+)
MyDelegate del = () => Console.WriteLine("Lambda");
Func<int, int, int> multiply = (a, b) => a * b;委托与事件
事件(Event) 是基于委托的封装,提供更安全的订阅/取消订阅机制。
事件只能在声明它的类中触发(避免外部直接调用委托)。
public class Button
{
public event EventHandler Clicked; // EventHandler 是内置委托
public void Press()
{
Clicked?.Invoke(this, EventArgs.Empty); // 安全调用(检查 null)
}
}
// 使用
Button btn = new Button();
btn.Clicked += (sender, e) => Console.WriteLine("Button clicked!");
btn.Press();委托的常见用途
回调模式:将方法传递给异步操作或算法。
事件处理:GUI 编程(如按钮点击)。
LINQ 查询:通过 Func 和 Action 实现灵活的数据操作。
异步编程:结合 BeginInvoke/EndInvoke(旧模式)或 Task(新模式)。
注意事项
空委托检查:调用前需检查是否为 null(可用 ?.Invoke() 简化)。
协变与逆变:委托支持参数类型和返回类型的灵活转换(C# 4.0+)。
性能:委托调用比直接方法调用略慢,但在大多数场景中可忽略。
通过委托,C# 实现了高度灵活的函数抽象,是理解事件、Lambda 和异步编程的基础