Skip to content

委托 Delegate

委托类似于一个面向对象的函数指针。

类表示的是数据和方法的集合,而委托表示的是一个或多个的方法。

声明委托时确定参数和返回类型,比如 public delegate int MyDelegate(int x, int y); 可以用来委托任意一个传入两个整形并返回一个整形的方法。

实例化对象同样需要 new 关键字,并传入需要委托的函数名。

静态方法和动态方法都可以被委托。

namespace SimpleExample
{
    public delegate int MyDelegate(int x, int y);
    class Calc
    {
        public static int Add(int x, int y)
        {
            return x + y;
        }

        public static int Minus(int x, int y)
        {
            return x - y;
        }
    }

    class Program
    {
        static void Main()
        {
            // 创建委托实例
            MyDelegate mdAdd = new MyDelegate(Calc.Add);
            MyDelegate mdMinus = new MyDelegate(Calc.Minus);
            // 使用委托
            Console.WriteLine(mdAdd(1, 2));
            Console.WriteLine(mdMinus(1, 2));
        }
    }
}

创建委托实例时,可以不写委托的类型名。比如下面的两种写法是等价的。

MyDelegate mdAdd = new MyDelegate(Calc.Add);
MyDelegate mdAdd = Calc.Add;

委托可以被组合,但其实委托不可以被修改,委托在创建时值就已经确定了。

在通过 += 运算符给委托添加方法后,实际是创建了一个新的委托。

MyDelegate mdAdd = Calc.Add;
MyDelegate mdMinus = Calc.Minus;
// mdAdd 会被执行,但只有mdMinus会返回值。
MyDelegate mdBoth = mdAdd + mdMinus;

如果委托有多个方法,而且有返回值的话,最后一个方法的返回值是委托的返回值,其他的返回值都会被忽略。

委托支持引用参数,值会根据委托列表中的方法而改变。在调用委托列表的下一个方法时,会将当时的新值传入。

...
// 注意声明时的ref关键字
public static int Plus(ref int x) => x++;
public delegate int MyDelegate2(ref int x);
...

MyDelegate2 mdPlus = Calc.Plus;
mdPlus += mdPlus;

int x = 1;
mdPlus(ref x);
Console.WriteLine("x: {0}", x); // x: 3

匿名方法

可以通过 delegate 来写匿名方法,匿名方法的返回值与委托的类型一致。

delegate int OtherDel(int InParam);
public void AnonymousMethod()
{
    OtherDel del = delegate (int x) { return x += 20; };
    Console.WriteLine(del(2));
}

Lambda 表达式

上面的匿名方法可以写成 Lambda 表达式。

OtherDel del = delegate (int x) { return x += 20; };
OtherDel del2 = (int x) => x += 20; // lambda
OtherDel del3 = x => x += 20; // lambda