• .NET知识梳理——5.委托


    1. 委托

    委托就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法。在IL中委托就是一个类。继承自System.MulticastDelegate 特殊类,不能被继承。

    1.1        委托的声明、实例化、调用

    1.1.1  声明

    委托用deleate关键字修饰,只有方法名。

       public delegate string XFInfoDelegate(string name, int age);

    1.1.2  实例化、调用

      XFInfoDelegate XFInfo = new XFInfoDelegate(GetXFInfo);

                XFInfo.Invoke("Olive", 116);

                XFInfo(“Olive”,116);//这两种方法都可以调用

    实例化的限制就是方法的参数列表&返回值类型必须和委托约束的一致。

    1.2        泛型委托

    有了泛型委托,就有了一能适用于任何返回类型和任意参数(类型和合理的个数)的通用委托,Func 和 Action(Action Func  .NetFramework3.0出现的)。

    delegate TResult Func <out TResult> ();
    delegate TResult Func <in T, out TResult> (T arg);
    delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2);
    ... 一直到 T16
    delegate void Action ();
    delegate void Action <in T> (T arg);
    delegate void Action <in T1, in T2> (T1 arg1, T2 arg2);
    ... 一直到 T16

    1.2.1  Action

    只有输入参数、无返回值。

    Action<string, int> SayInfo = (name, age) => Console.WriteLine($"Action Lambda方法:{name} 的年龄是:{age}");//是一种语法糖。

    可以直接SayInfo(“XF”,30)调用,

    也可以SayInfo.BeginInvoke调用,异步,会在线程池中启动一个线程。

    IAsyncResult actionResult = SayInfo.BeginInvoke("XF", 30, (r) => {

                if(r==null||r.AsyncState==null)

                    {

                        Console.WriteLine("回调异常");

                    }           

                    Console.WriteLine($"Action 回调成功,结果为:{r.AsyncState}");//可以拿到传入的第三个参数

                }, "Action 密码:Olive");

    1.2.2  Func

    必须有返回值,最有一个参数类型为返回值类型。

    Func<string, int, string> PeopleInfo = delegate (string name, int age)

    {

       ThreadInfo("Func 委托 线程情况打印");

       return $"Func采用匿名方法:{name} 的年龄为:{age}";

    };

    可以直接PeopleInfo (“XF”,30)调用,

    也可以PeopleInfo.BeginInvoke调用,异步,会在线程池中启动一个线程。

    PeopleInfo.BeginInvoke("XF", 30, (r) => {

    Console.WriteLine($"Func 回调成功,结果为:{ PeopleInfo.EndInvoke(r)}");

                }, “Func 密码:Olive”);

    1.3        委托的意义

    1.3.1  解耦

    1.3.2  异步多线程

    委托可以通过BeginInvoke的方法实现异步多线程。

    1.3.3  多播委托

    任何一个委托都是多播委托类型的子类,可以通过+=/-=去增加/移除方法,会形成方法链,Invoke时,会按顺序执行系列方法一个委托实例包含多个方法。

    1.3.3.1      += 

    给委托的实例添加方法,会形成方法链,Invoke时,会按顺序执行系列方法

    1.3.3.2      -= 

    给委托的实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除,且只移除一个,如果没有匹配,则无任何影响。(不同的实例的相同方法不能移除)

    多播委托实例不能异步,如果需要异步,可以对委托实例单个方法进行异步调用。

    一般情况下多播委托用的是不带返回值的,如果带返回值,则返回最后一个方法的返回值。

    1.3.3.3      猫叫示例

    1.3.3.3.1              Cat类

    猫叫,引发一些列行为

    public class Cat

        {

            /// <summary>

            /// 猫叫,并引发一系列的后续行为

            /// 类内调用其它类实例方法,依赖多个类型,任何类型变化都需调整

            /// </summary>

            public void Miao()

            {

                Console.WriteLine($"{this.GetType().Name} Miao");

                new Mouse().Run();

                new Dog().Wang();

                new Baby().Cry();

                Console.WriteLine("该睡觉了");

            }

    }

    1.3.3.3.2              IObject

    所有的观察者都需要继承的一个接口

    /// <summary>

        /// 为所有的观察者定义一个接口

        /// </summary>

        public interface IObject

        {

            void DoAction();

        }

    1.3.3.3.3              Mouse类

    /// <summary>

        /// 老鼠类,实现了IObject接口,方便后边作为观察者对象被添加

        /// </summary>

        public class Mouse:IObject

        {

            public void DoAction()

            {

                Run();

            }

            public void Run()

            {

                Console.WriteLine($"{this.GetType().Name} Run");

            }

    }

    1.3.3.3.4              Dog类

    /// <summary>

        /// 狗类,实现了IObject接口,方便后边作为观察者对象被添加

        /// </summary>

        public class Dog:IObject

        {

            public void DoAction()

            {

                Wang();

            }

            public void Wang()

            {

                Console.WriteLine($"{this.GetType().Name} Wang");

            }

        }

    1.3.3.3.5              Baby类

    /// <summary>

        /// 儿童类,实现了IObject接口,方便后边作为观察者对象被添加

        /// </summary>

        public class Baby:IObject

        {

            public void DoAction()

            {

                Cry();

            }

            public void Cry()

            {

                Console.WriteLine($"{this.GetType().Name} Cry");

            }

        }

    1.3.3.3.6              常规调用

    Cat cat = new Cat();

    cat.Miao();

    1.3.3.3.7              委托实现

    现在Cat类中定义一个委托,

      public Action CatMiaoAction;

    同时定义一个触发委托的方法

    public void MiaoDelegate()

            {

                Console.WriteLine($"{this.GetType().Name} MiaoDelegate");

                this.CatMiaoAction?.Invoke();

                Console.WriteLine($"{this.GetType().Name} MiaoDelegate OVER");

            }

    为委托绑定多个方法,

       cat.CatMiaoAction += new Mouse().Run;

       cat.CatMiaoAction += new Dog().Wang;

       cat.CatMiaoAction += new Baby().Cry;

    调用

       cat.CatMiaoAction.Invoke();

    1.4        事件和观察者模式

    1.4.1  事件

    由5.3.3.7委托实现猫叫示例和5.4.1.2事件实现猫叫示例可以看出,事件和委托的实现基本一样,事件只是多了一个event修饰的委托。实际上相当于委托的一个实例。

    事件有一系列规则和约束用以保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其它操作。

    1.4.1.1      标准事件

    创建一个类,在类中声明一个事件

    EventHandler(EventHandle是框架封装的委托,该委托有两个参数,一个是object事件源,一个EventArgs类,该类主要用于传递数据),

      public class School

        {

            public event EventHandler SchoolStateHandler;

            private string state;

            public string State {

                get { return state; }

                set

                {

                    state = value;

                    if (value == "OK")//当State改变时,触发绑定事件,并传入State参数

                        SchoolStateHandler?.Invoke(this, new XFEventArgs() { State = "OK" });

                    else  if (value == "NO")

                            SchoolStateHandler?.Invoke(this, new XFEventArgs() { State = "NO" });

     

                } }

        }

    同时创建一个继承于EventArgs的类,用来传递参数

    public class XFEventArgs : EventArgs

        {

            public string State { get; set; }//用来传递State参数

        }

    创建一些订阅事件的类,其中要有参数为(object obj,EventArgs e)的方法

    public class  Teacher

        {//事件订阅类

            public void Teach(object obj,EventArgs e)

            {

     

                if (((XFEventArgs)e).State.Equals("OK"))

                    Console.WriteLine("开始教学");

                else if(((XFEventArgs)e).State.Equals("NO"))

                    Console.WriteLine("停止教学");

            }

        }

        public class Student

        {

            public void Study(object obj, EventArgs e)

            {

                if (((XFEventArgs)e).State.Equals("OK"))

                    Console.WriteLine("开始学习");

                else if (((XFEventArgs)e).State.Equals("NO"))

                    Console.WriteLine("回家休息");

            }

        }

    创建类的实例,并订阅一些方法。

      School school = new School();

                school.SchoolStateHandler += new Teacher().Teach;

                school.SchoolStateHandler += new Student().Study;

    当类中某些属性发生变化时触发事件。

    school.State = "OK";

    school.State = "NO";

     结果如下:

    1.4.1.2      事件实现5.3.3.3中猫叫示例

    1.4.1.2.1              在Cat类中声明一个事件

    public event Action CatMiaoCationHandler;(委托是一个类,事件相当于是类的一个实例)

    1.4.1.2.2              定义一个执行事件的方法

    public void MiaoEvent()

            {

                Console.WriteLine($"{this.GetType().Name} MiaoEvent");

                this.CatMiaoCationHandler?.Invoke();//执行绑定方法

                Console.WriteLine($"{this.GetType().Name} MiaoEvent OVER");

            }

    1.4.1.2.3              为事件绑定一些列方法

       cat.CatMiaoCationHandler += new Mouse().Run;

                cat.CatMiaoCationHandler += new Dog().Wang;

                cat.CatMiaoCationHandler += new Baby().Cry;

    1.4.1.2.4              调用

       cat.MiaoEvent();

    1.4.2  观察者模式

    用观察者模式实现5.3.3.3中猫叫示例。

    在Cat类中增加观察者集合和添加观察者方法,

    通过调用观察者方法,逐一通知观察者。

      /// <summary>

            /// 添加一个观察者集合,用来存储观察者

            /// </summary>

            private List<IObject> list = new List<IObject>();

            /// <summary>

            /// 添加观察者

            /// </summary>

            /// <param name="observer"></param>

            public void AddObserver(IObject observer)

            {

                list.Add(observer);

            }

            /// <summary>

            /// 猫叫通知观察者

            /// </summary>

     

            public void MiaoObserver()

            {

                foreach(var item in list)

                {

                    item.DoAction();

                }

                Console.WriteLine($"{this.GetType().Name} Miao Over");

            }

    调用:

    cat.AddObserver(new Mouse());

                cat.AddObserver(new Dog());

                cat.AddObserver(new Baby());

                cat.MiaoObserver();

  • 相关阅读:
    servlet-servletConfig
    servlet-servletContext网站计数器
    servlet-cookie
    Android 无cp命令 mv引起cross-device link
    android使用mount挂载/system/app为读写权限,删除或替换系统应用
    android使用百度地图、定位SDK实现地图和定位功能!(最新、可用+吐槽)
    解决android sdk manager无法下载SDK 的问题
    Android APK反编译详解(附图)
    Android如何防止apk程序被反编译
    不用外部JAR包,自己实现JSP文件上传!
  • 原文地址:https://www.cnblogs.com/Olive116/p/12355712.html
Copyright © 2020-2023  润新知