zoukankan      html  css  js  c++  java
  • 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

     

     

     

     

     

     

  • 相关阅读:
    [置顶] Django 微信开发(一)——环境搭建
    opencv学习_5 (IplImage的结构)
    HDU 3910 (13.10.31)
    Python源码学习七 .py文件的解释
    Android高效加载大图、多图解决方案,有效避免程序内存溢出现象
    记录cocos2d-html5与cocosd-x jsb中遇到的坑
    【PAT Advanced Level】1011. World Cup Betting (20)
    Linux文件实时同步,可实现一对多
    mahout源码分析之DistributedLanczosSolver(五)Job over
    php引入lucene方法
  • 原文地址:https://www.cnblogs.com/zhaoyl9/p/12156148.html
Copyright © 2011-2022 走看看