前言:這篇簡單介紹下委託的使用。當然啦,園子裡面很多介紹委託的文章都會說道:委託和事件的概念就像一道坎,過了這個檻的人,覺得真是太容易了,而沒有過去的人每次見到委託和事件就覺得心裡發慌。確實這東西就像最開始學C語言的指標一樣,令人有一種很糾結的感覺,總覺得要呼叫一個方法直接呼叫就行了,為啥非要定義一個委託時執行這個方法呢。其實在C#裡面很多的技術都是為了重用和簡化程式碼而生,委託也不例外,很多使用C#多型去實現的設計模式其實都可以使用委託的方式去改寫,可以理解為一種輕量級的設計模式吧。博主打算抽一篇專門分享下多型和委託實現設計模式的異同。這篇就先介紹簡單委託的使用。
一、什麼是委託:C# 中的委託(Delegate)類似於 C 或 C++ 中函式的指標。用博主的話說,委託就是一種允許將方法名稱作為引數傳遞的引用型別。它定義的是方法的型別,可以說就是方法的抽象,那麼反過來說,方法可以理解為委託的例項,如public delegate void TestDelegate(string str);這種委託定義的就是所有引數型別為string,沒有返回值的方法的一種抽象。
二、為什麼要使用委託:記得博主剛開始做專案的時候看到委託的寫法就頭大,總覺得這是沒事找事,唯一的好處貌似就是程式碼看上去很酷~~隨著工作的累積,發現專案中某些小的需求使用這種輕量級的委託來實現的時候確實能減少很多程式碼量。
三、委託的使用:
1、.Net Framework 裡面的委託型別:使用過委託的朋友可能注意到了C#裡面定義了兩種型別的委託變數,基本能滿足我們的一般需求。
(1)Action型別的委託:C#裡面定義Action委託用於抽象化那種沒有返回值的方法。將Action變數轉到定義可知它的最簡單形式:
1 2 3 4 |
// 摘要: //封裝一個方法,該方法不具有引數並且不返回值。 [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")] public delegate void Action(); |
它定義是就是一種沒有返回值沒有引數的委託。同時Action還提供了16個泛型的委託,用於定義方法的傳入引數:
我們來看他們的使用方法,我們首先定義測試的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private static void Test5(int a, int b, int c) { //...... } //無引數無返回值 private static void Test1() { Console.WriteLine("Func Test1, No Parameter"); } //有引數無返回值 private static void Test2(string str) { Console.WriteLine("Func Test2, Parameter is" + str); } //無引數有返回值 private static object Test3() { Console.WriteLine("Func Test3, Parameter"); return Guid.NewGuid().ToString(); } //有引數有返回值 private static object Test4(string strRes) { Console.WriteLine("Func Test4, Parameter and Return Value"); return strRes; } |
呼叫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
static void Main(string[] args) { //1.無參無返回值方法 var oAction1 = new Action(Test1); oAction1.Invoke();//呼叫方式一 oAction1();//呼叫方式二 //2.有參無返回值 var oAction2 = new Action<int, int, int>(Test5); oAction2.Invoke(1, 2, 3); oAction2(1, 2, 3); //匿名方法的呼叫 var oAction3 = new Action<int, int, int>((a,b,c) => { //...... }); oAction3.Invoke(1, 2, 3); } |
(2)Func型別的委託:還記得Linq裡面的擴充套件方法Where()、Select()等方法的引數嗎。public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)。這裡的引數就是一個Func型別的委託。C#裡面Func型別的委託用於處理有引數有返回值的方法。不多說,上程式碼:
1 2 3 4 5 6 7 |
static void Main(string[] args) { var oFunc1 = new Func<object>(Test3); var ofuncRes1 = oFunc1.Invoke(); var oFunc2 = new Func<string, object>(Test4); oFunc2("a"); } |
知道了Func的方法就可以推想到我們神奇的lamada表示式了,其實lamada表示式就是一個匿名的委託。
1 2 |
var lstTest = new List<string>(); var lstRes = lstTest.Where(x => x.Contains("_")); |
這個Where裡面的lamada表示式我們把他拆解:
1 2 3 4 |
private static bool TestWhere(string x) { return x.Contains("_"); } |
1 2 |
var oFunc = new Func<string, bool>(TestWhere); lstRes = lstTest.Where(oFunc); |
是不是一樣一樣的~~
2、自定義委託:
1 |
public delegate void TestDelegate(string str); |
其實很多人應該都自己寫過Action、Func型別的委託。其實自己定義一個泛型委託也很簡單:
1 2 |
public delegate void MyAction<in T>(); public delegate TResult MyFunc<in T, out TResult>(T arg); |
其實使用起來和系統的Action和Func基本沒有區別。
3、委託的合併和拆解就放在事件裡面分享了。這篇且過之。。。
4、如果按照上面的方法去使用委託,那真的是要彆扭死人了,因為呼叫方法直接用方法名呼叫就好了,何必還要定義一個委託變數去呼叫,這不是將簡單問題複雜化麼。確實,上面只是為了介紹委託而寫的程式碼,實際專案中肯定不會這麼用。其實委託在專案中一般用在將委託變數作為引數傳遞或者函式回撥。來看下面程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
class Program { static void Main(string[] args) { Person strHelper = new Person(); string r1 = strHelper.ProcessFunc("中國人", "你好", new MyDelegate(strHelper.ChineseSayHello)); string r2 = strHelper.ProcessFunc("English", "Hello", new MyDelegate(strHelper.EnglishSayHello)); string r3 = strHelper.ProcessFunc("Japanese", "こんにちは", new MyDelegate(strHelper.JapaneseSayHello)); Console.WriteLine(r1); Console.WriteLine(r2); Console.WriteLine(r3); Console.ReadKey(); } } public delegate string MyDelegate(string s1, string s2); public class Person { public string ProcessFunc(string s1, string s2, MyDelegate process) { return process(s1, s2); } public string ChineseSayHello(string s1, string s2) { return s1 +","+ s2; } public string EnglishSayHello(string s1, string s2) { return s1 + "," + s2; } public string JapaneseSayHello(string s1, string s2) { return s1 +","+ s2; } } |
得到結果:
public string ProcessFunc(string s1, string s2, MyDelegate process)裡面定義了一個回撥函式,可以將任意一個符合這個委託的方法傳遞進去,得到想對應的結果。細看這種設計是不是和工廠設計模式十分相似,我簡單構造了個工廠:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public class Person { public virtual string SayHello(string s2) { return s2; } } public class Chinese : Person { public override string SayHello(string s2) { return "中國人," + s2; } } public class English : Person { public override string SayHello(string s2) { return "English," + s2; } } public class Japanese : Person { public override string SayHello(string s2) { return "Japanese," + s2; } } //Main函式裡面呼叫 class Program { static void Main(string[] args) { var r1 = GetPerson("你好").SayHello("你好"); var r2 = GetPerson("Hello").SayHello("Hello"); var r3 = GetPerson("こんにちは").SayHello("こんにちは"); Console.WriteLine(r1); Console.WriteLine(r2); Console.WriteLine(r3); Console.ReadKey(); } public static Person GetPerson(string strType) { if (strType == "你好") return new Chinese(); else if (strType == "Hello") return new English(); else return new Japanese(); } } |
得到結果和上面相同:
這樣一比較是不是對委託的用法有點感覺了呢~~如果你不怕麻煩,可以在專案是用起來試試,相信你會有收穫~~