C# 2.0 Specification(一)簡介

來源:互聯網
上載者:User

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 時,它必須使用一個顯式的強制轉換而被unboxing。

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<T>的執行個體接受它們被建立時的類型,並且儲存那個類型的資料而沒有轉換它。型別參數T充當一個預留位置,直到使用的時候才指定一個實際的類型。注意,T被用作內部items數組的元素類型、Push方法參數的類型和Pop方法的傳回值類型。

Public class Stack<T>{T[] items;int count;public void Push(T item){…}public T Pop(){…}}

當泛型類Stack<T>被使用時,T所代替的實際類型將被指定。在下面的例子中,int 將被作為T的型別參數而給出。

Stack<int> stack = new Stack<int>();Stack.Push(3);int x = stack.Pop();

Stack<int>類型被稱為構造類型(constructed type)。在Stack<int>類型中,T的每次出現都被使用型別參數int代替。當Stack<int>的執行個體被建立時,items數組的本機存放區就是一個int[]而不是object[],與非泛型Stack相比,它提供了更高的儲存效率。同樣地,在int值上的Stack<int>操作的Push和Pop方法,將會使得壓入其他類型的值到堆棧中出現一個編譯時間錯誤,並且當取回值的時候也不需要轉換回它們原始的類型。

泛型提供了強型別,意義例如壓入一個int到Customer對象堆棧將會出現錯誤。就好像Stack<int>被限制只能在int值上操作,同樣Stack<Customer>也被限制用於Customer對象。

對於下面的例子,編譯器將會在最後兩行報告錯誤。

Stack<Customer> stack = new Stack<Customer>();Stack.Push(new Customer());Customer c = stack.Pop();stack.Push(3); //類型不符錯誤int x = stack.Pop(); //類型不符錯誤

泛型型別聲明可以有任意數量的型別參數。先前的Stack<T>例子 只有一個型別參數,但一個通用的Dictionary類可能有兩個型別參數,一個用於鍵(key)的類型,另一個用於值(value)的類型。

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<int>,.NET通用語言執行平台的Just-In-Time 編譯器(JIT)將在進程中把泛型IL和中繼資料轉換為本地代碼,並且將型別參數替換為實際的類型。對於那個建構的泛型型別的後續引用將會使用相同的機器碼。從一個泛型型別建立一個特定構造類型的過程,稱為泛型型別執行個體化(generic type instantiation)。[/b]

.NET通用語言執行平台使用實值型別為每個泛型型別執行個體建立了一個本地代碼的特定拷貝,但對於所有的參考型別它將共用那份本地代碼的單一拷貝(因為,在本地代碼層級,引用只是帶有相同表示的指標)。

19.1.4約束

一般來講,泛型類不限於只是根據型別參數儲存值。泛型類經常可能在給定型別參數的類型的對象上調用方法。例如,Dictionary<K , V>類中的Add方法可能需要使用CompareTo方法比較索引值。

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>,IPersisablewhere E:Entity, new(){public void Add(K key , E entity){…if(key.CompareTo(x)<0){…}…}}

在前面的例子中,建構函式約束new(),確保為E用作型別參數的類型具有一個公有的、無參數建構函式,並且它允許泛型類使用new E()建立該類型的執行個體。

型別參數約束應該很小心的使用。儘管它們提供了更強的編譯時間類型檢查,在某些情況下增強了效能,但它們也限制了泛型型別的可能的用法。例如,泛型類List<T>可能約束T實現IComparable介面,由此它的Sort方法將可以比較項的大小。然而,這麼做卻使得沒有實現IComparable 介面的類型不能使用List<T>,即使是在這些情形下,Sort方法根本就沒有被調用過。

以上就是C# 2.0 Specification(一)簡介的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!


  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.