C# 委托(Delegate)

什么是委托

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

委托是一种动态调用方法的类型,属于引用型。

委托是对方法的抽象和封装。委托对象实质上代表了方法的引用(即内存地址)

可以理解为函数的一个包装,它使得c#中的函数可以作为参数来被传递

简单理解是这样的

比如您要管您的孩子,把孩子送进了幼儿园

OK,此时您就把您的孩子委托给了幼儿园

当幼儿园放学,将孩子交还给您的手中则是委托的回调

当然我这里的例子是说异步委托调用,您也可以同步

什么是同步委托调用?

您去银行存钱,把钱给柜员,他们帮你点钞,存款然后给您存折或卡

那么您就相当于把钱委托给了柜员让他去执行存钱的这个函数

委托的定义

1. 以delegate关键字开头。

2. 返回类型+委托类型名+参数列表

public delegate void MyDelegate(int para1, string para2);

委托能包装的方法是有一定限制的,例如能被前面的委托类型MyDelegate包装的方法需要满足以下条件:

    1.方法的返回类型必须为void;

    2.方法必须有两个参数,并且第一个参数应为int类型,第二个参数为string类型。

总结:可以被委托包装的方法必须满足以下规则:

    1.方法的签名必须与委托一致,方法签名包括参数的个数、类型和顺序;

    2.方法的返回类型要和委托一致,注意,方法的返回类型不属于方法签名的一部分。

 委托的使用

//委托使用的演示
    class Program
    {
        //1.使用delegate关键字来定义一个委托类型
        public delegate void MyDelegate(int para1, int para2);
        static void Main(string[] args)
        {
            //2.声明委托变量d
            MyDelegate d;
            //3.实例化委托类型,传递的方法也可以为静态方法,这里传递的是实例方法
             d = new MyDelegate(new Program().Add);
            //4.委托类型作为参数传递给另一个方法
            MyMethod(d);

            Console.ReadKey();
        }

        void Add(int para1,int para2)
        {
            int sum = para1 + para2;
            Console.WriteLine("两个数的和为:" + sum);
        }

        private static void MyMethod(MyDelegate mydelegate)
        {
            //5.在方法中调用委托
             mydelegate(1, 2);
        }
    }

 从以上代码可以看出,使用委托的步骤为:定义委托类型—声明委托变量—实例化委托—作为参数传递给方法—调用委托。如下具体分析委托的使用过程。

  (1)定义委托类型:public delegate void MyDelegate(int para1, int para2) 其定义方式类似于方法的定义,只是多了一个delegate关键字。

  (2)声明委托变量:MyDelegate d;既然委托是一种类型,那么可以使用委托来声明一个委托变量,相当于int a;

  (3)实例化委托: d = new MyDelegate(new Program().Add);。第二步只是声明了委托变量,但并没有将它实例化。类的实例化使用new关键字来实现,而委托也属于类类型,所以委托的实例化也使用new关键字来进行的。这里需要注意的是,委托的实例化是用一个方法名(不能带左右括号)作为参数,并且该方法的定义必须符合委托的定义,即该方法的返回类型、参数个数和类型必须与委托定义中的一样。这样,前面3步就好比构造了一个律师对象,而方法InstanceMethod好比是当事人的方法。

  (4)作为参数传递给方法:MyMethod(d);。委托使用得在C#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。

  (5)在方法中调用委托:MyMethod(d);。委托使用得在c#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。

   总结:在使用委托时,需要注意以下几个问题。

    1.在第三步中,被传递的方法的定义必须与委托定义相同,即方法的返回类型和参数个数、参数类型都必须与委托相同。并且,传递的是方法名,方法名后不能带有左右括号。

    2.在第五步中,委托的调用与方法调用类似,传递的实参类型和个数必须与委托定义一致。

    3.由于委托是方法的包装类型,所以对委托的调用也就是对其所包装的的方法的调用,上面第5步时间上是调用了Add方法来对传入的实参进行计算。

为什么要使用委托

委托使得一个方法可以作为另一个方法的参数进行传递,这就是委托最大的作用。使用委托可以将同类型的方法绑定到同一个变量上,当调用此变量时就可以一次调用绑定的方法,很方便。如下例子:

例如我们要实现一个打招呼的方法,而每个国家打招呼的方式都不一样,刚开始我们可能会像下面这样实现打招呼的方法:

public void Greeting(string name,string language)
        {
            switch (language)
            {
                case "zh-cn":
                    ChineseGreeting(name);
                    break;
                case "en-us":
                    EnglishGreeting(name);
                    break;
                default:
                    EnglishGreeting(name);
                    break;
            }
        }

        public void EnglishGreeting(string name )
        {
            Console.WriteLine("Hello, " + name);
        }

        public void ChineseGreeting(string name)
        {
            Console.WriteLine("你好, " + name);
        }

若后续我们需要添加德国、日本等打招呼方法,就必须修改Greeting方法内的case语句,来适应新的需求,这样特别不方便。有了委托,我们就可以把函数作为参数,并像如下代码实现Greeting方法:

public delegate void GreetingDelegate(string name);
        static void Main(string[] args)
        {
            //引入委托
             Program p = new Program();
            p.Greeting("小叶", p.ChineseGreeting);
            p.Greeting("Tommy Li", p.EnglishGreeting);

            Console.Read();
        }

        public void Greeting(string name,GreetingDelegate callback)
        {
            callback(name);
        }
        public void EnglishGreeting(string name)
        {
            Console.WriteLine("Hello, " + name);
        }

        public void ChineseGreeting(string name)
        {
            Console.WriteLine("你好, " + name);
        }

如下,增加德国人打招呼:

class Program
    {
        public delegate void GreetingDelegate(string name);
        static void Main(string[] args)
        {
            //引入委托
             Program p = new Program();
p.Greeting(
"小叶", p.ChineseGreeting); p.Greeting("Tommy Li", p.EnglishGreeting); p.Greeting("Ha Wa", p.GermanyGreeting); Console.Read(); } public void Greeting(string name,GreetingDelegate callback) { callback(name); } public void EnglishGreeting(string name) { Console.WriteLine("Hello, " + name); } public void ChineseGreeting(string name) { Console.WriteLine("你好, " + name); } public void GermanyGreeting(string name) { Console.WriteLine("Hallo, " + name); } }

委托链的使用

委托链其实就是委托类型,只是委托链把多个委托链接在一起而已,也就是说,我们把链接了多个方法的委托称为委托链或多路广播委托。如下:

public delegate void DelegateTest();
        static void Main(string[] args)
        {
            //用静态方法来实例化委托
             DelegateTest dtstatic = new DelegateTest(Program.method1);
            DelegateTest dtinstance = new DelegateTest(new Program().method2);
            //定义一个委托对象,一开始初始化为null,即不代表任何方法。
             DelegateTest delegatechain = null;
            //使用 “+”符号链接委托,链接多个委托后就成为了委托链
            delegatechain += dtstatic;
            delegatechain += dtinstance;
            //调用委托链
            delegatechain();
            Console.Read();
        }
private static void method1() { Console.WriteLine("这是静态方法"); } //实例方法  private void method2() { Console.WriteLine("这是实例方法"); }

从委托链中移除委托

public delegate void DelegateTest();
        static void Main(string[] args)
        {
            //用静态方法来实例化委托
             DelegateTest dtstatic = new DelegateTest(Program.method1);
            DelegateTest dtinstance = new DelegateTest(new Program().method2);

            //定义一个委托对象,一开始初始化为null,即不代表任何方法。
             DelegateTest delegatechain = null;
            //使用 “+”符号链接委托,链接多个委托后就成为了委托链
            delegatechain += dtstatic;
            delegatechain += dtinstance;
            //使用 “-”运算符 移除委托 
             delegatechain -= dtstatic;
            //调用委托链
            delegatechain();
            Console.Read();
        }
        private static void method1()
        {
            Console.WriteLine("这是静态方法");
        }

        //实例方法 
         private void method2()
        {
            Console.WriteLine("这是实例方法");
        }
使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。

多播委托

如果要调用多个方法,就需要多次显式调用这个委托。但是委托也可以包含多个方法,这种委托称为多播委托。

如果调用多播委托就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。

例如:

  • Action operations=方法一;operartion+=方法二;

  • 也可以 委托1=方法1;委托二=方法2;委托3=委托1+委托2。

所以当调用委托3时,委托1和委托2会同时执行,与第一种实现多播委托一个道理。

但是多播委托还有一个缺陷,一旦一个委托发生异常,其他委托都会停止。为了避免这个问题,应自己迭代方法列表。Delegate类定义GetInvocationList()方法,它返回一个Delegate对象数组。现在可以使用这个委托调用与委托直接相关的

方法,捕获异常,并继续下一次迭代。(.net core 中间件)

匿名方法

到目前为止,要想使委托工作,方法名必须已经存在。但还有另外一种使用委托的方式:匿名方法。

匿名方法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。

在匿名方法中您不需要指定返回类型,它是从方法主体内的 return 语句推断的。

编写匿名方法的语法

匿名方法是通过使用 delegate 关键字创建委托实例来声明的。例如:

delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
    Console.WriteLine("Anonymous Method: {0}", x);
};

代码块 Console.WriteLine("Anonymous Method: {0}", x); 是匿名方法的主体。

委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。

例如:

nc(10);

实例

下面的实例演示了匿名方法的概念:

using System;

delegate void NumberChanger(int n);
namespace DelegateAppl
{
    class TestDelegate
    {
        static int num = 10;
        public static void AddNum(int p)
        {
            num += p;
            Console.WriteLine("Named Method: {0}", num);
        }

        public static void MultNum(int q)
        {
            num *= q;
            Console.WriteLine("Named Method: {0}", num);
        }

        static void Main(string[] args)
        {
            // 使用匿名方法创建委托实例
            NumberChanger nc = delegate(int x)
            {
               Console.WriteLine("Anonymous Method: {0}", x);
            };
            
            // 使用匿名方法调用委托
            nc(10);

            // 使用命名方法实例化委托
            nc =  new NumberChanger(AddNum);
            
            // 使用命名方法调用委托
            nc(5);

            // 使用另一个命名方法实例化委托
            nc =  new NumberChanger(MultNum);
            
            // 使用命名方法调用委托
            nc(2);
            Console.ReadKey();
        }
    }
}
View Code

当上面的代码被编译和执行时,它会产生下列结果:

匿名方法是用作委托的参数的一段代码,用匿名方法定义委托的语法与前面的定义并没有区别,但在实例化委托时,就有了区别。下面是一个非常简单的代码,它说明了如何使用匿名方法:

Func<string,string> anonDel=delegate(string param)
{
  param+=mid;
  param+=" and this was added to the string.";
  return param;
}

使用匿名方法的规则很明显,它前面是关键字delegate,后面是一个字符串参数.

使用匿名方法

  1. 声明委托变量时候作为初始化表达式。
  2. 组合委托时在赋值语句的右边。
  3. 为委托增加事件时在赋值语句的右边。

匿名方法语法

delegate (parameters ){implementationcode};
  关键字     参数        方法体

匿名方法不会声明返回值类型。但是匿名方法返回值类型必须和委托返回值一样。

我们可以使圆括号为空,或省略圆括号来简化匿名方法的参数列表。但是仅在下面两项都为真的情况下才可以这么做。

  1. 委托的参数列表不包含任何out参数的委托。
  2. 匿名方法不使用任何参数。
例如:
 class Program
    {
        delegate int otherdel(int param);
        public static void Main()
        {
            otherdel del = delegate
            {
                cleanup();
                printMessage();
            };          
        }
    } 

params参数

如果委托参数包含params参数,那么params关键字就会被匿名方法的参数列表忽略。如下:

delegate int otherdel(int x,params int y);

otherdel del = delegate(int x,int y)
{

};

 

 

 

 

参考链接:https://www.cnblogs.com/xiaoyehack/tag/c%23%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/default.html?page=1

参考链接:https://www.cnblogs.com/zhan520g/p/10917053.html

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/zhaoyl9/p/12156148.html