C# 委托

一、方法的描述

假设有如下方法,Method1,Method2,Method3,Method4,Method5,抛开方法的内容,我们来观察一下他们的名字、参数和它的返回值。

class Example
{
    public void Method1(int a)
    {
        Console.WriteLine("this is method1: " + a);
    }

    public void Method2(int b)
    {
        Console.WriteLine("this is method2: " + b);
    }

    public int Method3(int m)
    {
        Console.WriteLine("this is method3: " + m);
        return m * m;
    }

    public int Method4(int n)
    {
        Console.WriteLine("this is method4: " + n);
        return n + n;
    }

    public int Method5(int x, int y)
    {
        Console.WriteLine("this is method5");
        return x + y;
    }
}

 

当我们来描述一个方法的时候,其实有两个特征:一个是参数类型列表,一个是返回值。比如:

 public void Method1(int a) 

"一个参数为(int), 返回值为void的方法";

 public void Method2(int b)  

"一个参数为(int), 返回值为void的方法";

 public int Method3(int m) 

 "一个参数为(int), 返回值为int的方法";

 public int Method4(int n)  

"一个参数为(int), 返回值为int的方法";

 public int Method5(int x, int y) 

 "一个参数为(int,int), 返回值为int的方法"。

可以发现,Method1,Method2属于同一类,Method3,Method4属于同一类。思考一下,一般的类型都是由一个属于来描述的,比如整数类型用int,学生类型用Student这种class类型,接口用interface,如何用一个标准的术语来描述一个方法类型?

 

 二、委托

委托(delegate),是对一个方法类型的描述。例如:

 public delegate void DelegateType(int a); 

DelegateType类型指的是“一种参数为(int), 返回值为void的方法”,其中delegate声明它是个委托类型,DelegateType是类型的名字。

 一个委托对象,就是一个方法的实例,传参时要指定某个对象的某个方法,例如:

// 第一类方法
public delegate void DelegateType1(int a);

// 第二类方法
public delegate int DelegateType2(int a);

// 第三类方法
public delegate int DelegateType3(int a, int b);

static void Main(string[] args)
{
    Example ex = new Example();

    DelegateType1 method1 = new DelegateType1(ex.Method1);
    method1(2022); //相当于 ex.Method1(2022);

}

 

 三、匿名委托

每次实例化一个委托时,都需要事先定义一个委托所要调用的方法。为了简化这个流程,官方给出了一种匿名方法来实例化委托。这样我们在实例化委托时可以非常方便的实例化它的方法。

// 第一类方法
public delegate void DelegateType1(int a);

// 第二类方法
public delegate int DelegateType2(int a);

// 第三类方法
public delegate int DelegateType3(int a, int b);

static void Main(string[] args)
{
    // 等号后面为匿名委托
    DelegateType1 type1 = delegate (int a)
    {
        Console.WriteLine("this is type1: " + a);
    };

    type1(2022);
}

 

四、Lambda表达式

纵然匿名方法使用很方便,官方还是用了一种更加方便的写法代替了匿名方法,Lambda表达式。

 以上的匿名委托的实例化最佳改进

static void Main(string[] args)
{
    // Lambda表达式
    DelegateType1 type1 = a => Console.WriteLine("this is type1: " + a);

    type1(2022);
}

其中=>符号代表Lambda表达式,它的左侧是参数,右侧是要返回或执行的语句。参数要放在圆括号中,若只有一个参数,为了方便起见可省略圆括号。有多个参数或者没有参数时,不可省略圆括号。
相比匿名函数,在表达式Lambda中,方法体的花括号{}和return关键字被省略掉了。 

 

五、Action与Func

上面讲到,委托实际上是对一类方法的特征描述。其实官方已经给出了两个通用的Delegate:

System.Action 表示返回值为void的方法;

System.Func 表示返回值不为void的方法;

几乎所有的方法,都可以用这两种委托来表示。因此上面介绍的方法可以如下表示

static void Main(string[] args)
{
    Example ex = new Example();

    Action<int> action1 = new Action<int>(ex.Method1);
    action1(2022); //相当于 ex.Method1(2022);

    Func<int, int, int> func1 = new Func<int, int, int>(ex.Method5);
    func1(2, 6); //相当于 ex.Method5(2, 6);
}

 

六、委托参数的定义和使用

委托的运用,最具代表性的就是LINQ,LINQ 查询表达式模式依赖于其所有功能的委托。比如:

 var smallNumbers = numbers.Where(n => n < 10); 

其中,Where方法内传递的就是一个以Lambda表示的委托类型,那么我们自己是否也可以定义参数为委托类型的方法呢?当然是可以的。

static void Main(string[] args)
{
    // 创建一个 0-9 的数组
    int[] arr = new int[10];

    for (int i = 0; i < 10; i++)
    {
        arr[i] = i;
    }

    // 累加大于5的值 6 + 7 + 8 + 9 = 30
    var num = SumNumber(arr, a => a > 5);
}

// 参数中有委托类型的方法
private static long SumNumber(int[] array, Func<int, bool> predicate)
{
    long number = 0;
    foreach(var item in array)
    {
        // 表达式条件成立,则累加
        if (predicate(item))
        {
            number += item;
        }
    }
    return number;
}

我们先定义一个返回值是(long),参数是(int[], Func<int, bool>)的SumNumber方法。在调用此方法时传递数组,和lambda表达式。

 

posted @ 2022-02-06 21:14  Zeng。  阅读(50)  评论(0编辑  收藏  举报