• C#委托全解析


    什么是委托?                                                                                      

      委托类似于C语言中的函数指针,更类似于C++11中的std::function,但更安全也更强大。委托如同类一样是一个类型,只不过类的实例指向一个对象,而委托的实例指向一个方法,该方法可以是成员方法也可以是静态方法。

     

     

    委托的基本使用

      下面介绍委托的两种初始化和调用方式

    using System;
    
    namespace Delegate01
    {
        class Program
        {
            public delegate string MyDelegate(string s);  //定义一个委托,其返回值是string,参数为空
            static void Main(string[] args)
            {
                //委托初始化和调用的第一种方式。该例子中绑定的方法为成员方法
                Program self = new Program();
                MyDelegate d1 = new MyDelegate(self.ShowSelf);    
                Console.WriteLine(d1.Invoke("tianya"));
    
                //委托初始化和调用的第二种方式,这种方式因为更简便,所以更常用。该例子中绑定的方法为静态方法
                MyDelegate d2 = Program.Show;                              
                Console.WriteLine(d2("feng"));   
            }
            
            string ShowSelf(string str)
            {
                Console.WriteLine("ShowSelf str:" + str);
                return "ShowSelf Ret";
            }
    
            static string Show(string str)
            {
                Console.WriteLine("Show str:" + str);
                return "Show Ret";
            }
        }
    }

    Action委托和Func委托

      Ation 委托可以用来替代返回值是 void 的委托,泛型参数表示委托的参数,至多可以传递16个参数,其中没有泛型参数的Action类可调用没有参数的方法。

      Func 委托可以用来替代返回值不是 void 的委托,最后一个泛型参数表示返回类型,其它参数表示委托的参数,至多可以传递16个参数和一个返回类型,Func委托至少包括一个泛型参数,可调用没有参数的方法。

      Action 和 Func 委托并没有特殊之处,只是为了方便使用而提出的,尤其是把委托做为函数的某个参数时,不需要显式定义一个委托类型。

    using System;
    
    namespace Delegate02
    {
        class Program
        {
            public delegate int MyDelegate(int i, int j);  //定义一个委托
            static void Main(string[] args)
            {
                CallDelegate(Add);                          //方法参数是一个委托定义
                CallDelegateWithFunc(Add);          //方法参数是一个Func委托
                CallDelegateWithAction(Show);       //方法参数是一个Action委托
            }
            
            //如果不使用Action和Func,将委托做为参数,必须要先定义一个委托类型
            static void CallDelegate(MyDelegate d)
            {
                d(1,2);
            }
    
            //如果使用Action和Func,将委托做为参数,就不需要先行定义。明显这种写法更简洁清晰
            static void CallDelegateWithFunc(Func<int,int,int> d)
            {
                d(10,20);
            }
    
            static void CallDelegateWithAction(Action d)
            {
                d();
            }
    
            static int Add(int i, int j)
            {
                Console.WriteLine("sum:" + (i + j).ToString());
                return i + j;
            }
    
            static void Show()
            {
                Console.WriteLine("Show");
            }
        }
    }

    匿名方法和lambda表达式

    using System;
    
    namespace Delegate03
    {
        class Program
        {
            static void Main(string[] args)
            {
                //匿名方法使用 delegate关键字修饰,主要用于一些只需要使用一次的方法,这样就不需要显式去定义。
                Func<int,int,int> f1= delegate(int i,int j) { return i + j; };
                Console.WriteLine(f1(10, 20));
    
                //Lambda表达式是匿名方法的一种更简化写法
                Func<int, int, int> f2 = (i, j) => { return i + j; };
                Console.WriteLine(f2(10, 20));
    
                //Lambda的参数只有一个的时候,可以不需要小括号,当函数体中的语句只有一句的时候,可以不加上大括号和return关键字
                Action<string> a = s => Console.WriteLine(s);           //等同于 Action<string> a = (s) => { Console.WriteLine(s); };
                a("Action");
            }
        }
    }

    多播委托

      所谓多播委托,是指一个委托可以注册多个方法,可以使用 +,+=,-,-= 操作符,在调用委托时,可能触发多个方法调用。

    using System;
    
    namespace Delegate04
    {
        class Program
        {
            static void Main(string[] args)
            {
                //多播委托
                Action<string> a = Run;
                a += Jump;
                a("Cat");
    
                //使用Lambda实现的多播委托
                Action<string> a1 = name => Console.WriteLine(name + "->Run");
                a1 += name => Console.WriteLine(name + "->Jump");
                a1("Mouse");
    
                //使用有返回值的多播委托,因为有返回值,这种多播委托实际上没有效果,只会调用最后一个注册的方法。这个要格外注意
                Func<int, int, int> f = Add;
                f += Mul;
                Console.WriteLine(f(10, 20));
    
                //多播委托在调用时,如果其中一个方法调用抛出异常,则后续的方法将不会执行。
                //可以通过 GetInvocationList 方法获取该委托对象上已注册的方法列表,然后依次执行
                Delegate[] delegates = a.GetInvocationList();
                foreach (Action<string> action in delegates)
                {
                    action("Rabbit");
                }
                //使用 DynamicInvoke 方法可以在委托的类型不可知的情况下通过反射来调用,但对性能有影响。
                foreach (var action in a.GetInvocationList())
                {
                    action.DynamicInvoke("Rabblit");
                }
            }
    
            static void Run(string name)
            {
                Console.WriteLine(name + "->Run");
            }
    
            static void Jump(string name)
            {
                Console.WriteLine(name +"->Jump");
            }
    
            static int Add(int i, int j)
            {
                return i + j;
            }
    
            static int Mul(int i,int j)
            {
                return i * j;
            }
    
        }
    }

    事件

      事件是一种受约束的委托,通过 event 关键字修饰,事件只能定义为类的成员,且只能在当前类中被调用,你可以理解它为私有的(但其实不是,而且必须不是,因为其它类需要注册该委托)。那么为什么要弄出事件这个东西来呢?主要是为了实现发布订阅机制,在发布者类中定义一个事件,并由发布者类中的一个公有方法调用该事件,而诸多订阅者则可以为事件注册方法,注册好之后,调用发布者中的公有方法即可触发事件。如果使用普通的委托,会带来一个问题,即订阅者也可以直接调用委托。

    using System;
    
    namespace Delegate05
    {
    
        class Cat
        {
            public event Action action;
            
            //当该方法调用后,会回调定义好的委托,继续执行已经注册的方法
            public void Shout()
            {
                Console.WriteLine("Cat Shout!");
                action();         
            }
        }
    
        class Mouse
        {
            private string name;
            public Mouse(string name)
            {
                this.name = name;
            }
            public void Run()
            {
                Console.WriteLine("name:" + name + " Run!");
            }
    
            public void Jump()
            {
                Console.WriteLine("name:" + name + " Jump!");
            }
        }
    
        class Program
        {
            public static void Main()
            {
                Mouse mouse1 = new Mouse("mouse1");
                Mouse mouse2 = new Mouse("mouse2");
                Cat cat = new Cat();
                cat.action += mouse1.Run;
                cat.action += mouse1.Jump;
                cat.action += mouse2.Run;
                cat.action += mouse1.Jump;
                cat.Shout();
                //cat.action();         //使用事件时,无法在类外调用委托。
            }
        }
    }
  • 相关阅读:
    【STL源码学习】STL算法学习之二
    android_SurfaceView 画图
    android_layout_linearlayout(一)
    LINUX_记录(一)
    工作经验之石氏thinking
    android_layout_linearlayout(二)
    android_layout_relativelayout(一)
    两个线程解决一个线程卡之路
    android_layout_relativelayout(二)
    android_layout_framelayout
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/4913999.html
Copyright © 2020-2023  润新知