什么是委托
可以认为委托是持有一个或多个方法的对象。但它与对象不同,因为委托可以被执行。当执行委托时,委托会执行它所“持有”的方法。先看一个完整的使用示例。
// See https://aka.ms/new-console-template for more information
delegate void MyDel(int value);  // 声明委托类型
class Program
{
    void PrintLow(int value)
    {
        Console.WriteLine("{0} - Low value", value);
    }
    void PrintHigh(int value)
    {
        Console.WriteLine("{0} - High value", value);
    }
    static void Main(string[] args)
    {
        Program p = new Program();
        MyDel del;  // 声明委托变量
        Random rand = new Random();
        int randomValue = rand.Next(1, 101);
        del = randomValue < 50 ? p.PrintLow : p.PrintHigh;  // 选择调用哪个方法
        del(randomValue);  // 调用委托方法
    }
}
如果生成的随机数 randomValue 小于50,则 del 引用的是 p.PrintLow;否则,del 引用的是 p.PrintHigh。
委托概述
下图是使用类和委托的对比:
 
可以把 delegate 看成一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。如下图所示:
 
委托的几点说明:
-  方法的列表称为调用列表; 
-  委托保存的方法可以来自任何类或者结构,只要它们在委托的返回类型和委托的签名(包括ref和out修饰符)上保持一致; 
-  调用列表中的方法可以是实例方法,和静态方法; 
-  执行委托时,会按照方法的添加顺序执行调用列表。 
声明委托类型

创建委托对象
委托是引用类型,因此初始化委托变量需要创建一个对象。有两种方式创建委托对象:
-  使用 new运算符:class Obj { void MyM1(int value) {} static void Other(int value) {} } delegate void MyDel(int value); MyDel del1, del2; Obj obj = new Obj(); del1 = new MyDel(obj.MyM1); // 使用一个对象的方法 del2 = new MyDel(Obj.Other); // 使用静态方法创建委托对象
-  省略 new运算符:MyDel del1, del2; del1 = obj.MyM1; // 使用一个对象的方法 del2 = Obj.Other; // 使用静态方法创建委托对象当为委托变量赋值时,除了为委托分配内存,创建委托对象还会把第一个方法放入委托的调用列表。 当然,我们还可以在声明委托变量时初始化委托变量。 MyDel del1 = obj.MyM1;当我们为同一个委托变量赋值另外一个委托对象时,之前的委托对象就会被垃圾回收器回收。 MyDel delVar; delVar = myInstObj.MyM1; ... delVar = SClass.OtherM2;

组合委托
可以将两个同类型的委托变量进行 + 操作,赋值给另外一个同类型的新变量,完成委托的组合:
MyDel delA = myInstObj.MyM1;
MyDel delB = SClass.OtherM2;
MyDel delC = delA + delB;

为委托添加或者删除方法
委托可以持有多个方法, 通过使用运算符 += 和 -= 可以为委托添加或者删除方法。
MyDel del = inst.MyM1;
del += SCl.m3;
del += X.Act;

 从委托移除方法:
del -= SCl.m3;

移除委托时需要注意以下事项:
- 如果委托的调用列表中存在多个实例,-=运算符将从列表的最后开始搜索,并移除第一个与方法匹配的实例;
- 当要删除的方法在调用列表不存在时,什么也不会发生;
- 试图调用空委托将会导致异常,因此执行委托之前有必要进行 null 判空。
调用委托
调用委托的方式与调用方法一样。用于调用委托的参数将会传递给调用列表当中的每一个方法(除非有输出参数)。
MyDel delVar = inst.MyM1;
delVar += SCl.m3;
delVar += X.Act;
...
delVar(55);

下面是一个完整的示例:
delegate void PrintFunction();
class Test
{
    public void Print1()
    {
        Console.WriteLine("Print1 -- instance method");
    }
    public static void Print2()
    {
        Console.WriteLine("Print2 -- static method");
    }
    
}
class Program
{
    static void Main()
    {
        Test t = new Test();
        PrintFunction pf;
// 实例方法
        pf = t.Print1;
        
        // 给委托增加3个另外的方法
        pf += Test.Print2;
        pf += t.Print1;
        pf += Test.Print2;   // 现在委托含有4个方法
        if (null != pf)
        {
            pf();
        }
        else
        {
            Console.WriteLine("委托为空");
        }
    }
}
输出如下:
Print1 -- instance method
Print2 -- static method
Print1 -- instance method
Print2 -- static method
调用带引用参数的委托
如果委托有引用参数,在调用委托列表中的下一个方法时,参数的新值会传给下一个方法。
delegate void MyDel(ref int x);
class MyClass
{
    public void Add2(ref int x) { x += 2; }
    public void Add3(ref int x) { x += 3; }
    static void Main()
    {
        MyClass mc = new MyClass();
        MyDel del = mc.Add2;
        del += mc.Add3;
        del += mc.Add2;
        int x = 5;
        del(ref x);
        Console.WriteLine("Value: {0}", x);
    }
}
输出如下:
Value: 12

Lambda 表达式
C# 当中的 Lambda 表达式是一种简洁的方式来表示匿名方法。通常用于简化代码,尤其是在与 LINQ、委托或事件等相关的场景中。(LINQ 和 事件会在后面相关文章中讲到)。
语法:
(parameters) => expression
- parameters:代表输入参数。如果只有一个参数,可以省略- ()。
- =>:称为 lambda 操作符,表示从参数到表达式或代码块的映射。
- expression:返回的表达式。如果有多条语句,可以使用代码块{}。
Lambda 表达式的常见使用场景
- 委托与 Lambda 表达式
delegate void MyDel(int x);
MyDel del = x => x *2;
用于 Func<T>委托:
using System;
class Program
{
    static void Main()
    {
        // Func 委托,接受两个整数参数,返回它们的和
        Func<int, int, int> add = (a, b) => a + b;
        
        int result = add(3, 4);
        Console.WriteLine(result);  // 输出 7
    }
}
Func 是 C# 中一个常用的泛型委托类型,在 C# 3.0 中引入,专门用于表示带有返回值的方法。在使用时,Func<T> 委托的最后一个泛型参数是返回值类型(必须有),前面的泛型参数是输入参数类型(可以没有输入参数)。
-  Lambda 表达式与 LINQ 查询 using System; using System.Linq; using System.Collections.Generic; class Program { static void Main() { List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 }; // 使用 Lambda 表达式筛选出大于 3 的数 var result = numbers.Where(n => n > 3).ToList(); foreach (var number in result) { Console.WriteLine(number); // 输出 4, 5, 6 } } }
-  用于事件处理 using System; class Program { static void Main() { Action<string> messagePrinter = msg => Console.WriteLine(msg); messagePrinter("Hello, Lambda!"); // 输出 "Hello, Lambda!" } }
委托的使用场景
委托的主要使用场景如下:
- 事件处理。
- 回调函数。
以上两个使用场景的具体例子会在后面文章介绍到。
小结:本章主要介绍了 C# 当中委托和 Lambda 的概念、用法。
各位道友,码字不易,如有收获,记得一键三连啊。

















