委托
在 C# 中,委托(Delegate) 是一种类型安全的函数指针,它允许将方法作为参数传递给其他方法。
我们的核心目标是将方法作为参数传递,而不是将数据作为参数传递。类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量,引用可在运行时被改变。
所有的委托(Delegate)都派生自 System.Delegate 类。
声明委托
委托是一个引用类型,它定义了一个方法签名,可以用于存储指向该签名的方法。通过委托,你可以调用其他类中的方法。
public delegate <return type> <delegate–name> <parameter list>;
例如以下代码,我们定义一个接受两个整数并返回一个整数的委托:
public delegate int MathOperation(int a, int b);
委托的实例化
委托的实例化与类的实例化类似,你可以使用 new 关键字来创建一个委托的实例。
MathOperation add = new MathOperation(Add);
在C#中,也可以对上面的代码进行简写,这种写法更常用。(语法糖)
MathOperation add = Add;
因为委托类型在赋值时,编译器能根据左边的类型自动推断出:
“右边这个 Add 是一个方法组(method group),我要把它转换成对应的委托对象”
一个委托的代码示例
using System;
namespace DelegateDemo
{
class Program
{
// 1) 定义一个委托类型:要求方法必须是 (int, int) -> int
delegate int Calc(int a, int b);
static void Main(string[] args)
{
// 2) 把方法赋值给委托(委托就像“方法的引用”)
Calc c1 = Add;
Calc c2 = Sub;
// 3) 通过委托调用方法
Console.WriteLine("=== 直接调用委托 ===");
Console.WriteLine("Add: " + c1(10, 3)); // 13
Console.WriteLine("Sub: " + c2(10, 3)); // 7
// 4) 把委托当参数传进去(核心用途:把“行为”传递)
Console.WriteLine("\\n=== 委托作为参数 ===");
Console.WriteLine("DoCalc(Add): " + DoCalc(5, 2, Add)); // 7
Console.WriteLine("DoCalc(Sub): " + DoCalc(5, 2, Sub)); // 3
// 5) 也可以用 Lambda 表达式直接写一个方法给委托
Calc mul = (x, y) => x * y;
Console.WriteLine("\\n=== Lambda 作为委托 ===");
Console.WriteLine("Mul: " + mul(5, 2)); // 10
Console.WriteLine("\\n按任意键退出…");
Console.ReadKey();
}
// 被委托引用的方法 1
static int Add(int x, int y)
{
return x + y;
}
// 被委托引用的方法 2
static int Sub(int x, int y)
{
return x – y;
}
// 委托作为参数:你传什么算法进来,它就用什么算法算
static int DoCalc(int a, int b, Calc calc)
{
return calc(a, b);
}
}
}
这个例子展示了委托的基本用法,包括定义委托类型、实例化委托、使用委托调用方法、将委托作为参数传递以及使用 Lambda 表达式直接写一个方法给委托。
委托也可以在类外声明,只是需要在类外声明时,需要加上 public 关键字。
public delegate int MathOperation(int a, int b);
如果不加上 public 关键字,那么委托只能在当前类中使用。
内置委托
C# 提供了一些内置的委托类型,用于常见的场景。
- Action:无返回值的委托。
- Func<T>:有返回值的委托。
- Predicate<T>:返回 bool 类型的委托。
Action(无返回值)
Action 委托是一个无返回值的委托类型。它可以用于表示没有参数的方法,或者参数类型为 void 的方法。
Action action = () => Console.WriteLine("Hello World!");
action(); // 输出:Hello World!
Func(有返回值)
Func<T> 委托是一个有返回值的委托类型。它可以用于表示有参数的方法,并且参数类型可以是任意数量的。
Func<int, int, int> add = (a, b) => a + b;
int result = add(2, 3); // result = 5
Func 的最后一个泛型参数是返回值类型,其他泛型参数是参数类型。
Predicate(返回 bool 类型)
Predicate<T> 委托是一个返回 bool 类型的委托类型。它可以用于表示有参数的方法,参数类型为 T,返回值为 bool。
Predicate<int> isEven = x => x % 2 == 0;
bool result = isEven(4); // result = true
例子
我们可以使用Func委托将我们上面代码中的委托声明进行改写
我们之前声明了委托
// 1) 定义一个委托类型:要求方法必须是 (int, int) -> int
delegate int Calc(int a, int b);
我们可以改写为
// 1) 定义一个委托类型:要求方法必须是 (int, int) -> int
Func<int, int, int> Calc;
事件
C# 事件(Event)是一种成员,用于将特定的事件通知发送给订阅者。事件通常用于实现观察者模式,它允许一个对象将状态的变化通知其他对象,而不需要知道这些对象的细节。
就像是一个广播,当某个事件发生时,所有订阅了这个事件的对象都会收到通知。
事件和委托的关系
事件是基于委托实现的。事件是一个特殊的委托,它只能在类中声明,并且只能在类的内部调用。
委托可以这样用:
public Action OnSomething;
但这有风险:外部可以直接把它清空或乱赋值:
obj.OnSomething = null; // 外部直接干掉所有订阅者 ❌
obj.OnSomething(); // 外部还能直接触发事件 ❌
所以 C# 提供了 event:
public event Action OnSomething;
event的意义是:
- 只能在类的内部调用。
- 外部不能直接赋值,只能通过 += 和 -= 来订阅和取消订阅。
通过事件使用委托
声明事件(Event)
在类的内部声明事件,首先必须声明该事件的委托类型。例如:
public delegate void BoilerLogHandler(string status);
然后,声明事件本身,使用 event 关键字:
public event BoilerLogHandler OnBoilerLog;
触发事件
当事件发生时,需要触发事件。触发事件的代码通常在类的内部,例如在某个方法中。
private void Log(string status)
{
OnBoilerLog?.Invoke(status);
}
在上面的代码中,OnBoilerLog?.Invoke(status) 用于触发事件。如果没有订阅者,OnBoilerLog 为 null,调用 Invoke 方法会安全地忽略调用。
订阅事件
外部代码可以订阅事件,例如:
boiler.OnBoilerLog += LogToConsole;
在上面的代码中,LogToConsole 是一个方法,它符合 BoilerLogHandler 委托的签名。当 boiler 对象触发 OnBoilerLog 事件时,LogToConsole 方法会被调用。
取消订阅事件
当外部代码不再需要接收事件通知时,应该取消订阅事件,例如:
boiler.OnBoilerLog -= LogToConsole;
事件的一个完整例子
using System;
class Publisher
{
// 事件类型:无参无返回
public event Action SomethingHappened;
public void DoWork()
{
Console.WriteLine("Publisher: 我开始工作了…");
// 触发事件(通知所有订阅者)
SomethingHappened?.Invoke();
}
}
class Program
{
static void Main()
{
var p = new Publisher();
// 订阅事件(+=)
p.SomethingHappened += () => Console.WriteLine("Subscriber1: 我收到通知了!");
p.SomethingHappened += () => Console.WriteLine("Subscriber2: 我也收到通知了!");
// 触发事件(由发布者内部触发)
p.DoWork();
Console.ReadKey();
}
}
运行结果类似
Publisher: 我开始工作了…
Subscriber1: 我收到通知了!
Subscriber2: 我也收到通知了!
这个例子中,
- Publisher 类声明了一个事件 SomethingHappened,它的委托类型是 Action(无参无返回)。
- Program 类订阅了 Publisher 类的 SomethingHappened 事件。
- Program 类调用 Publisher 类的 DoWork 方法,触发 SomethingHappened 事件。
- 所有订阅了 SomethingHappened 事件的方法都会被调用。
请您注意这里面的一些误区,订阅事件的主体是事件,而不是需要订阅的对象,您可以理解为事件有一个“事件中心”,事件中心负责管理所有订阅者并在事件触发时调用它们。
EventHandler
EventHandler 是一个预定义的委托类型,用于处理事件。它的签名是 (object sender, EventArgs e),其中 sender 是触发事件的对象,e 是事件数据。
public event EventHandler SomethingHappened;
在上面的代码中,SomethingHappened 事件的委托类型是 EventHandler。当事件触发时,EventHandler 会自动传递事件源和事件数据。
我们来看一个完整的例子:
using System;
class MyButton
{
// 标准事件:无额外信息
public event EventHandler Clicked;
public void Click()
{
Console.WriteLine("MyButton: Click() 被调用");
// 触发事件
Clicked?.Invoke(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
var btn = new MyButton();
// 订阅事件(绑定处理方法)
btn.Clicked += OnButtonClicked;
btn.Click();
Console.ReadKey();
}
static void OnButtonClicked(object sender, EventArgs e)
{
Console.WriteLine("订阅者: 收到 Clicked 事件");
// sender 就是触发事件的对象(这里是 btn)
Console.WriteLine("sender 类型:" + sender.GetType().Name);
}
}
小结
委托(Delegate)=“类型安全的方法指针”,用来把“行为”当成参数传递;
- 自定义:delegate int Calc(int a,int b);
- 系统内置:Action / Func / Predicate,优先使用内置。
事件(Event)=“受控的委托”,只能在类内部触发,外部只能 += 订阅、-= 取消,防止误操作;
- 简单场景:public event Action Something;
- 标准场景:public event EventHandler Something; 自动携带 sender 与 EventArgs。
典型流程
发布者 → 声明事件 → 在合适时机 ?.Invoke()
订阅者 → += 注册方法 → 收到通知后执行回调。



![P11246 [GESP202409 六级] 小杨和整数拆分-171主机测评](https://www.171host.com/wp-content/uploads/2026/11/20261108100631-6af049a75855c-220x150.png)


