什么是委托 delegate
诞生:技术点出来的原因就是 有一些需要将方法作为入参 处理的情况 比如 业务处理完需要回调函数 那就需要传函数入参
委托是C#中的一种类型安全函数指针,允许将方法作为参数传递或存储。它本质上是面向对象的,对方法签名抽象封装(参数列表和返回类型),并支持多播(绑定多个方法)。
所以 当说到委托 脑海里就是 对应的 方法抽象类 使用就是 普通的class类里去声明去掉用当作成员使用
关键特点:
类型安全:编译时检查方法签名,避免非法调用。
动态绑定:运行时决定调用的方法,支持灵活的回调机制。
多播性:通过+=和-=操作符链式调用多个方法
如何使用委托 进行实例化
很多种 下面会从复杂到简化版本介绍,但是流程基本都是 1 定义委托(方法参数声明) 2 实例化对应方法的实现 3使用
- 传统 new 关键字实例化
语法:委托类型 实例名 = new 委托类型(方法名);
public delegate void PrintDelegate(string message);
public static void PrintToConsole(string msg) => Console.WriteLine(msg);// 实例化
PrintDelegate print = new PrintDelegate(PrintToConsole);
print("Hello!"); // 输出:Hello!
特点:
C# 1.0 的经典语法,需显式指定方法名。
适用于需要明确绑定静态方法或实例方法的场景。
- 简写方法名赋值
语法:委托类型 实例名 = 方法名;
PrintDelegate print = PrintToConsole; // 无需 new 关键字
print("Hello!");
特点:
语法更简洁,编译器自动推导委托类型。
推荐在代码可读性要求高时使用。
- 匿名方法(C# 2.0+)
语法:
csharp
委托类型 实例名 = delegate(参数列表) { /* 方法体 */ };
PrintDelegate print = delegate(string msg) {Console.WriteLine($"匿名方法输出:{msg}");
};
print("Test"); // 输出:匿名方法输出:Test
特点:
无需单独定义方法,直接内联实现逻辑。
适用于简单的一次性操作或临时逻辑。
- Lambda 表达式(C# 3.0+)
语法:委托类型 实例名 = (参数列表) => { /* 方法体 */ };
PrintDelegate print = msg => Console.WriteLine($"Lambda 输出:{msg}");
print("Hello"); // 输出:Lambda 输出:Hello
特点:
语法最简洁,支持表达式体和语句块。
广泛用于 LINQ、事件处理和异步编程。
- 内置泛型委托(Func/Action)
语法:
Func<参数类型…, 返回类型> 实例名 = 方法或Lambda;
Action<参数类型…> 实例名 = 方法或Lambda;
// Func(有返回值)
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 5)); // 输出:8// Action(无返回值)
Action<string> log = s => Console.WriteLine($"日志:{s}");
log("操作完成");
特点:
无需自定义委托类型,直接使用 Func 或 Action。
Func 最多支持 16 个参数和 1 个返回值,Action 无返回值。
- 多播委托(链式调用)
语法:
委托类型 实例名 += 方法1;
实例名 += 方法2;
PrintDelegate printChain = PrintToConsole;
printChain += msg => Console.WriteLine($"第二条输出:{msg}");printChain("多播测试");
// 输出:
// Hello!
// 第二条输出:多播测试
多播委托 就是方法链 就是+= 增加或-= 减少委托的方法
但是注意
- -=只能是移除同一个实例的 顺序 是 +123 -321
- 方法链返回的只是最后一个方法返回 都是string内容 只返回最后一个
- 如果中间出错 不捕获异常 就中断了 捕获异常 后续也不执行
使用委托的目的: 解耦逻辑 扩展 异步回调
还是面向对象的 但是 对 方法的抽象 解耦 对内公用 对外扩展的一个封装
比如 猫叫 狗跑 鸡飞 业务逻辑等等 正常就是 调用这三个方法在逻辑中
解耦就吧这三个方法写入委托中 在执行逻辑中只需调用委托不关心 具体的方法了
总结就是 委托就是 提炼方法来代替 参数业务逻辑处理 从而达到 解耦,新业务就新方法 ,其次是 委托的这些方法有公共逻辑可以 整合到委托中 无需在各个方法中 在写一遍
委托 协变 与 逆变
协变 返回值 派生类–》父类 (委托定义)
逆变 入参 父类–》派生类 (委托定义)
什么是事件 有了委托还要事件干嘛? 加了权限的委托 ,安全上配合发布订阅使用 观察者模式
事件是基于委托的发布-订阅模式,用于对象间通信。它封装了委托的调用权限,确保外部代码只能通过+=和-=订阅或取消订阅。
就是特殊的委托 加了权限的委托 只允许在声明的类内部 进行invoke 哪怕子类也不行 外部调用也不行
1.比委托更加安全 相当于更完善,委托是public 可能出错的概率大 比如不小心在外部影响 事件是 private 只在内部 2. 可使用 发布订阅模式异步的方式 以及更好解耦扩展 委托本身而言是 同步的机制 虽然可以结合task多线程异步使用
关键特点:
封装性:事件只能在声明类内部触发(Invoke),外部仅能订阅。
松耦合:发布者与订阅者无需直接依赖,提升代码可维护性
目的 一般类中一些变量改变之后需要外界感知响应的一些操作 wpf mvvm这种数据响应
标准.NET事件模式
EventHandler委托:内置泛型委托 统一的事件签名(object sender, EventArgs e)。就跟委托内置的func action一样就是为了让开发者方便使用 事件 而不必自己 必须声明一下 delegate 这代码 所以提前就封装好
参数说明:
sender:触发事件的对象(事件源)。
e:事件参数,类型为 EventArgs(或其派生类)。若无额外数据需传递,使用 EventArgs.Empty
比如登录事件
public class UserAuthenticator
{// 定义事件public event EventHandler LoginSuccess;public void Login(string username, string password){// 模拟登录逻辑if (username == "admin" && password == "123456"){// 触发事件,传递当前对象和空参数LoginSuccess?.Invoke(this, EventArgs.Empty);}}
}// 使用
var authenticator = new UserAuthenticator();
authenticator.LoginSuccess += (sender, e) =>
{Console.WriteLine("登录成功!");
};
authenticator.Login("admin", "123456"); // 输出:登录成功!
自定义事件参数:通过继承EventArgs传递附加数据 就是规则 只要继承这个EventArgs 识别成事件 去使用
比如 声明事件 按钮点击事件ButtonClickEventArgs :EventArgs(使用EventHandler泛型委托)
public event EventHandler Click;
定义与使用 一样步骤 1 定义声明 2 触发实现方法 3 使用 也就是订阅
// 定义事件参数(继承EventArgs)
public class ButtonClickEventArgs : EventArgs {public string ButtonName { get; set; }
}// 发布者类
public class Button {// 声明事件(使用EventHandler泛型委托)public event EventHandler<ButtonClickEventArgs> Click;// 触发事件的方法protected virtual void OnClick(ButtonClickEventArgs e) {Click?.Invoke(this, e); // 安全调用}public void SimulateClick() {OnClick(new ButtonClickEventArgs { ButtonName = "OK" });}
}// 订阅者类
public class Form { private void OnButtonClicked(object sender, ButtonClickEventArgs e) {Console.WriteLine($"按钮 {e.ButtonName} 被点击!");}
}//在业务地方使用Button button = new Button();button.Click += OnButtonClicked;// 订阅事件button.SimulateClick();
这样在实现 Button的SimulateClick 方法时 事件触发 对于订阅者的 方法也将会执行
委托与事件的区别
使用场景
1. 委托的典型应用
回调机制:异步编程中的完成通知(如Task回调)。
LINQ查询:动态定义查询条件(如Where方法的谓词参数)。
策略模式:运行时切换算法逻辑(如排序策略):
Func<int[], int[]> strategy = SortAlgorithms.QuickSort;
int[] sorted = strategy(data);
2. 事件的典型应用
GUI交互:处理按钮点击、键盘输入等用户操作。
异步通知:文件下载完成、网络请求响应等场景。
观察者模式:实现松耦合的组件通信(如日志系统)
总结
委托是方法引用的灵活容器,适用于回调、策略模式等场景。
事件是委托的安全封装,专为松耦合的发布-订阅模型设计,广泛应用于GUI、异步编程
面试
1 谈谈你对委托的理解
答:委托 就是面向对象中 对方法的抽象封装 使得方法更加灵活的去使用 达到解耦扩展的目的
2 哪些使用场景
答: 最常见的异步回调 以及 linq 中查询表现形式 Lambda 表达式
3 事件是什么
答:事件 就是 private 安全把控的 委托 并搭配 发布订阅方式 异步方式 更进一步的解耦和扩展
4.事件哪些场景使用
答:最常见的就是 GUI 按钮点击事件这种 或者 文件下载异步完成通知事件 socket异步通信通知事件