1.泛型的約束: (1)介面約束; (2)基類約束,基類約束必須放在第一(假如有多個約束); (3)struct/class約束; (4)多個參數類型的約束,每個類型參數都要用where關鍵字; (5)構造器約束,只能是無參構造器,如new(); (6)約束可以由派生類繼承,但必須在派生類中顯式地指 ...
1.泛型的約束:
(1)介面約束;
(2)基類約束,基類約束必須放在第一(假如有多個約束);
(3)struct/class約束;
(4)多個參數類型的約束,每個類型參數都要用where關鍵字;
(5)構造器約束,只能是無參構造器,如new();
(6)約束可以由派生類繼承,但必須在派生類中顯式地指定這些約束;
(7)泛型方法的約束設置與泛型類的約束設置,是一樣的;
2.協變性與逆變性:在泛型中,將一個較具體的類型賦給一個較泛化的類型,即是協變。將一個較泛化的類型賦給一個較具體的類型,即是逆變。
協變:在C#4.0中使用out類型參數修飾符允許協變性,該out修飾的類型參數,會導致該類型參數只能用於成員的返回與屬性的取值方法,永遠不用於輸入參數或者屬性的賦值方法。
逆變:在C#4.0中使用in類型參數修飾符允許逆變性,該in修飾的類型參數,會導致該類型參數只能用於成員的輸入(輸入參數)與屬性的設置方法。
如Contact類繼承自PdaItem,若對泛型介面(IConvertible<in T1,out T2>)進行了協變與逆變的設置,就可以成功地從一個IConvertible<PdaItem,Contact>轉換成一個IConvertible<Contact,PdaItem>。
綜上所述:協出逆進。
3.數組本身是支持協變與逆變,如PdaItem[] pdaItems=new Contact[]{},Contact[] pdaItems=(Contact)new PdaItem[]{}。
4.委托概述:長期以來經驗豐富的C、C++程式員利用方法指針,將可執行的步驟(方法)作為參數傳遞給另一個方法。C#使用委托來提供相同的功能,它將方法作為對象封裝起來,允許在運行時間接綁定一個方法的調用,可查看DelegateSample類的代碼。
5.委托的定義:C#將所有委托的定義成間接派生於System.Delegate,其繼承層次為Object -> Delegate ->MulticastDelegate -> 自定義委托。當然我們使用關鍵delegate聲明,其在編譯後生成的CIL代碼,就是自動繼承上面的結構,因此我們不能手動顯式繼承委托。在委托實例化中,從[email protected]開始可以不使用new來手工創建,直接賦值相同的簽名方法即可,編譯器會在編譯過程中會自動根據委托推斷,自動添加new創建,以及把方法名稱作為委托參數傳遞也是一樣,
6.系統自定義的委托:在.net3.5(C#3.0)中加入了Action與Func這個兩個泛型委托,前者是沒有返回值的委托,後者是有返回值的委托,在.Net4.0(C#4.0)中又在這些泛型委托中加入了in(逆變)/out(協變)的功能。這些委托的定義省略了我們手工進行自定義委托的情況,因為這些泛型委托都涵蓋了我們可能遇到的所有使用情況。(除非你要定義一個有特殊含義的委托名稱)。
7.委托使用可變性,即委托類型參數的可變性(協變與逆變),可查看DelegateSample.CovariantAndContravariant()方法的代碼。
8.Lambda表達式:Lambda表達式是匿名函數的簡潔表達形式,Lambda表達式包含語句Lambda與表達式Lambda,前者可以含有多個語句的語句塊(一般只使用兩三條語句),後者只有一句表達式。
9.使用Lambda表達式註意事項:
(1)Lambda表達式本身是無類型,只有賦值給一個委托,Lambda表達式就表現為有一個類型,就是該方法簽名的委托。
(2)因Lambda表達式是無類型的,所以也就不能賦值給隱式類型的局部變數。
(3)如果目的地在Lambda表達式的外部,C#就不允許在匿名函數內部使用跳轉語句(break、goto、continue),類似的,不能從Lambda表達式外部跳入Lambda表達式內部。
(4)對於在Lambda表達式內部引入的變數,其作用域僅限於Lambda表達式主體。
(5)編譯器的程式執行流程分析機制檢測不到在Lambda表達式內部初始化局部變數的情況。
以上的註意事項的代碼實例,可以查看DelegateSample.NoticeMatter()方法。
10.Lambda表達式並非CLR內部的固有構造,它們的實現是由C#編譯器在編譯時生成的,編譯器會自動把Lambda表達式生成為一個當前類的內部方法(靜態或實例方法),其方法名是編譯器自動處理(其一般含有anonymous等字眼的描述)。
11.在匿名函數中使用外部局部變數,編譯器會把這些外部變數生成為當前類的嵌套類(sealed閉包)的公共欄位,其嵌套類中含有公共欄位與對應的匿名名稱方法,因此這些局部變數變成了嵌套的成員,其生命周期也就被延長了。通過查看ILdasm(中間語言反編譯,能夠查看類庫編譯後的中間語言,VS工具自帶)。也可查看DelegateSample.AscendingAndTimeOfCompiler()與_LocalDisplayClass_00001嵌套類。
12.表達式樹:“解釋”是C#引入表達式樹這一概念的重要動機,如果一種Lambda表達式代表的是與表達式有關的數據,而不是編譯好的代碼(匿名函數與委托),這種Lambda表達式就是“表達式樹”。由於表達式樹代表的是數據而非編譯好的代碼,所以可以把數據轉換成一種替代格式。
例如,可以把它從表達式數據轉換成在資料庫中執行的SQL代碼。如IQueryable介面中含有表達式樹類型、查詢供應者、查詢返回的類型,只要實現該介面就能對特定的數據源進行查詢操作,期內包含需要自定義解析表達式樹的指令。所有表達式樹的類型(如ParameterExpression、BinaryExpression、MethodCallExpression),都是從Expression繼承。
13.Lambda表達式和表達式樹的比較:無論是用於委托的Lambda表達式,還是用於表達式樹的Lambda表達式都會被編譯,而且在這兩種情況下,表達式的語法都會在編譯時進行完整的語義分析驗證。但是,兩者的區別在於,Lambda表達式在CIL中遍編譯成委托,而表達式樹被編譯成System.Linq.Expressions.Expression類型的一個數據結構。可查看AnalysisExpression類的代碼,以及Output()方法輸出的數據內容。
14.一個表達式樹是由零個或者多個其他表達式樹構成的,被包含的表達式樹存儲在Expression的Body屬性中。通常,語句Lambda和表達式Lambda可以互換使用,然而,不能將語句Lambda轉換成“表達式樹”,只能使用表達式Lambda語法來表示“表達式樹”。
public class DelegateSample { //冒泡排序,基本排序處理 public static void BubbleSort<TSource>(TSource[] items, Func<TSource, TSource, bool> predicate) where TSource : IComparable<TSource>, IEquatable<TSource> { if (items == null || items.Length < 2) { return; } TSource tempItem; /*第一種演算法,定點比較 for (int i = 0; i < items.Length; i++) { for (int j = i; j < items.Length; j++) { if (predicate(items[i], items[j])) { tempItem = items[i]; items[i] = items[j]; items[j] = tempItem; } } }*/ //第二種演算法,逐一比較 for (int i = items.Length - 1; i >= 0; i--) { for (int j = 1; j <= i; j++) { if (predicate(items[j - 1], items[j])) { tempItem = items[j - 1]; items[j - 1] = items[j]; items[j] = tempItem; } } } } public static void Ascending() { int[] items = new int[] { 3, 2, 1, 4, 6, 5 }; Console.WriteLine("升序前"); foreach (var item in items) { Console.Write("{0}\t", item); } BubbleSort(items, (first, second) => first > second); Console.WriteLine("\n升序後"); foreach (var item in items) { Console.Write("{0}\t", item); } } public static void Descending() { int[] items = new int[] { 3, 2, 1, 4, 6, 5 }; Console.WriteLine("降序前"); foreach (var item in items) { Console.Write("{0}\t", item); } BubbleSort(items, (first, second) => first < second); Console.WriteLine("\n降序後"); foreach (var item in items) { Console.Write("{0}\t", item); } } //按字母排序 public static void AlphabeticalGreaterThan() { int[] items = new int[] { 1, 12, 13, 5, 4, 6 }; Console.WriteLine("排序前"); foreach (var item in items) { Console.Write("{0}\t", item); } BubbleSort(items, (first, second) => { int comparison = first.ToString().CompareTo(second.ToString()); return comparison > 0; }); Console.WriteLine("\n排序後"); foreach (var item in items) { Console.Write("{0}\t", item); } } //委托的協變與逆變 public static void CovariantAndContravariant() { //逆變 Action<object> broadAction = delegate (object data) { Console.WriteLine(data); }; Action<string> narrowAction = broadAction; //協變 Func<string> narrowFunction = () => Console.ReadLine(); Func<object> broadFunction = narrowFunction; //協變與逆變 Func<object, string> func1 = (data) => data.ToString(); Func<string, object> func2 = func1; } //Lambda表達式註意事項 public static void NoticeMatter() { //錯誤:控制權不能離開Lambda表達式的主體。 /*string[] args; Func<string> expression; switch (args[0]) { case "/File": expression = () => { if (!File.Exists(args[1])) { break; } //... return args[1]; }; //... }*/ //不能在Lambda表達式內部進行初始化局部變數。 /*int number; Func<string, bool> expression = text => int.TryParse(text, out number); if (expression("1")) { //錯誤:使用未賦值的局部變數 Console.WriteLine(number); } int number: Func<int, bool> isFortyTwo = x => 42 == (number = x); if (isFortyTwo(42)) { //錯誤:使用未賦值的局部變數 Console.WriteLine(number); }*/ } //升序,並記錄其比較的次數,編譯前 public static void AscendingAndTime() { int comparisonCount = 0; int[] items = new int[] { 3, 2, 1, 4, 6, 5 }; Console.WriteLine("升序前"); foreach (var item in items) { Console.Write("{0}\t", item); } BubbleSort(items, (first, second) => { comparisonCount++; return first > second; }); Console.WriteLine("\n升序後"); foreach (var item in items) { Console.Write("{0}\t", item); } Console.WriteLine("\n比較的次數 {0}", comparisonCount); } //升序,並記錄其比較的次數,編譯後 public static void AscendingAndTimeOfCompiler() { _LocalDisplayClass_00001 locals = new _LocalDisplayClass_00001(); locals.comparisonCount = 0; int[] items = new int[] { 3, 2, 1, 4, 6, 5 }; Console.WriteLine("升序前"); foreach (var item in items) { Console.Write("{0}\t", item); } BubbleSort(items, locals._AnonymousMethod_000000); Console.WriteLine("\n升序後"); foreach (var item in items) { Console.Write("{0}\t", item); } Console.WriteLine("\n比較的次數 {0}", locals.comparisonCount); } private sealed class _LocalDisplayClass_00001 { public int comparisonCount; public bool _AnonymousMethod_000000(int first, int second) { comparisonCount++; return first > second; } } } public class AnalysisExpression { //輸出表達式樹的數據結構 public void Output() { Expression<Func<int, int, bool>> expression = (x, y) => x > y; Console.WriteLine("------------{0}------------", expression); PrintNode(expression.Body, 0); Console.WriteLine("\n"); expression = (x, y) => x * y > x + y; Console.WriteLine("------------{0}------------", expression); PrintNode(expression.Body, 0); Console.WriteLine("\n"); } //列印表達式樹節點,一般表達式樹 private void PrintNode(Expression expression, int indent) { if (expression is BinaryExpression) { PrintNode(expression as BinaryExpression, indent); } else { PrintSingle(expression, indent); } } //列印表達式樹節點,二元表達式樹 private void PrintNode(BinaryExpression expression, int indent) { //表達式樹是一個數據集合,而通過遍曆數據,就可以將數據轉換成另一種格式。此處將數據直接轉換成對應的文本,然而,具體如何對數據進行解釋,完全是開發者自己決定。 PrintNode(expression.Left, indent + 1); PrintSingle(expression, indent); PrintNode(expression.Right, indent + 1); } //列印單個表達式樹節點內容 private void PrintSingle(Expression expression, int indent) { Console.WriteLine("{0," + indent * 5 + "}{1}", "", NodeToString(expression)); } //節點轉到字元 private string NodeToString(Expression expression) { string content; switch (expression.NodeType) { case ExpressionType.Multiply: content = "*"; break; case ExpressionType.Add: content = "+"; break; case ExpressionType.Divide: content = "/"; break; case ExpressionType.Subtract: content = "-"; break; case ExpressionType.GreaterThan: content = ">"; break; case ExpressionType.LessThan: content = "<"; break; default: content = string.Format("{0}({1})", expression, expression.NodeType); break; } return content; } }View Code
---------------------以上內容根據《C#本質論 第三版》進行整理