標籤:class blog code http tar ext
委託的定義與方法的定義類似,只是傳回值類型的前面多了個delegate。如:public delegate void PrientDelegate(string content),其實委託就是一個能夠將方法封裝成一個參考型別,能和普通對象一樣傳遞。
一、委託聲明的注意點
1、和類一樣,委託的聲明可以再類的外部聲明,委託也可以再類的內部聲明。
2、委託的聲明雖然形式上與方法很像,但是它沒有方法主體,而是直接以分號結尾。
3、修飾符可以是new、public、protected、internal、private。
4、delegate關鍵字後是本委託類型可以匹配的方法簽名,尤其需要注意的是,它還包括方法的傳回型別,但並非與方法名完全符合。
二、給委託Binder 方法時,要符合的兩點要求
1、方法的簽名要與委託一樣,方法參數的個數和類型。
2、方法的傳回值類型要與委託一樣。
調用委託就相當於調用委託所綁定的方法, 一個委託可以綁定多個方法。委託是參考型別,因此它就可以到處傳遞,沒有它,C#能夠將方法到處傳遞嗎?
先附上一個基礎的例子
public delegate void Print(string str); //聲明一個委託 class Program { public static void PrintString(string str) { Console.WriteLine(str); } static void Main(string[] args) { //建立一個委派物件,並將方法PrintString賦給該委託 Print p = new Print(Program.PrintString); //Print p = Program.PrintString; //直接用=號也可以讓委託Binder 方法。 p("調用委託實際等於調用了委託所綁定的方法!"); //輸出調用委託實際等於調用了委託所綁定的方法! Console.ReadKey(); } }
三、委託的組合(多個委託之間的關係)
"+"可以將兩個委託組合起來,"-"可以從一個委託組合中刪除其中一個成員。
public delegate void Print(); class Program { public static void PrintString1() { Console.WriteLine("我是第一個方法"); } public static void PrintString2() { Console.WriteLine("我是第二個方法"); } static void Main(string[] args) { //建立一個委派物件並Binder 方法 Print p1 = Program.PrintString1; //委託p1Binder 方法1 Print p2 = Program.PrintString2; //委託p2Binder 方法2 Print p3 = p1 + p2; //將p1委託和p2委託組合成一個新委託p3 p3(); //調用委託p3,相當於調用p3所有已經綁定的方法,此時是方法1與方法2,此時輸出 我是第一個方法 "換行" 我是第二個方法 Print p4 = p3 - p1; //建立一個新的委派物件,其綁定的方法為p3委託組合去除委託p1,實際就只剩下p2 p4(); //調用委託p4,相當於調用所有p4綁定的方法 輸出 我是第二個方法 Console.ReadKey(); } }
四、委託的添加方法與刪除方法(一個委託的事情)
通過"+="符可以像已有委託新添加綁定一個方法,通過"-="可以從委託中刪除一個已綁定的方法。
public delegate void Print(); class Program { public static void PrintString1() { Console.WriteLine("我是第一個方法"); } public static void PrintString2() { Console.WriteLine("我是第二個方法"); } static void Main(string[] args) { Print p = PrintString1; //聲明一個委託並綁定第一個方法 p(); //調用委託p 輸出 我是第一個方法 p += PrintString2; //再為p多綁定一個方法 p(); //調用委託p 輸出我是第一個方法 "換行" 我是第二個方法,看到現在p委託一經是綁定兩個方法的了 p -= PrintString1; //從委託p中刪除一個已經綁定的方法p1; p(); //調用委託p 輸出 我是第二個方法 看到p現在已經是只綁定第二個方法了 Console.ReadKey(); } }
五、委託在回調中的應用
委託在回呼函數中的調用:
public delegate void PrintCompleteCallback(string message); class Program { public static void PrintMessage(string message) { Console.WriteLine(message); } static void Main(string[] args) { PrintCompleteCallback CompleteCallback = Program.PrintMessage; //建立一個委託,並綁定PrintMessage方法 Printer printer = new Printer("我是一個印表機"); printer.Print(CompleteCallback); //調用p對象的Print方法,要求傳入一個委託作為參數,相當於傳入一個方法作為參數 Console.ReadKey(); } } public class Printer { private string _name; public Printer(string name) { _name = name; } public void Print(PrintCompleteCallback callback) //委派物件作為方法參數 { callback(_name + "列印完畢"); //調用委託,相當於調用callback委託所綁定的對象 } }
六、匿名方法
匿名方法最大的優勢在於其減少了系統開銷,方法僅在委託使用時才定義。直接給委託傳遞一段代碼比先建立一個方法再綁定到委託要簡單。
匿名方法的使用規則:
1、匿名方法中不能使用跳躍陳述式跳至此匿名方法的外部,反之亦然;匿名方法外部的跳躍陳述式也不能跳到此匿名方法的內部。
2、在匿名方法內部不能訪問不安全的代碼。另外,也不能訪問在匿名方法外部定義的ref與out參數。但可以使用在匿名方法外部定義的其它變數。
public delegate void Print(); //定義一個委託 public delegate int GetLength(string str); //定義一個帶參數的委託 class Program { static void Main(string[] args) { Print p = delegate //不帶參數的匿名方法 { Console.WriteLine("你在他鄉還好嗎?"); }; p(); GetLength g = delegate(string str) //帶參數的匿名方法,注意方法的參數要與委託一致 { return str.Length; }; int length = g("你還好嗎?"); //調用委託,注意要傳入參數 Console.WriteLine(length); Console.ReadKey(); } }
七、Lambda運算式
Lambda運算式和匿名方法基本相同,只是文法不同而已,可以說,Lambda運算式就是匿名方法。必須要記住,Lambda就是一個匿名方法,是給委託賦值用的,也就是說,使用Lambda的情境只有給委託賦值。
Lambda運算式的文法更加簡潔:
(param)=>expr //其中param是一個輸出參數列表,說白了就是給委託的參數,expr是一個運算式或一系列語句,說白了就是輸出或執行的操作
Lambda運算式具有以下特性:
1、在一個具有唯一的顯式型別參數的Lambda運算式中,圓括弧可以從參數列表中刪除。例如:param=>expr
2、當輸入參數不唯一時,括弧不能省略。
3、輸入參數列表中的各參數可以顯式指定類型,也可以省略掉參數類型,具體類型通過類型推斷機制判斷。
4、expr可以只包含一個計算運算式,也可以包含一系列語句,只是如果是一系列語句必須包括在大括弧內。
一個委託參數的Lambda運算式
public delegate string Print(string content); //定義一個委託,該委託接受一個字串輸入,用於Lambda的左邊 class Program { static void Main(string[] args) { Print p = (str) => str += "-輸出參數"; //(str) 輸入參數,也就是委託參數中的content,箭頭後的是輸出,最簡單的理解就是,箭頭左邊是輸入,箭頭左邊是輸出,當然語句就不同了。 string outstr = p("輸入參數"); Console.WriteLine(outstr); //輸出 輸入參數-輸出參數 Console.ReadKey(); } }
兩個委託參數的Lambda運算式的調用:
public delegate string Print(string content1,string content2); //定義一個委託 class Program { static void Main(string[] args) { Print p = (str1,str2) => str1 += "左邊-右邊" + str2; //多個輸入參數,括弧不能省略 string outstr = p("張飛","關羽"); //輸出 張飛左邊-右邊關羽 Console.WriteLine(outstr); //輸出 輸入參數-輸出參數 Console.ReadKey(); } }
帶語句的Lambda運算式
public delegate void Print(string content); //定義一個委託 class Program { static void Main(string[] args) { Print p = str => //帶語句的Lambda運算式,由於只有一個參數,所以小括弧可以省略 { Console.WriteLine(str); }; p("你好"); //你好 Console.ReadKey(); } }
2014-04-08-----------------------------
現在學完了Lambda運算式了,可以結合擴充方法來瞧瞧Linq是怎麼實現的了。下面寫個模仿Linq的Where實現的樣本:
namespace ConsoleApplication3{ public delegate bool Compare(int i); class Program { static void Main(string[] args) { //.Net的Linq擴充的是IEnumerable<TSource>(底層,範圍廣),而我是Calculate類 Calculate c = new Calculate(); int[] intArr = new int[] { 10, 20 }; int[] intArrReturn = c.Where(m => m > 18, intArr); foreach (int i in intArrReturn) { Console.WriteLine(i); } Console.ReadKey(); } } public static class Exten { //技術點1:擴充方法,擴充的是Calculate類(第一個參數) //技術點2:以委託作為參數,在方法內部會調用(第二個參數) public static int[] Where(this Calculate c, Compare compare, int[] intArr) { int[] returnArr = new int[2]; //樣本定長數組 int count = 0; foreach (int i in intArr) { if (compare(i)) { returnArr[count] = i; count++; } } return returnArr; } } //計算類 public class Calculate { }}
有那麼點像了,Linq是泛型+Lambda,所以它的名字比較帥,如果我們把委託(參數名)改一下:Compare改為Func<T>
public delegate bool Func<T>(int i);
看下代碼提示,是不是更像Linq了?
很遺憾,到目前為止,我們擴充的Calculate類不支援鏈式操作,因為返回的結果是一個int[]數組。而鏈式操作的原理是,返回的結果又能繼續,如IEnumerable<int>的Where(m => m.xx)之後返回的又是IEnumerable<int>,所以能夠無限下去。
2014-04-08-----------------------------------
八、委託中的協變與逆變
協變:委託方法的傳回值類型直接或間接地繼承自委託簽名的傳回值類型,稱為協變。
逆變:委託簽名中的參數類型繼承自委託方法的參數類型,稱為逆變。
注意:協變強調的是傳回型別,逆變強調的則是方法參數。
協變Demo:
delegate People getSon(string name); class Program { static void Main(string[] args) { getSon g = sons; People p = g("張飛"); //注意雖然委託方法返回的是Son,但是委託本身返回的是父類People Console.WriteLine(p.Name); //輸出 張飛 Console.ReadKey(); } public static Son sons(string name) //委託方法的傳回值類型繼承自委託簽名的傳回值類型 逆變 { Son m = new Son(); m.Name = name; return m; } } public class People { public People() { } public int Id { get; set; } public string Name { get; set; } } public class Son : People { public int Age { get; set; } }
逆變Demo:
delegate string getSonName(Son s); class Program { static void Main(string[] args) { getSonName g = sons; Son s = new Son(); s.Name = "關羽"; string str = g(s); Console.WriteLine(str); //輸出 關羽 Console.ReadKey(); } public static string sons(People p) //委託簽名的參數類型繼承自委託方法的參數類型 此People為委託簽名參數Son的父類 { return p.Name; } } public class People { public People() { } public int Id { get; set; } public string Name { get; set; } } public class Son : People { public int Age { get; set; } }