天天看點

【學習筆記】委托、匿名方法、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      

繼續閱讀