zoukankan      html  css  js  c++  java
  • 04.委托Delegation

    1. 基本了解

    1.1 委托简述

    官方文档

    委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用,用于将方法作为参数传递给其他方法,可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托

    其它说明

    委托在IL中就是一个类(本质上是类),继承与System.MulticastDelegate类(特殊类)

    委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递

    委托类型

    自定义委托:Delegate

    系统内置委托:ActionFunc

    委托声明

    可以声明在类的外部也可以在内部,在IL中,无论在内外都会编译到类的内部

    委托在实例化时,需要传入一个方法,此方法返回值,参数(类型,个数,顺序)与委托一致

    1.2 使用步骤

    • 声明一个委托
    • 委托的实例化,传入指定方法
    • 调用执行

    2. Delegate委托

    Delegate:常用到的一种声明,且至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型

    2.1 示例一:无参,无返回值

    // 1.声明委托
    public delegate void NoReturnNoPare();
    
    // 2.准备委托执行方法
    public void Show()
    {
        Console.WriteLine("无参,无返回值");
    }
    
    // 3.实例,调用委托
    public void Start()
    {
        NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
        d1.Invoke();
    }
    

    2.2 示例二:有参,无返回值

    // 1.声明委托
    public delegate void NoReturnWithPare(int x, int y);
    
    // 2.准备委托执行方法
    public void Show(int x,int y)
    {
        Console.WriteLine("有参,无返回值");
    }
    
    // 3.实例,调用委托
    public void Start()
    {
        NoReturnWithPare d2 = new NoReturnWithPare(this.Show);
        d2.Invoke(1,2);
    }
    

    2.3 示例三:有参,有返回值

    // 1.声明委托
    public delegate int WithReturnWithPare(int x, int y);
    
    // 2.准备委托执行方法
    public int Show(int x, int y)
    {
        return x + y;
    }
    
    // 3.实例,调用委托
    public void Start()
    {
        WithReturnWithPare d2 = new WithReturnWithPare(this.Show);
        // 返回值类型,编译器会自动推断
        int IResult = d2.Invoke(1, 2);
        Console.WriteLine(IResult);
    }
    

    3. Action委托

    Action是系统内置委托(无需声明),是无返回值的泛型委托,至少0个参数,至多16个参数,且无返回值

    3.1 示例一:无参,无返回值

    // 1.定义执行方法
    public void Show()
    {
        Console.WriteLine("无参,无返回值");
    }
    
    // 2.调用执行
    public void Start()
    {
        Action action = new Action(this.Show);
        // Action<int, int> action = this.Show;
        action.Invoke();
    }
    

    3.2 示例二:有参,无返回值

    // 1.定义执行方法
    public void Show(int x, int y)
    {
        Console.WriteLine("有参,无返回值");
    }
    
    // 2.调用执行
    public void Start()
    {
        Action<int, int> action = new Action<int,int>(this.Show);
        // Action<int, int> action = this.Show;
        action.Invoke(1,2);
    }
    

    3.3 示例三:使用 lambda 表达式

    Action<int, int> action = (x, y) => { };
    action.Invoke(1, 2);
    

    3.4 示例四:将委托作为方法参数

    public void Start()
    {
        Action<int> action = (x) => { Console.WriteLine(x); };
        Show(action, 2);
    }
    
    public void Show<T>(Action<T> ac, T inputParam)
    {
        ac(inputParam);
    }
    

    4. Func委托

    Func是有返回值的泛型委托,至少0个参数,至多16个参数,根据返回值泛型返回;必须有返回值,不可void,且最后一位泛型类型,为返回值类型

    4.1 示例一:无参,有返回值

    public void Start()
    {
        Func<string> func = new Func<string>(this.Show);
        string IResult = func.Invoke();
        Console.WriteLine(IResult);
    }
    
    public string Show()
    {
        return "libai";
    }
    

    4.2 示例二:有参,有返回值

    public void Start()
    {
        Func<int, string> func = new Func<int, string>(this.Show);
        string IResult = func.Invoke(1);
        Console.WriteLine(IResult);
    }
    
    public string Show(int i)
    {
        return "libai	" + i;
    }
    

    4.3 示例三:使用lambda表达式

    public void Start()
    {
        Func<int> func1 = () => { return 1; };
        int IResultInt = func1.Invoke();
        Console.WriteLine(IResultInt);
    
        Func<int, string> func2 = (i) => { return i.ToString(); };
        string IResult = func2.Invoke(1);
        Console.WriteLine(IResult);
    }
    

    4.4 示例四:将委托作为方法参数

    例子一:简化

    public void Start()
    {
        Func<int, int, int> func = (x, y) => { return x + y; };
        int IResultInt = Test(func, 1, 2);
        Console.WriteLine(IResultInt);
    }
    
    public int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
    {
        return func(a, b);
    }
    

    示例二:一般写法

    static void Main(string[] args)
    {
        Console.WriteLine(Test<int,int>(Fun,100,200));
        Console.ReadKey();
    }
    public static int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
    {
        return func(a, b);
    }
    private static int Fun(int a, int b)
    {
        return a + b;
    }
    

    5. 链式委托

    5.1 文档说明

    官方文档

    委托对象的一个有用属性在于可通过使用 + 运算符将多个对象分配到一个委托实例,多播委托包含已分配委托列表,此多播委托被调用时会依次调用列表中的委托;仅可合并类型相同的委托

    • - 运算符可用于从多播委托中删除组件委托,顺序,从下至上(委托列表中没有移除的委托时不会报错)
    • + 运算符可用于将委托组件添加到委托列表,顺序,从上而下

    在执行有返回值的委托链时,只能得到最后一个委托的结果

    其它文档

    委托链(多播委托)是一个由委托组成的链表,而不是一个新的东西,所有的自定义委托都直接集成自System.MulticastDelegate类型,这个类型即是为委托链而设计的

    链式委托是指一个委托的链表,而不是指另外一类特殊的委托,当执行链上的一个方法时,后续委托将会被依此执行

    System.MuticastDelegate定义了对链式委托的支持,在System.Delegate的基础上,增加了一个指向后续委托的指针,这样就实现了一个简单的链表结构

    5.2 示例一:统一执行

    public delegate void NoReturnWithPare(int x,int y);
    
    public void Start()
    {
        NoReturnWithPare noReturnWith = new NoReturnWithPare(this.Fun1);
        noReturnWith += this.Fun2;
        noReturnWith += this.Fun1;
        noReturnWith += (x,y)=>{ Console.WriteLine("Lambda:	" + x + y);};
        noReturnWith.Invoke(1, 2);
    }
    
    public void Fun1(int x, int y)
    {
        Console.WriteLine("Fun1:	" + x + y);
    }
    
    public void Fun2(int x, int y)
    {
        Console.WriteLine("Fun2:	" + x + y);
    }
    

    5.3 示例二:逐个执行

    注意:逐个执行时,单项不能用var声明,必须使用委托的具体类型

    逐个指定,返回值,问题,b委托使用a的返回值?

    public void Start()
    {
        NoReturnWithPare noReturnWith = new NoReturnWithPare(this.Fun1);
        noReturnWith += this.Fun2;
        noReturnWith += this.Fun1;
        foreach (NoReturnWithPare item in noReturnWith.GetInvocationList())
        {
            item.Invoke(1,2);
        }
    }
    
    public void Fun1(int x, int y)
    {
        Console.WriteLine("Fun1:	" + x + y);
    }
    
    public void Fun2(int x, int y)
    {
        Console.WriteLine("Fun2:	" + x + y);
    }
    

    5.4 示例三:责任链模式

    典型案例:用代码模拟,猫叫了,狗叫了,然后老鼠跑了

    普通实现

    using System;
    
    namespace de2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Cat cat = new Cat();
                cat.Miao();
            }
        }
    
        public class Cat
        {
            public void Miao()
            {
                Console.WriteLine("猫叫了");
                new Dog().Wang();
                new Mouse().Run();
            }
        }
        public class Dog
        {
            public void Wang()
            {
                Console.WriteLine("狗叫了");
            }
        }
        public class Mouse
        {
            public void Run()
            {
                Console.WriteLine("老鼠跑了");
            }
        }
    }
    

    使用责任链模式实现(委托)

    using System;
    
    namespace de2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Cat cat = new Cat();
    
                cat.miaoAction += new Dog().Wang;
                cat.miaoAction += new Mouse().Run;
    
                cat.MiaoDelegate();
            }
        }
    
        public class Cat
        {
            public void Miao()
            {
                Console.WriteLine("猫叫了");
            }
    
            public Action miaoAction;
            public void MiaoDelegate()
            {
                this.Miao();
                this.miaoAction.Invoke();
            }
        }
        public class Dog
        {
            public void Wang()
            {
                Console.WriteLine("狗叫了");
            }
        }
        public class Mouse
        {
            public void Run()
            {
                Console.WriteLine("老鼠跑了");
            }
        }
    }
    

    使用责任链模式实现(抽象方法)

    using System;
    using System.Collections.Generic;
    
    namespace de2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Cat cat = new Cat();
                cat.Add(new Dog());
                cat.Add(new Mouse());
                cat.AbsServer();
            }
        }
    
        public interface IAbsServer
        {
            void Do();
        }
    
        public class Cat : IAbsServer
        {
            private List<IAbsServer> list = new List<IAbsServer>();
            public void Add(IAbsServer absServer)
            {
                list.Add(absServer);
            }
    
            public void AbsServer()
            {
                this.Do();
                foreach (var item in list)
                {
                    item.Do();
                }
            }
    
            public void Miao()
            {
                Console.WriteLine("猫叫了");
            }
    
            public void Do()
            {
                this.Miao();
            }
        }
        public class Dog : IAbsServer
        {
            public void Do()
            {
                this.Wang();
            }
    
            public void Wang()
            {
                Console.WriteLine("狗叫了");
            }
        }
        public class Mouse : IAbsServer
        {
            public void Do()
            {
                this.Run();
            }
    
            public void Run()
            {
                Console.WriteLine("老鼠跑了");
            }
        }
    }
    

    使用责任链模式实现(事件)

    using System;
    
    namespace de2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Cat cat = new Cat();
    
                cat.miaoEvent += new Dog().Wang;
                cat.miaoEvent += new Mouse().Run;
    
                cat.MiaoEvent();
            }
        }
    
        public class Cat
        {
            public void Miao()
            {
                Console.WriteLine("猫叫了");
            }
    
            /// <summary>
            /// 事件,只能在事件所在类(本身类,子类不可)的内部 Invoke 执行
            /// </summary>
            public event Action miaoEvent;
            public void MiaoEvent()
            {
                this.Miao();
                this.miaoEvent.Invoke();
            }
        }
        public class Dog
        {
            public void Wang()
            {
                Console.WriteLine("狗叫了");
            }
        }
        public class Mouse
        {
            public void Run()
            {
                Console.WriteLine("老鼠跑了");
            }
        }
    }
    

    5.5 补充说明

    链式委托的执行顺序是:按照委托链上的顺醋从当前委托开始依次往后执行,如果有需要可以使用GetInvocationList()方法来获得委托链上所有需要执行的委托,并且按照任何希望的顺序去逐个执行(Invoke

    委托可以是带有返回值的方法,但多余一个带返回值的方法被添加到委托链中时,程序员需要手动地调用委托链上的每个方法,否则委托使用者智能得到委托链上最后一个被执行的方法的返回值

    委托的应用场合通常是任务的执行者把细节工作进行再分配,执行者确切地知道什么工作将要被执行,但却把执行细节委托给其他组件、方法或者程序集

    6. 委托事件

    事件(Event):是委托的实例,在定义委托是加了enevt 关键字

    enevt 关键字,限定权限,只能在事件所在类中调用事件

    6.1 示例一:自定义标准事件

    模拟:用户订阅手机降价事件,当降价时用于购买手机

    using System;
    
    namespace de3
    {
        class Program
        {
            static void Main(string[] args)
            {
                Phone phone = new Phone
                {
                    name = "vivo",
                    Price = 1999
                };
                phone.DiscountEventHandler += new User() { name = "李白" }.Buy;
    
                phone.Price -= 400;
            }
        }
    
        // 事件额外信息
        public class EventPara
        {
            public int oValue { get; set; }
            public int nValue { get; set; }
        }
    
        public delegate void CostomEventHandler(object sender, EventPara para);
    
        // 手机,发布者,发布事件并且在满足条件情况下执行事件
        public class Phone
        {
            public string name { get; set; }
            private int price;
            public int Price
            {
                set
                {
                    if (value < this.price)
                    {
                        this.DiscountEventHandler?.Invoke(this, new EventPara
                        {
                            oValue = this.price,
                            nValue = value
                        });
                    }
                    this.price = value;
                }
                get { return this.price; }
            }
    
            public event CostomEventHandler DiscountEventHandler;
        }
    
        // 订户,关注事件,事件发生后执行动作
        public class User
        {
            public string name { get; set; }
            // 买手机
            public void Buy(object sender, EventPara para)
            {
                Phone phone = (Phone)sender;
                Console.WriteLine($"手机:{phone.name}	打折前:{para.oValue}	打折后:{para.nValue}");
                Console.WriteLine("购买手机!");
            }
        }
    }
    

    标注:委托事件实际应用还不太熟,示例做参考即可

    7. 扩展补充

    7.1 委托的内部结构

    IL语言的无参无返回值的委托结构(编译后)

    .class nested public auto ansi sealed NoReturnNoPare
        extends [mscorlib]System.MulticastDelegate
    {
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor (
                object 'object',
                native int 'method'
            ) runtime managed 
        {
        } // end of method NoReturnNoPare::.ctor
    
        .method public hidebysig newslot virtual 
            instance void Invoke () runtime managed 
        {
        } // end of method NoReturnNoPare::Invoke
    
        .method public hidebysig newslot virtual 
            instance class [mscorlib]System.IAsyncResult BeginInvoke (
                class [mscorlib]System.AsyncCallback callback,
                object 'object'
            ) runtime managed 
        {
        } // end of method NoReturnNoPare::BeginInvoke
    
        .method public hidebysig newslot virtual 
            instance void EndInvoke (
                class [mscorlib]System.IAsyncResult result
            ) runtime managed 
        {
        } // end of method NoReturnNoPare::EndInvoke
    
    } // end of class NoReturnNoPare
    

    7.2 调用委托

    使用委托实例调用,参数写在括号中

    NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
    d1();
    

    使用实例的Invoke()方法调用,参数写在方法中

    NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
    d1.Invoke();
    

    使用实例的BeginInvoke方法调用,开启一个新的线程执行委托

    using System;
    
    namespace de4
    {
        class Program
        {
            static void Main(string[] args)
            {
                Predicate<int> predicate = (i) => { return true; };
                predicate.BeginInvoke(1,null,null);
            }
        }
    }
    
    

    7.3 线程委托

    使用实例的BeginInvoke方法调用,开启一个新的线程执行委托

    IAsyncResult BeginInvoke([T value], AsyncCallback callback, object @object)
    

    使用方式一:无回调,无回调参数

    using System;
    using System.Threading;
    
    namespace th2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Action<string> action = (val) => Console.WriteLine(val);
                action.BeginInvoke("有参数无返回值委托", null, null);
    
                Func<string> func = () => 
                { 
                    Console.WriteLine("无参数有返回值委托"); 
                    return ""; 
                };
                func.BeginInvoke(null, null);
    
                Thread.Sleep(2000);
            }
        }
    }
    

    使用方式二:有回调,无回调参数

    using System;
    using System.Threading;
    
    namespace th2
    {
        class Program
        {
            static void Main(string[] args)
            {
                AsyncCallback callback = re =>
                {
                    Console.WriteLine("回调方法");
                };
                Action<string> action = (val) => Console.WriteLine(val);
                IAsyncResult result1 = action.BeginInvoke("有参数无返回值委托", callback, null);
    
                Func<string> func = () => { Console.WriteLine("无参数有返回值委托"); return "libai"; };
                IAsyncResult result2 = func.BeginInvoke(callback, null);
    
                Thread.Sleep(2000);
            }
        }
    }
    

    使用方式三:有回调,有自定义回调参数

    using System;
    using System.Threading;
    
    namespace th2
    {
        class Program
        {
            static void Main(string[] args)
            {
                string obj = "ck";
                AsyncCallback callback = re =>
                {
                    Console.WriteLine("回调方法");
                    Console.WriteLine("回调参数:" + re.AsyncState);
                };
                Action<string> action = (val) => Console.WriteLine(val);
                IAsyncResult result1 = action.BeginInvoke("有参数无返回值委托", callback, obj);
    
                Func<string> func = () => { Console.WriteLine("无参数有返回值委托"); return "libai"; };
                IAsyncResult result2 = func.BeginInvoke(callback, obj);
    
                Thread.Sleep(2000);
            }
        }
    }
    

    有返回值委托,使用EndInvoke方法获取委托返回值

    using System;
    using System.Threading;
    
    namespace th2
    {
        class User
        {
            public int id { get; set; }
        }
        class Program
        {
            static void Main(string[] args)
            {
                var obj = new User { id = 1 };
                AsyncCallback callback = re =>
                {
                    Console.WriteLine("回调方法");
                    Console.WriteLine("回调参数:" + re.AsyncState);
                };
                Action<string> action = (val) => Console.WriteLine(val);
                IAsyncResult result1 = action.BeginInvoke("有参数无返回值委托", callback, obj);
                // 没有返回值的无法使用EndInvoke方法
                // Console.WriteLine(action.EndInvoke(result1));
    
                Func<string> func = () => { Console.WriteLine("无参数有返回值委托"); return "libai"; };
                IAsyncResult result2 = func.BeginInvoke(callback, obj);
    
                var value = func.EndInvoke(result2);
                Console.WriteLine(value);
    
                Thread.Sleep(2000);
            }
        }
    }
    

    7.4 Predicate<T>委托

    说明:不常用,仅作为了解(看个人情况)

    Predicate是返回bool型的泛型委托,至少1个参数,至多1个参数,返回值固定为bool

    官方示例

    using System;
    using System.Drawing;
    
    public class Example
    {
       public static void Main()
       {
          Point[] points = { new Point(100, 200),
                             new Point(150, 250), new Point(250, 375),
                             new Point(275, 395), new Point(295, 450) };
    
          Predicate<Point> predicate = FindPoints;
          Point first = Array.Find(points, predicate);
           
          Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
       }
    
       private static bool FindPoints(Point obj)
       {
          return obj.X * obj.Y > 100000;
       }
    }
    
    到达胜利之前无法回头!
  • 相关阅读:
    invalid expression: missing ) after argument list in xxx 或者 console.error(("[Vue warn]: " + msg + trace));
    js的alert()
    第9节列表渲染
    第8节条件渲染
    第7节class与style绑定
    CF1215D Ticket Game 博弈论
    CF833A The Meaningless Game 思维
    蚯蚓 队列
    洛谷P2566[SCOI2009]围豆豆
    ants 思维
  • 原文地址:https://www.cnblogs.com/weiyongguang/p/15115633.html
Copyright © 2011-2022 走看看