C# 委托(Delegate)
委托类型、`Action`/`Func`、多播、以及 `Delegate.Combine`。
Key Points委托是「方法的类型」,可以像变量一样传递、组合、调用。
声明与使用
C#
delegate int MathOp(int a, int b);
static int Add(int a, int b) => a + b;
static int Mul(int a, int b) => a * b;
MathOp op = Add;
Console.WriteLine(op(3, 4));
op = Mul;
Console.WriteLine(op(3, 4));
// 用 lambda 赋值
op = (a, b) => a - b;
Console.WriteLine(op(3, 4));Output
7
12
-1内置委托
C#
// Action<T>:无返回值
Action<string> log = msg => Console.WriteLine("日志:" + msg);
log("启动");
Action<int, int> printSum = (a, b) => Console.WriteLine(a + b);
printSum(1, 2);
// Func<T, TResult>:有返回值(最后一个类型参数是返回类型)
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
Func<int, int, string> describe = (a, b) => $"{a} 和 {b}";
Console.WriteLine(describe(1, 2));
// Predicate<T>:返回 bool
Predicate<int> isEven = x => x % 2 == 0;
Console.WriteLine(isEven(4));Output
日志:启动
3
25
1 和 2
True| Delegate | Signature | Use |
|---|---|---|
Action | void() | 无参无返回 |
Action<T> | void(T) | 一参无返回 |
Action<T1,T2> | void(T1,T2) | 两参无返回 |
Func<TResult> | TResult() | 无参有返回 |
Func<T,TResult> | TResult(T) | 一参有返回 |
Predicate<T> | bool(T) | 判断 |
Comparison<T> | int(T,T) | 排序比较 |
**99% of the time you need no custom delegate** — use Action/Func. Declare your own only beyond 16 parameters or when the type deserves a meaningful name.
多播
C#
Action<string> pipeline = null!;
pipeline += msg => Console.WriteLine("A: " + msg);
pipeline += msg => Console.WriteLine("B: " + msg);
pipeline += msg => Console.WriteLine("C: " + msg);
pipeline("hello");
Console.WriteLine("--- 移除 B ---");
pipeline -= null; // 空操作
Action<string> b = msg => Console.WriteLine("B: " + msg);
// 注意:lambda 每次都是新对象,-= 删不掉,除非先存变量
Console.WriteLine(pipeline.GetInvocationList().Length);Output
A: hello
B: hello
C: hello
--- 移除 B ---
3Multicast delegates run in order. **Handlers registered as lambdas cannot be removed with
-=** — each lambda is a new object. Store the delegate in a variable to remove it later. A common source of event leaks.作为参数
C#
static int[] Filter(int[] data, Func<int, bool> predicate)
{
var result = new List<int>();
foreach (var x in data)
{
if (predicate(x)) result.Add(x);
}
return result.ToArray();
}
var nums = new[] { 1, 2, 3, 4, 5, 6 };
Console.WriteLine(string.Join(",", Filter(nums, x => x % 2 == 0)));
Console.WriteLine(string.Join(",", Filter(nums, x => x > 3)));
// 组合
Func<int, int> inc = x => x + 1;
Func<int, int> dbl = x => x * 2;
Func<int, int> both = x => dbl(inc(x));
Console.WriteLine(both(5));Output
2,4,6
4,5,6
12Passing delegates is the modern strategy pattern. LINQ's Where takes a Func<T, bool>, which is why any condition works.
委托与 lambda 的关系
C#
Func<int, int> f1 = delegate (int x) { return x * 2; }; // 匿名方法(旧写法)
Func<int, int> f2 = x => x * 2; // lambda(新写法)
Console.WriteLine(f1(3));
Console.WriteLine(f2(3));
// 方法组转换:直接传方法名
static int Triple(int x) => x * 3;
Func<int, int> f3 = Triple;
Console.WriteLine(f3(3));Output
6
6
9声明与使用
C#
delegate int MathOp(int a, int b);
static int Add(int a, int b) => a + b;
static int Mul(int a, int b) => a * b;
MathOp op = Add;
Console.WriteLine(op(3, 4));
op = Mul;
Console.WriteLine(op(3, 4));
// 用 lambda 赋值
op = (a, b) => a - b;
Console.WriteLine(op(3, 4));Output
7
12
-1内置委托
C#
// Action<T>:无返回值
Action<string> log = msg => Console.WriteLine("日志:" + msg);
log("启动");
Action<int, int> printSum = (a, b) => Console.WriteLine(a + b);
printSum(1, 2);
// Func<T, TResult>:有返回值(最后一个类型参数是返回类型)
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
Func<int, int, string> describe = (a, b) => $"{a} 和 {b}";
Console.WriteLine(describe(1, 2));
// Predicate<T>:返回 bool
Predicate<int> isEven = x => x % 2 == 0;
Console.WriteLine(isEven(4));Output
日志:启动
3
25
1 和 2
True| 委托 | 签名 | 用途 |
|---|---|---|
Action | void() | 无参无返回 |
Action<T> | void(T) | 一参无返回 |
Action<T1,T2> | void(T1,T2) | 两参无返回 |
Func<TResult> | TResult() | 无参有返回 |
Func<T,TResult> | TResult(T) | 一参有返回 |
Predicate<T> | bool(T) | 判断 |
Comparison<T> | int(T,T) | 排序比较 |
**99% 的情况不需要自定义 delegate**,直接用 Action / Func。只有当参数太多(超过 16 个)或想给类型一个有意义的名字时才自己声明。
多播
C#
Action<string> pipeline = null!;
pipeline += msg => Console.WriteLine("A: " + msg);
pipeline += msg => Console.WriteLine("B: " + msg);
pipeline += msg => Console.WriteLine("C: " + msg);
pipeline("hello");
Console.WriteLine("--- 移除 B ---");
pipeline -= null; // 空操作
Action<string> b = msg => Console.WriteLine("B: " + msg);
// 注意:lambda 每次都是新对象,-= 删不掉,除非先存变量
Console.WriteLine(pipeline.GetInvocationList().Length);Output
A: hello
B: hello
C: hello
--- 移除 B ---
3多播委托用
+= 组合,按顺序依次执行。**用 lambda 注册的处理器无法用 -= 移除**——每次写 lambda 都生成新对象。要能移除就必须先把委托存进变量。这是事件泄漏的常见原因。作为参数
C#
static int[] Filter(int[] data, Func<int, bool> predicate)
{
var result = new List<int>();
foreach (var x in data)
{
if (predicate(x)) result.Add(x);
}
return result.ToArray();
}
var nums = new[] { 1, 2, 3, 4, 5, 6 };
Console.WriteLine(string.Join(",", Filter(nums, x => x % 2 == 0)));
Console.WriteLine(string.Join(",", Filter(nums, x => x > 3)));
// 组合
Func<int, int> inc = x => x + 1;
Func<int, int> dbl = x => x * 2;
Func<int, int> both = x => dbl(inc(x));
Console.WriteLine(both(5));Output
2,4,6
4,5,6
12把委托当参数就是「策略模式」的现代写法。LINQ 的 Where 就是接受 Func<T, bool>,所以你能传任意条件。
委托与 lambda 的关系
C#
Func<int, int> f1 = delegate (int x) { return x * 2; }; // 匿名方法(旧写法)
Func<int, int> f2 = x => x * 2; // lambda(新写法)
Console.WriteLine(f1(3));
Console.WriteLine(f2(3));
// 方法组转换:直接传方法名
static int Triple(int x) => x * 3;
Func<int, int> f3 = Triple;
Console.WriteLine(f3(3));Output
6
6
9