19.C#2.0介紹
C#2.0引入了幾項語言擴展,其中最重要的是泛型、匿名方法、迭代器和不完整類型(partial type)。
泛型可以讓類別、結構、介面、委託和方法,透過他們所儲存和操縱的資料的類型被參數化。泛型是很有用的,因為他們提供了更強的編譯時類型檢查,減少了資料類型之間的明確轉換,以及裝箱操作和運行時類型檢查。
匿名方法可以讓程式碼區塊以內聯的方式潛入期望委託值的地方。匿名方法與Lisp 程式語言中的λ函數(lambda function)相似。 C#2.0支援「closures」的創建,在其中匿名方法可以存取相關局部變數和參數。
迭代器是可以遞增計算和產生值的方法。迭代器讓類型指定foreach語句如何迭代它的所有元素,變得很容易。
不完整類型可以讓類別、結構和介面被分割成多個部分儲存在不同的來源檔案中,這更有利於開發和維護。此外,不完整類型允許某些類型的機器產生的部分與使用者編寫的部分之間的分離,因此增加由工具產生的程式碼很容易。
本章將介紹這些新特徵。介紹完之後,接下來的四章提供了這些特徵的完整的技術規格。
C#2.0的語言擴充主要被設計用來確保與現存的程式碼之間最大的相容性。例如,儘管C#2.0對於where、yield 和partial這些詞在特定上下文中賦予了特別的意義,但這些詞仍然可被用作標識符。實際上,C# 2.0並沒有增加任何可能與現有程式碼中的識別碼衝突的關鍵字。
19.1 泛型
泛型可以讓類別、結構、介面、委託和方法,透過他們所儲存和操縱的資料的類型被參數化。 C#泛型對於使用Eiffel或Ada的泛型的用戶,或者對於C++模板的用戶來說是很熟悉的;但他們將不用再去忍受後者的眾多的複雜性。
19.1.1為什麼使用泛型
沒有泛型的話,通用目的的資料結構可以採用object類型儲存任何類型的資料。例如,下面的Stack類別在一個object數組中儲存數據,而它的兩個方法,Push和Pop相應地使用object接收和返回數據。
public class Stack { object[] items; int count; public void Push(object item){…} public object Pop(){…} }
儘管使用型object可以讓Stack類別更有彈性,但這樣做並不是沒有缺點。例如,你可以將一個任何類型的值,諸如,Customer的一個實例壓入(Push)堆疊。但當你取回一個值時,Pop方法的結果必須被明確地強制轉換到合適的類型,為一個運行時類型檢查去編寫程式碼,以及帶來的效能不利影響,是很令人討厭的。
Stack stack = new Stack(); Stack.Push(new Customer()); Customer c = (Customer)stack.Pop();
如果一個值類型的值,例如一個int被傳遞到Push方法,它將會被自動裝箱。當後面取得這個int 時,它必須使用一個明確的強制轉換而被取消裝箱。
Stack stack = new Stack(); Stack.Push(3); int I = (int)stack.Pop();
這種裝箱和取消裝操作增加了效能開銷,因為它們涉及到動態記憶體的分配和運行時類型檢查。
Stack類別的更大的問題是,它不能強制放置在堆疊上的資料種類。實際上,Customer實例可以被壓入堆疊,而取回它時可能被強制轉換到錯誤的類型。
Stack stack = new Stack(); Stack.Push(new Customer()); String s = (string)stack.Pop();
儘管先前的程式碼是Stack類別的一種不恰當用法,但這段程式碼從技術上說是正確的,並且也不會報告編譯時錯誤。問題直到程式碼執行時才會冒出來,在這一點上將會拋出一個InvalidCastException異常。
如果Stack類別具有能夠指定其元素的類型能力,那麼很顯然它能從這種能力得到好處。使用泛型,這將會變成可能。
19.1.2 建立和使用泛型
泛型為建立具有型別參數(type parameter)的型別提供了工具。下面的範例聲明了一個帶有型別參數T的泛型Stack類別。類型參數在類別名字之後的“<“和“>”分界符中指定。這裡沒有object與別的類型之間的相互轉換,Stack
Public class Stack<T> { T[] items; int count; public void Push(T item){…} public T Pop(){…} }
当泛型类Stack
Stack<int> stack = new Stack<int>(); Stack.Push(3); int x = stack.Pop();
Stack
泛型提供了强类型,意义例如压入一个int到Customer对象堆栈将会出现错误。就好像Stack
对于下面的例子,编译器将会在最后两行报告错误。
Stack<Customer> stack = new Stack<Customer>(); Stack.Push(new Customer()); Customer c = stack.Pop(); stack.Push(3); //类型不匹配错误 int x = stack.Pop(); //类型不匹配错误
泛型类型声明可以有任意数量的类型参数。先前的Stack
public class Dictionary<K , V> { public void Add(K key , V value){…} public V this[K key]{…} } 当Dictionary<K , V> 被使用时,必须提供两个类型参数。 Dictionary<string , Customer> dict = new Dictionary<string , Customer>(); Dict.Add(“Peter”, new Customer()); Custeomer c = dict[“Perter”];
19.1.3泛型类型实例化
与非泛型类型相似,被编译过的泛型类型也是由中间语言[Intermediate Language(IL)]指令和元数据表示。泛型类型的表示当然也对类型参数的存在和使用进行了编码。
当应用程序首次创建一个构造泛型类型的实例时,例如,Stack
.NET公共语言运行时使用值类型为每个泛型类型实例创建了一个本地代码的特定拷贝,但对于所有的引用类型它将共享那份本地代码的单一拷贝(因为,在本地代码级别,引用只是带有相同表示的指针)。
19.1.4约束
一般来讲,泛型类不限于只是根据类型参数存储值。泛型类经常可能在给定类型参数的类型的对象上调用方法。例如,Dictionary
public class Dictionary<K , V> { public void Add(K key , V value) { … if(key.CompareTo(x)<0){…}//错误,没有CompareTo方法 … } }
因为为K所指定的类型参数可能是任何类型,可以假定key参数存在的唯一成员,就是那些被声明为object类型的,例如,Equals,GetHashCode和ToString;因此,在先前例子中将会出现编译时错误。当然,你可以将key参数强制转换到一个包含CompareTo方法的类型。例如,key参数可能被强制转换到IComparable接口。
public class Dictionary<K , V> { public void Add(K key , V value) { … if(((IComparable)key).CompareTo(x)<0){…} … } }
尽管这种解决办法有效,但它需要在运行时的动态类型检查,这也增加了开销。更糟糕的是,它将错误报告推迟到了运行时,如果键(key)没有实现IComparable接口将会抛出InvalidCastException异常。
为了提供更强的编译时类型检查,并减少类型强制转换,C#允许为每个类型参数提供一个约束(constraint)的可选的列表。类型参数约束指定了类型必须履行的一种需求,其目的是为了为类型参数被用作实参(argument)。约束使用单词where声明,随后是类型参数的名字,接着是类或接口类型的列表,和可选的构造函数约束new()。
public class Dictionary<K, V> where K :IComparable { public void Add(K key , V value) { … if(key.CompareTo(x)<0){…} … } }
给定这个声明,编译器将会确保K的任何类型实参是实现了IComparable接口的类型。
并且,在调用CompareTo方法之前也不再需要对key参数进行显式地强制转换。为类型参数作为一个约束而给出的类型的所有成员,对于类型参数类型的值时直接有效的。
对于一个给定的类型参数,你可以指定任意数量的接口作为约束,但只能有一个类。每个约束的类型参数有一个单独的where 语句。在下面的例子中,类型参数K有两个接口约束,类型参数e有一个类约束和一个构造函数约束。
public class EntityTable<K, E> where K:IComparable<K>,IPersisable where E:Entity, new() { public void Add(K key , E entity) { … if(key.CompareTo(x)<0){…} … } }
在前面的例子中,构造函数约束new(),确保为E用作类型参数的类型具有一个公有的、无参数构造函数,并且它允许泛型类使用new E()创建该类型的实例。
类型参数约束应该很小心的使用。尽管它们提供了更强的编译时类型检查,在某些情况下增强了性能,但它们也限制了泛型类型的可能的用法。例如,泛型类List
以上就是C# 2.0 Specification(一)简介的内容,更多相关内容请关注PHP中文网(www.php.cn)!