一句话理解委托
在 LabVIEW 中,你可以把 VI 的引用(VI Reference)传给 Call By Reference Node,在运行时动态决定执行哪个 VI。委托(Delegate)做的就是同一件事:把方法变成可以赋值、传递、调用的变量。
把 VI Ref 连到 Call By Reference Node 的输入端,运行时决定调用哪个 VI

声明一个委托类型的变量,把方法赋值给它,运行时直接调用
// 步骤1:声明委托类型(定义"什么样"的方法可以被引用)
// 输入: 两个 int 输出: int
delegate int MathOperation(int x, int y);
// 步骤2:写几个签名匹配的方法
static int Add(int a, int b) => a + b;
static int Subtract(int a, int b) => a - b;
static int Multiply(int a, int b) => a * b;
// 步骤3:把方法"装进"委托变量 → 通过委托调用
MathOperation op = Add;
Console.WriteLine(op(3, 2)); // 5委托本质上是"指向方法的类型安全的指针"。C 语言有函数指针但极不安全;LabVIEW 有 VI Reference 但需要图形化连线。C# 的委托就是两者优点的结合——类型安全 + 语言层面的简洁语法。
委托的基础用法
三步走:声明委托类型 → 创建委托变量 → 调用:
// 步骤1:声明委托类型(定义"什么样"的方法可以被引用)
// 输入: 两个 int 输出: int
delegate int MathOperation(int x, int y);
// 步骤2:写几个签名匹配的方法
static int Add(int a, int b) => a + b;
static int Subtract(int a, int b) => a - b;
static int Multiply(int a, int b) => a * b;
// 步骤3:把方法"装进"委托变量 → 通过委托调用
MathOperation op = Add;
Console.WriteLine(op(3, 2)); // 5
// 步骤4:一个变量,切换指向不同的方法
op = Subtract;
Console.WriteLine(op(3, 2)); // 1
// op 就像 LabVIEW 中一个可以切换 VI Ref 的端子
op = Multiply;
Console.WriteLine(op(3, 2)); // 6“类比:在 LabVIEW 中,同一个 Call By Reference Node,你把 Add.vi 的 Ref 连进来它就做加法,换成 Subtract.vi 的 Ref 它就做减法。op = Add 就是这回事——一个变量,在不同时刻指向不同的方法。”
委托变量也可以指向实例方法,用于指向实际真实具体对象的某个方法
var sensor = new Sensor();
Func<string> getInfo = sensor.GetInfo; // 指向实例方法
Console.WriteLine(getInfo()); // 调用 sensor.GetInfo()委托不关心方法是 static 还是实例方法,只要签名匹配就行。这比 LabVIEW 的静态 VI Reference(只能指向固定的 VI 文件)更灵活。
委托作为方法参数(回调模式)
这是委托最经典的用法:把"执行什么操作"的决定权交给方法的调用方。
// 这个方法接受一个"操作"作为参数
// 调用时才知道具体用什么算法
static void CalculateAndPrint(int x, int y, MathOperation operation)
{
int result = operation(x, y);
Console.WriteLine($"结果是: {result}");
}
// 用法:最后一个参数传的是"方法",不是"数据"
CalculateAndPrint(10, 5, Add); // 结果: 15
CalculateAndPrint(10, 5, Subtract); // 结果: 5在 LabVIEW 中做同样的事:你需要把 Add.vi 和 Subtract.vi 的 VI Reference 分别创建,然后作为参数传入一个 SubVI——那个 SubVI 内部需要一个Call By Reference Node 来调用传入的 VI。整个过程涉及 VI Reference 的创建、连线、类型匹配,步骤比 C# 多不少。
而在 C# 中这一切只需要一行 CalculateAndPrint(10, 5, Add)。这就是委托被称为 "方法即参数" 的由来。
多播委托:一个委托调用多个方法
委托可以用 += 绑定多个方法,一次调用全部执行——这是 LabVIEW 中不容易直接表达的能力:
// 声明一个无参数、无返回值的委托类型
delegate void LogHandler(string msg);
static void LogToConsole(string msg)
=> Console.WriteLine($"[控制台] {msg}");
static void LogToFile(string msg)
=> Console.WriteLine($"[文件] {msg} → log.txt");
// 多播:一个委托变量绑定两个方法
LogHandler logger = LogToConsole;
logger += LogToFile; // 用 += 追加
// 一次调用,两个方法依次执行!
logger("系统启动");
// 输出:
// [控制台] 系统启动
// [文件] 系统启动 → log.txt
// 移除:用 -=
logger -= LogToFile;
logger("系统关闭");
// 输出:
// [控制台] 系统关闭LabVIEW 中要实现"一条日志同时写控制台和文件",通常需要用 User Event 让多个 Event Structure 同时注册,或者用 Notifier 广播。多播委托用一行 += 就做到了。
“注意:多播委托的返回值只能取最后一个方法的返回值。如果前面的方法抛异常,后续方法不会执行。所以有返回值时通常不用多播;没有返回值时(delegate void)才是多播的舒适区。”
内置委托:Action、Func、EventHandler
在上面的讲解中,C#创建一个委托变量时,需要先声明这个变量的类型,然后才能创建变量,需要两步。
.NET 框架内置了三种泛型委托,绝大多数情况下你不需要自己声明委托类型,直接用它们就行:
| 内置委托 | 签名 | 适用场景 |
|---|---|---|
| Action | 无参数、无返回值 | 最简单的回调 |
| Action<T> | 1 个参数、无返回值 | 带数据的回调 |
| Action<T1,T2> | 2 个参数、无返回值 | 多参数回调(最多 16 个) |
| Func<TResult> | 无参数、有返回值 | 工厂方法、懒加载 |
| Func<T,TResult> | 1 参数、有返回值 | 映射 / 转换(LINQ 大量使用) |
| EventHandler<T> | void(object, T) | 事件处理(见事件相关文章) |
// 不需要自己声明委托,直接用内置的!
Action<string> log = Console.WriteLine; // delegate void Action<T>(T obj);
Func<int, int, int> calc = Add; // delegate TResult Func<T1,T2,TResult>(T1, T2);
log("Hello"); // 调用 Console.WriteLine("Hello")
calc(3, 2); // 调用 Add(3, 2) → 5“经验法则:如果你需要"方法作为参数",优先考虑 Action<T>(无返回值)或 Func<T,TResult>(有返回值)。自己声明 delegate 类型的场景已经很少了。”
“记忆公式:
委托声明:delegate 返回值 委托名(参数列表);
委托变量:委托名 变量 = 方法名;
委托调用:变量(实参);LabVIEW 对照:
委托声明 → Connector Pane 设计
委托变量 → VI Ref 线
委托调用 → Call By Reference Node 执行
委托传参 → VI Ref 连入 SubVI 端子”
/* ═══════════════════════════════════════════════
* 委托(Delegate)最简演示
* 一句话理解:委托 = 可以把方法当参数传递
*
* LabVIEW 对照:Call By Reference Node
* ═══════════════════════════════════════════════ */
using System;
// 步骤1:声明委托类型(定义"什么样的方法"可以被传递)
// 输入: 两个 int 输出: int
delegate int MathOperation(int x, int y);
// 步骤2:写几个签名匹配的方法
class Program
{
static int Add(int a, int b) => a + b;
static int Subtract(int a, int b) => a - b;
static void Main()
{
// 步骤3:把方法"装进"委托变量
MathOperation op = Add;
Console.WriteLine($"Add(3, 2) = {op(3, 2)}"); // 5
// 步骤4:换个方法,同样的变量
op = Subtract;
Console.WriteLine($"Subtract(3, 2) = {op(3, 2)}"); // 1
// ── 委托作为方法参数 ──────────────────────
// 这是委托最实用的场景:把"做什么"的决定权交给调用方
Console.WriteLine();
Console.WriteLine("把方法当参数传入:");
CalculateAndPrint(10, 5, Add); // 输出 15
CalculateAndPrint(10, 5, Subtract); // 输出 5
}
// 这个方法接受一个"操作"作为参数
// 调用时才知道具体是加还是减
// 类比 LabVIEW:VI 的输入端子连了一个 VI Reference
static void CalculateAndPrint(int x, int y, MathOperation operation)
{
int result = operation(x, y);
Console.WriteLine($" 计算结果是: {result}");
}
}
/* ═══════════════════════════════════════════════
* 运行输出:
* Add(3, 2) = 5
* Subtract(3, 2) = 1
*
* 把方法当参数传入:
* 计算结果是: 15
* 计算结果是: 5
* ═══════════════════════════════════════════════ */