国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

委托和事件 (1)

2019-11-17 02:35:44
字體:
來源:轉載
供稿:網友

委托和事件 (1) - 委托簡析

個人認為,c#最重要的精髓在于其委托。

說實話現在已經是c#5.0的時代,c#6很快也要出來了,委托作為一個c#1就有的性質,已經早就被更高級的工具例如泛型委托,lambda表達式包裝起來了,基本上已經很少有人會在程序中聲明一個delegate。不過,了解一下基礎也是很好的,

基本概念

委托是一個特殊的類(密封類),可以被視為函數指針,其代表一類和委托簽名的輸入輸出變量類型和個數相同的方法。委托本身可以作為變量傳入方法。

借用經典的greetPeople例子,在實際工作中,總會遇到類似的情況,即通過switch來對不同的輸入執行不同的結果。但我們看到,其實每個switch執行的方法都很類似,方法的簽名還完全相同。此時我們很容易想到的就是當再加入一個新的switch case的時候,我們除了要加一個新方法之外,還要對現成的GreetPeople方法進行修改,這違反了開閉原則(對修改關閉)。有沒有一種方法,可以在不修改GreetPeople方法的前提下對程序進行擴展呢?

public class PRogram    {        public static void Main()        {            GreetPeople("Alex", "Chinese");            GreetPeople("Beta", "English");            GreetPeople("Clara", "France");            Console.ReadKey();        }        public static void GreetPeople(string name, string lang)        {            switch (lang)            {                case "English":                    EnglishGreeting(name);                    break;                case "Chinese":                    ChineseGreeting(name);                    break;                case "France":                    FrenchGreeting(name);                    break;            }        }        public static void EnglishGreeting(string name)        {            Console.WriteLine("Morning, " + name);        }        public static void ChineseGreeting(string name)        {            Console.WriteLine("早上好, " + name);        }        public static void FrenchGreeting(string name)        {            Console.WriteLine("Bonjour, " + name);        }    }

首先,我們要放棄使用switch,否則我們終究避免不了修改GreetPeople方法的命運。之后,我們自然而然的會想,假設我們在主函數里面傳入的第二變量不是字符串,而是方法名,那么似乎我們就不需要那個switch了。因為我們會直接去到對應的方法,不用switch再分派過去。那么這件事該怎么實現呢?傳入方法名到底意味著什么呢?這些方法的簽名全都一樣,我是否可以用某種手法將他們封裝起來呢

于是,委托就出現了,它可以解決上面我們所有的問題。委托代表了一類具有相同簽名的方法,可以變身為其中任何一個。委托也可以作為變量傳入方法,其行為和其他類型例如int,string完全一樣。很多人覺得委托很不好理解,是因為委托代表的是方法,而普通類型代表的都是值或者對象。比如string,其可以代表任何的字符串,int也是可以代表在某個取值范圍中任何的整數一樣。委托則代表著某一類方法(視其定義而定),當某個函數的其中一個變量是委托時,意味著我們將要傳入一個可以被該委托所代表的方法名。委托是方法的指針,可以指向不同的方法,類比一下,如同string可以指向堆上的字符串,int可以指向棧上的整數一樣。

public class Program    {        //現在這個委托代表了一類輸入一個字符串,沒有輸出的方法        public delegate void GreetPeopleDelegate(string name);        public static void Main()        {            //利用委托,傳入不同的方法會得到不同的結果            GreetPeople("Alex", ChineseGreeting);            GreetPeople("Beta", EnglishGreeting);            GreetPeople("Clara", FrenchGreeting);            Console.ReadKey();        }        //委托可以作為方法的變量,從而代替switch        public static void GreetPeople(string name, GreetPeopleDelegate aGreetPeopleDelegate)        {            aGreetPeopleDelegate(name);        }        public static void EnglishGreeting(string name)        {            Console.WriteLine("Morning, " + name);        }        public static void ChineseGreeting(string name)        {            Console.WriteLine("早上好, " + name);        }        public static void FrenchGreeting(string name)        {            Console.WriteLine("Bonjour, " + name);        }    }

委托的方法和屬性

1. MulticastDelegate(委托自己所在的密封類)

小寫的delegate是你用來聲明委托的關鍵字,當你聲明完之后,編譯器創建一個新的密封類,該類的類型是MulticastDelegate(繼承自System.MultipleDelegate,其再繼承自System.Delegate)這就是大寫的和小寫d的delegate關鍵字的區別。

這個新的密封類定義了三個方法,invoke, begininvoke和endinvoke。invoke是當你調用委托所代表的方法時隱式執行的,例如aGreetPeopleDelegate(name)實際上和aGreetPeopleDelegate.Invoke(name)沒有區別。所以Invoke的方法簽名永遠和委托本身相同,即如果某委托簽名為int a(int x, int y)則它的invoke簽名一定是public int Invoke(int x, int y)。

后兩者則賦予委托異步的能力。這兩個方法放到多線程系列中進行分析。

2.System.MultipleDelegate和委托的調用列表(方法鏈)

System.MultipleDelegate中重要的方法GetInvocationList()獲得當前委托所代表的方法的各種信息。注意這個方法返回的是一個數組,這也就是說,委托可以同時代表多個方法(此時,invoke委托會將該組方法順序一個一個執行),這也叫做委托的多路廣播。通過+=和-=,我們可以為委托增加和減少方法。我們無需深入研究方法鏈是如何實現的,但以下幾個事情需要知道:

1. 可以重復增加相同的方法,此時該方法將執行兩次

2. 可以刪除委托所有的方法,即委托可以暫時不代表方法,此時invoke委托將什么都不發生

3. 即使不小心多刪除了方法一次,也不會出現異常(如增加了一個方法然后誤刪除了兩次),此時委托暫時不代表任何方法

4. +=和-=是操作符的重載,本質是調用System.Delegate中的Combine和Remove方法

System.MultipleDelegate還重載了==和!=,判斷兩個委托是否相等僅僅看它們代表的方法鏈是否相等(即都是指向相同對象上的相同方法)。

3.System.Delegate

System.Delegate中有兩個重要的公共成員target和method。其中method代表方法的信息,而如果Method代表一個靜態成員,則Target為null,否則,target代表方法所在的對象。通過GetInvocationList()我們可以查看當前委托中方法鏈的信息。另外這個類還有Combine和Remove方法,其已經被子類重載故不需要直接調用他們。

public class Program    {        public delegate void GreetPeopleDelegate(string name);        public static void Main()        {            //實例化委托一定要為其指派一個符合要求的方法            GreetPeopleDelegate aGreetPeopleDelegate = new GreetPeopleDelegate(ChineseGreeting);            PrintInvocationList(aGreetPeopleDelegate.GetInvocationList());            //增加一個方法            aGreetPeopleDelegate += EnglishGreeting;            PrintInvocationList(aGreetPeopleDelegate.GetInvocationList());            anotherClass a = new anotherClass();            //增加一個非靜態方法            aGreetPeopleDelegate += a.NonStaticGreeting;            PrintInvocationList(aGreetPeopleDelegate.GetInvocationList());            Console.ReadKey();        }        //觀看當前委托中代表的方法鏈        public static void PrintInvocationList(Delegate[] aList)        {            foreach (var delegateMethod in aList)            {                //Method代表當前維護的方法的詳細信息                //如果Method代表一個靜態成員,則Target為null,否則,target代表方法所在的對象                Console.WriteLine(string.Format("Method name: {0}, value: {1}", delegateMethod.Method, delegateMethod.Target));            }            Console.WriteLine("------------------------------------");        }        public static void EnglishGreeting(string name)        {            Console.WriteLine("Morning, " + name);        }        public static void ChineseGreeting(string name)        {            Console.WriteLine("早上好, " + name);        }        public static void FrenchGreeting(string name)        {            Console.WriteLine("Bonjour, " + name);        }    }    public class anotherClass    {        public void NonStaticGreeting(string name)        {            Console.WriteLine("Bonjour, " + name);        }    }

動態維護委托的調用列表

上面說了委托都是有一個調用列表的,我們可以動態的操作他,為他添加或者刪除成員。如果我們創建一個公共的委托成員列表,則可以很容易的實現多路廣播。下面例子來自精通c#第六版。其中調用列表

public CarEngineHandler methodList;

是公共的,并且外部方法main會創建一個新的實例作為訂閱者,在適當情形下,調用委托然后執行委托列表中的方法。

public class Program    {        public static void Main()        {            //創建了一個新的訂閱者            var c = new Car("Mycar", 0, 100);            //該訂閱者(消費者)訂閱了方法OnCarEvent1            c.methodList += OnCarEvent1;            //取消注釋實現多路廣播,此時將會執行兩個方法            //c.methodList += OnCarEvent2;            for (int i = 0; i < 10; i++)            {                c.Accel(20);            }            Console.ReadKey();        }        public static void OnCarEvent1(string msg)        {            Console.WriteLine("***** message from car *****");            Console.WriteLine("=> " + msg);            Console.WriteLine("****************************");        }        public static void OnCarEvent2(string msg)        {            Console.WriteLine("=> " + msg.ToUpper());        }    }    public class Car    {        public string name { get; set; }        public int currentSpeed { get; set; }        public int MaxSpeed { get; set; }        private bool isDead { get; set; }        public delegate void CarEngineHandler(string message);        public CarEngineHandler methodList;        public Car(string name, int currentSpeed, int MaxSpeed)        {            this.name = name;            this.currentSpeed = currentSpeed;            this.MaxSpeed = MaxSpeed;            this.isDead = false;        }        public void Accel(int delta)        {            //死亡時執行訂閱列表中的方法            if (isDead)            {                if (methodList != null)                    methodList("Sorry, car is broken");            }            else            {                currentSpeed += delta;                if (currentSpeed >= MaxSpeed) isDead = true;                else Console.WriteLine("Current speed: " + currentSpeed);            }        }
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 富裕县| 墨脱县| 丹东市| 璧山县| 舟曲县| 临夏市| 新沂市| 新乡县| 海兴县| 广安市| 大田县| 太仆寺旗| 昌都县| 南阳市| 永吉县| 新丰县| 神池县| 封开县| 麦盖提县| 云南省| 元江| 张掖市| 民勤县| 锦屏县| 梓潼县| 临湘市| 浦城县| 万盛区| 文登市| 东至县| 纳雍县| 临朐县| 潜江市| 开鲁县| 阜南县| 海口市| 泾阳县| 榆林市| 陇川县| 衡山县| 慈溪市|