天天看点

【学习笔记】委托、匿名方法、Lambda表达式和事件

【学习笔记】委托、匿名方法、Lambda表达式和事件

一、委托

1、概念

  委托是一个特殊的类,其保存的是一个或者多个方法,这些方法具有相同的签名和返回类型。

2、委托使用

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = new Test();

            //var myDele = new MyDelegate(t.DoInstance); //声明委托方式1
            MyDelegate myDele = Test.DoStatic; //声明委托方式2
            myDele += test.DoInstance; //添加方法
            myDele += Test.DoStatic;
            myDele -= Test.DoStatic; //移除方法
            //调用委托,委托调用与方法调用一致,需要传递实参,当委托调用时,其所关联的方法都将被顺序调用
            myDele("Hello World");

            Console.ReadKey();
        }
    }

    /// <summary>
    /// 声明委托
    /// </summary>
    /// <param name="msg"></param>
    public delegate void MyDelegate(string msg);

    /// <summary>
    /// 测试类
    /// </summary>
    public class Test
    {
        //静态方法
        public static void DoStatic(string msg)
        {
            Console.WriteLine($"DoStatic:{msg}");
        }

        //实例方法
        public void DoInstance(string msg)
        {
            Console.WriteLine($"DoInstance:{msg}");
        }
    }
}      

3、使用委托的目的

  逻辑解耦,方便维护升级。代码重用,去掉重复代码。

using System;

namespace MyDelegateEvent
{
    class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student()
            {
                Id = 123,
                Name = "Rabbit",
                Age = 23,
                ClassId = 1
            };

            {
                SayHiDelegate method = new SayHiDelegate(student.SayHiChinese);
                student.SayHiPerfact("大脸猫", method);
            }
            {
                SayHiDelegate method = new SayHiDelegate(student.SayHiAmerican);
                student.SayHiPerfact("PHS", method);
            }
            {
                SayHiDelegate method = new SayHiDelegate(student.SayHiMalaysia);
                student.SayHiPerfact("icefoxz", method);
            }

            Console.ReadKey();
        }
    }

    /// <summary>
    /// People种类枚举
    /// </summary>
    public enum PeopleType
    {
        Chinese = 1,
        American = 2,
        Malaysia = 3
    }

    /// <summary>
    /// 打招呼委托
    /// </summary>
    /// <param name="name">姓名</param>
    public delegate void SayHiDelegate(string name);

    /// <summary>
    /// 学习类
    /// </summary>
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int ClassId { get; set; }
        public int Age { get; set; }

        /// <summary>
        /// 缺点:
        ///     任意分支变化都得修改方法
        ///     经常改动,很不稳定,难以维护
        /// 优点:
        ///     增加公共逻辑方便
        /// 
        /// 特点:
        ///     传递个变量 => 判断一下 => 执行对应的逻辑
        ///     
        /// 思考:
        ///     能不能直接传递个逻辑,逻辑就是方法 => 委托
        /// </summary>
        public void SayHi(string name, PeopleType peopleType)
        {
            Console.WriteLine("prepare SayHi..");
            switch (peopleType)
            {
                case PeopleType.Chinese:
                    Console.WriteLine($"{name},晚上好~");
                    break;
                case PeopleType.American:
                    Console.WriteLine($"{name},good evening~");
                    break;

                case PeopleType.Malaysia:
                    Console.WriteLine($"{name},*^%*^%^&%^%~");
                    break;

                default://遇到不认识的枚举,说明调用有问题,那就不要隐藏,直接异常
                    throw new Exception("wrong peopleType");
            }
        }

        /// <summary>
        /// 既要增加公共逻辑方便,又想使逻辑分离、维护简单,鱼和熊掌怎么兼得?
        /// 
        /// 自上而下 => 逻辑解耦,方便维护升级
        /// 自下而上 => 代码重用,去掉重复代码
        /// </summary>
        public void SayHiPerfact(string name, SayHiDelegate method)
        {
            Console.WriteLine("prepare SayHi..");

            method.Invoke(name);
        }

        /// <summary>
        /// 优点:
        ///     修改某个方法不影响别的方法
        /// 缺点:
        ///     增加公共逻辑,多个方法有很多重复代码
        /// </summary>
        public void SayHiChinese(string name)
        {
            //Console.WriteLine("prepare SayHi..");

            Console.WriteLine($"{name},晚上好~");
        }

        public void SayHiAmerican(string name)
        {
            //Console.WriteLine("prepare SayHi..");

            Console.WriteLine($"{name},good evening~");
        }

        public void SayHiMalaysia(string name)
        {
            //Console.WriteLine("prepare SayHi..");

            Console.WriteLine($"{name},*^%*^%^&%^%~~");
        }
    }
}      

二、匿名方法

  方法只会被使用一次用来初始化委托,无需创建具名的方法,系统自动创建当前类的私有静态方法。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //匿名方法用来初始化委托
            MyDelegate myDele = delegate (string msg)
            {
                return $"Delegate:{msg}";
            };
            var retMsg = myDele("Hello World");

            Console.WriteLine(retMsg);
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 声明委托
    /// </summary>
    public delegate string MyDelegate(string msg);
}      

三、Lambda表达式

  用来简化匿名方法,简化过程如下:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //匿名方法用来初始化委托
            MyDelegate myDele = delegate (string msg)
            {
                return $"Delegate:{msg}";
            };
            //1、去掉delegate,在参数括号后面添加“=>”
            myDele = (string msg) => { return $"Delegate:{msg}"; };
            //2、参数类型可以去掉
            myDele = (msg) => { return $"Delegate:{msg}"; };
            //3、如果参数个数只有1个,可以去掉“()”
            myDele = msg => { return $"Delegate:{msg}"; };
            //4、如果方法体内部只有一个return语句,去掉“return”和“{}”
            myDele = msg => $"Delegate:{msg}";

            var retMsg = myDele("Hello World");
            Console.WriteLine(retMsg);
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 声明委托
    /// </summary>
    public delegate string MyDelegate(string msg);
}      

四、事件

  事件是类的成员之一,它封装了委托类型的变量,在类的外部只允许使用“+=”和“-=”操作符进行事件的注册和注销。声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = new Test();
            var eventTest = new EventTest();
            eventTest.MyEvent += test.DoInstance;
            eventTest.MyEvent += Test.DoStatic;
            eventTest.MyEvent -= test.DoInstance;
            eventTest.OnEventHangler("Hello World"); //在合适的地方调用

            Console.ReadKey();
        }
    }

    /// <summary>
    /// 声明委托
    /// </summary>
    /// <param name="msg"></param>
    public delegate void MyDelegate(string msg);

    /// <summary>
    /// 测试类
    /// </summary>
    public class Test
    {
        //静态方法
        public static void DoStatic(string msg)
        {
            Console.WriteLine($"DoStatic:{msg}");
        }

        //实例方法
        public void DoInstance(string msg)
        {
            Console.WriteLine($"DoInstance:{msg}");
        }
    }

    /// <summary>
    /// 事件测试类
    /// </summary>
    public class EventTest
    {
        /// <summary>
        /// 声明事件
        /// </summary>
        public event MyDelegate MyEvent;

        /// <summary>
        /// 定义激活事件的方法(在类的外部激活)
        /// </summary>
        public virtual void OnEventHangler(string msg)
        {
            //事件只能在声明事件所在类的内部进行调用
            MyEvent?.Invoke(msg);
        }
    }
}      

事件的标准写法,下面来看个示例:

using System;

namespace MyDelegateEvent
{
    /// <summary>
    /// 标准事件写法
    /// </summary>
    public class EventStandard
    {
        /// <summary>
        /// 订阅:把订户和发布者的事件关联起来
        /// </summary>
        public static void Show()
        {
            Lesson lesson = new Lesson()
            {
                Id = 123,
                Name = "零基础到多项目实战班",
                Price = 2699
            };
            //订阅:把订户和发布者的事件关联起来
            lesson.IncreaseHandler += new Student().Buy;
            lesson.IncreaseHandler += new Tencent().Popularize;
            lesson.Price = 3999;
        }

        /// <summary>
        /// 订户:关注事件,事件发生后,自己做出对应的动作
        /// </summary>
        public class Student
        {
            public void Buy(object sender, EventArgs e)
            {
                Lesson lesson = (Lesson)sender;
                Console.WriteLine($"This is {lesson.Name} Lesson");

                XEventArgs args = (XEventArgs)e;
                Console.WriteLine($"之前价格{args.OldPrice}");
                Console.WriteLine($"现在价格{args.NewPrice}");
                Console.WriteLine("果断买了!!!");
            }
        }

        /// <summary>
        /// 订户
        /// </summary>
        public class Tencent
        {
            public void Popularize(object sender, EventArgs e)
            {
                Lesson lesson = (Lesson)sender;
                Console.WriteLine($"This is {lesson.Name} Lesson");

                XEventArgs args = (XEventArgs)e;
                Console.WriteLine($"之前价格{args.OldPrice}");
                Console.WriteLine($"现在价格{args.NewPrice}");
                Console.WriteLine("广大用户请留意!!!");
            }
        }

        /// <summary>
        /// 事件参数:一般会为特定的事件去封装个参数类型
        /// </summary>
        public class XEventArgs : EventArgs
        {
            public int OldPrice { get; set; }
            public int NewPrice { get; set; }
        }

        /// <summary>
        /// 事件的发布者:发布事件并且在满足条件的时候,触发事件
        /// </summary>
        public class Lesson
        {
            public int Id { get; set; }
            public string Name { get; set; }

            private int _price;

            public int Price
            {
                get
                {
                    return this._price;
                }
                set
                {
                    if (value > this._price)
                    {
                        this.IncreaseHandler?.Invoke(this,
                            new XEventArgs()
                            {
                                OldPrice = this._price,
                                NewPrice = value
                            });
                        this._price = value;
                    }
                }
            }

            /// <summary>
            /// 打折事件
            /// </summary>
            public event EventHandler IncreaseHandler;
        }
    }
}      
/// <summary>
/// 为啥要用事件?事件究竟能干什么?
///     事件(观察者模式)能把固定动作和可变动作分开,完成固定动作,把可变动作分离出去,由外部控制
///     搭建框架时,恰好就需要这个特点,可以通过事件去分离可变动作,支持扩展
///     event限制权限,事件只能在类的内部发生,避免外部乱来
/// </summary>
class Program
{
    static void Main(string[] args)
    {
        //委托
        {
            Student student = new Student()
            {
                Id = 123,
                Name = "Rabbit",
                Age = 23,
                ClassId = 1
            };

            {
                SayHiDelegate method = new SayHiDelegate(student.SayHiChinese);
                student.SayHiPerfact("大脸猫", method);
            }
            {
                SayHiDelegate method = new SayHiDelegate(student.SayHiAmerican);
                student.SayHiPerfact("PHS", method);
            }
            {
                SayHiDelegate method = new SayHiDelegate(student.SayHiMalaysia);
                student.SayHiPerfact("icefoxz", method);
            }
        }

        Console.WriteLine("******************分割线******************");

        //事件
        {
            EventStandard.Show();
        }

        Console.ReadKey();
    }
}      
【学习笔记】委托、匿名方法、Lambda表达式和事件

五、常用的系统内置的泛型委托

1)、Action泛型委托:0-16个传入参数,无返回值。

public delegate void Action();

public delegate void Action<in T>(T obj);

public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);

public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);

......      

2)、Func泛型委托:0-16个传入参数,一个返回值。

public delegate TResult Func<out TResult>();

public delegate TResult Func<in T, out TResult>(T arg);

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);

public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);

......      

3)、Predicate泛型委托:1个传入参数,返回bool类型。

public delegate bool Predicate<in T>(T obj);      

4)、Comparison泛型委托:1个类型传入参数,接受两个同类型参数用于比较,返回int类型。

public delegate int Comparison<in T>(T x, T y);      

Demo源码:

链接:https://pan.baidu.com/s/1XnBK8OMh_2DjF8j5qe8dOg 
提取码:rdt4      

继续阅读