1、什么是委托

        在C#中,委托是c/c++ 中函数指针的升级版!

我们先在c语言编写一段代码:

#include<stdio.h> 

int Add(int a,int b){
	return a+b;
}

int Sub(int a,int b){
	return a-b;
}

int main(){
	int x = 100;
	int y = 200;
	int z=0;
	
	z = Add(x,y);
	printf("%d+%d=%d\n",x,y,z); 
	
	z = Sub(x,y);
	printf("%d-%d=%d",x,y,z); 
	
	return 0;
}

运行结果如下:

接下来我们来声明一个函数指针

typedef int(* Calc)(int a,int b);

这里我们声明了一个函数指针类型,这个类型的名字叫Calc,它要求传入的参数类型为两个Int类型,限制了返回值类型为int类型。

        下面我们来尝试使用它:

#include<stdio.h>

typedef int(* Calc)(int a,int b);

int Add(int a,int b){
	return a+b;
}

int Sub(int a,int b){
	return a-b;
}

int main(){
	int x = 100;
	int y = 200;
	int z=0;
	
	Calc Fun1 = &Add;
	Calc Fun2 = &Sub;
	
	z = Fun1(x,y);
	printf("%d+%d=%d\n",x,y,z); 
	
	z = Fun2(x,y);
	printf("%d-%d=%d",x,y,z); 
	
	return 0;
}

运行结果如下:

我们发现两次结果一样,说明代码没有错误。在上面的代码中,我们发现直接调用方法和间接通过指针调用方法的结果是一样的。

直接调用:CPU 通过函数名直接获得函数所在地址并开始执行。

间接调用:CPU 通过读取函数指针存储的值获得函数所在地址并开始执行。

我们要首先理解一个概念,一切皆地址:

我们知道,java语言和c#语言都是c++语言发展而来,但是java语言为了安全性,不允许程序员直接访问地址,舍弃了与指针相关的东西,但是c#语言却通过委托来保留了与函数指针这部分对应的功能。

2、c#中的委托

先在c#中编写如下代码:

namespace EventExample
{
    class Program
    {
        static void Main(string[] args)
        {

        }
    }

    class Calculator
    {
        public void Report()
        {
            Console.WriteLine("I have 3 methods");
        }

        public int Add(int a, int b)
        {
            int result = a + b;
            return result;
        }

        public int Sub(int a, int b)
        {
            int result = a - b;
            return result;
        }
    }

}

c#中已经为我们准备好了两个委托,现在我们来使用一下:

2.1  Action 委托

我们在Main方法中编写如下代码:

 static void Main(string[] args)
 {
     Calculator calculator = new Calculator();
     Action action = new Action(calculator.Report);
 }

这里我们声明了 Action 的委托 aciton ,它接收一个返回值为void的且无参数方法

所以在构造函数中传入了calculator实例的Report方法。

接下来我们尝试使用直接调用和间接调用

 static void Main(string[] args)
 {
     Calculator calculator = new Calculator();
     Action action = new Action(calculator.Report);
     calculator.Report();
     action.Invoke();
 }

结果如下:

结果完全一样!

委托的间接调用时通过 : 委托名.Invoke();   也就是:   aciton.Invoke():

其实它还有一种简便的写法: 委托名();  也就是:   action();

 2.2  Func 委托

Func<int, int, int> func = new Func<int, int, int>(calculator.Add);

这种委托接收的是有返回值的函数,对函数接收的参数没有要求!他还是一种泛型委托。

三个int表示最后一个int是返回值类型,前面的全部都是参数类型。所以这里传入的是Add方法(Sub方法也可以)

我们来运行一下:

  int x = 100;
  int y = 200;
  Func<int, int, int> func = new Func<int, int, int>(calculator.Add);
  int z = func.Invoke(x, y);
  Console.WriteLine(z);

结果如下:
 

        public double chufa(double a, double b)
        {
            return a / b;
        }

2.3  自定义委托

首先,我们要知道,委托它是一种

我们来验证一下:

Type t = typeof(Action);
Console.WriteLine(t.IsClass);

结果如下:

所以它确实是一种类!

我们新定义一个除法方法

        public double chufa(double a, double b)
        {
            return a / b;
        }

我们来新建一个自定义委托

public delegate double Calc(double a , double b);

注意要声明在类平级的地方,这个委托名字叫Calc,第一个double是返回值类型,后面两个是参数类型。我们来使用一下:

 double a = 100;
 double b = 200;
 Calc c = new Calc(calculator.chufa);
 double d = c.Invoke(a, b);
 Console.WriteLine(d);

返回结果:

2.4  多播委托

多播委托意思就是一个委托内部封装了不止一个方法。

编写如下代码:

using System;
using System.Threading;
namespace EventExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow};
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
            Action action1 = new Action(stu1.DoHomework);
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);
            action1.Invoke();
            action2.Invoke();
            action3.Invoke();
        }
    }

    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }

        public void DoHomework()
        {
            for(int i=0; i < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s)",this.ID,i);
                Thread.Sleep(1000);
            }
        }
    }
}

结果:

像这种一个委托封装一个方法的形式叫单播委托。

接下来我们来看多播委托:

action1 += action2;
action1 += action3;

action1.Invoke();

这里的+=就相当于把后面的委托封装到前面的委托里面

现在的action1就相当于有了三个想法。

这里我执行后效果跟原来一样,但是我只执行了action1,用一个委托封装了多个方法!而且执行顺序是按照封装顺序来的。

2.5 委托的隐式异步调用

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐