C# 委托(Delegate)

委托类型、`Action`/`Func`、多播、以及 `Delegate.Combine`。

Intermediate
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
DelegateSignatureUse
Actionvoid()无参无返回
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 ---
3
Multicast 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
12

Passing 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

Friends & Partners