C# 程式設計語言的未來功能
最後更新:2017-02-28
來源:互聯網
上載者:User
編程 C# 程式設計語言的未來功能
Prashant Sridharan
Microsoft Corporation
2003年3月
適用於:
Microsoft® Visual C#(TM)
摘要:Microsoft Corporation 正在開發 C# 語言的下一個主要版本。本文介紹了四種主要的新功能,即泛型、迭代程式、匿名方法和局部類型。
目錄
簡介
泛型
迭代程式
匿名方法
局部類型
符合標準
可用性
更多資訊
簡介
C# 是創新性的新式程式設計語言,它巧妙地結合了最常用的行業語言和研究語言中的功能。在保持 C# 設計思想不變的同時,Microsoft 在 C# 語言中引入了幾種潛在的新功能,提高了開發人員在語言構造方面的效率。
Microsoft C#
自 2001 年 2 月 C# 問世以來,很多開發人員已經開始使用 C# 程式設計語言來構建軟體。而 Microsoft 自身也使用 C# 構建了幾種正式的應用程式,包括 .NET Framework、MSN Web 屬性和 Tablet PC SDK。由此可見,C# 是一種適用於構造高品質商業軟體的語言。
C# 語言中的許多功能是基於以下四種不同設計目標而建立的:
統一的類型系統以及簡化實值型別和參考型別在 C# 語言中的用法。
通過 XML 注釋、特性、屬性、事件和委託等功能建立基於組件的設計。
藉助 C# 語言的獨特功能(包括安全的指標操作、溢出檢查等)建立實用的開發人員控制功能。
建立諸如 foreach 和 using 語句這樣的實用語言構造,提高開發人員的效率。
在 C# 語言的“Visual Studio for Yukon”版本中,Microsoft 計劃通過將廣泛的研究語言和行業語言中的各種功能結合在一起建立一種簡潔、實用的文法。這些語言功能包括泛型、迭代程式、匿名方法和局部類型。
潛在的未來功能
實際上,C# 的未來創新功能主要基於統一的類型系統、基於組件的開發、開發人員控制功能和實用的語言構造。下面總結了 Microsoft 計劃在 C# 語言的下一個主要版本中提供的四種主要的新功能。這些功能的設計尚未完成,Microsoft Corporation 歡迎廣大的開發人員針對這些功能發表評論。
泛型
隨著項目變得越來越複雜,程式員日益需要一種方法來更好地重複使用和自訂他們現有的基於組件的軟體。為了實現在其他語言中重複使用進階代碼,程式員通常要使用一種名為“泛型”的功能。C# 將包括一種安全且高效的泛型,它與 C++ 中的模板和 Java 語言中提出的泛型在文法上只是稍有差別,但在實現方式上卻存在很大差別。
產生最新的泛型類
利用目前的 C#,程式員可以通過在基本物件類型的執行個體中儲存資料來建立有限形式的真正泛型。由於在 C# 中每個對象都是從基本物件類型繼承的,再加上統一 .NET 類型系統的裝箱和unboxing功能,程式員可以將參考型別和實值型別儲存到物件類型的變數中。但是,對於參考型別、實值型別與基本物件類型之間的轉換,還有一些效能缺陷。
為了說明這一點,以下程式碼範例建立了一個簡單的 Stack 類型,其中包含兩個操作“Push”和“Pop”。Stack 類將其資料存放區在物件類型的數組中,Push 和 Pop 方法使用基本物件類型來接受和返回資料:
public class Stack
{
private object[] items = new object[100];
public void Push(object data)
{
...
}
public object Pop()
{
...
}
}
然後,就可以將自訂類型(例如 Customer 類型)壓入堆棧。但是,如果程式需要檢索資料,則需要將 Pop 方法的結果(基本物件類型)顯式轉換成 Customer 類型。
Stack s = new Stack();
s.Push(new Customer());
Customer c = (Customer) s.Pop();
如果將一個實值型別(例如一個整數)傳遞給 Push 方法,運行時會自動將其轉換為參考型別(該過程稱作裝箱),然後將其儲存在內部資料結構中。與此類似,如果程式要從堆棧中檢索一個實值型別(例如一個整數),則需要將從 Pop 方法擷取的物件類型顯式轉換成實值型別,該過程稱作unboxing:
Stack s = new Stack();
s.Push(3);
int i = (int) s.Pop();
實值型別和參考型別之間的裝箱和unboxing操作非常繁重。
而且,在當前的實現中,無法限制堆棧中放置的資料類型。實際上,可以先建立堆棧,然後將 Customer 類型壓入堆棧。然後,可使用同一堆棧並嘗試將資料彈出,接著將其轉換為其他類型,如下例所示:
Stack s = new Stack();
s.Push(new Customer());
Employee e = (Employee) s.Pop();
儘管上一個程式碼範例錯誤地使用了要實現的單個類型 Stack 類,應視為錯誤,但它實際上卻是合法代碼,對它進行編譯時間不會出現問題。但在運行時,該程式會由於無效轉換操作而失敗。
建立和使用泛型
使用 C# 中的泛型可以根據它們所用的類型建立專供編譯器使用的高效資料結構。建立這些所謂的參數化型別後,其內部演算法保持不變,但其內部資料的類型可以隨著終端使用者的設定而改變。
為了協助開發人員節省學習該語言的時間,C# 中泛型的聲明方法與 C++ 中的大致相同。程式員可以按通常的方法建立類和結構,並使用角括弧標記(< 和 >)指定型別參數。使用類時,必須用該類的使用者提供的實際類型替換每個參數。
下例將建立一個 Stack 類,在此類聲明後的角括弧中指定並聲明一個名為 ItemType 的型別參數。泛型 Stack 類的執行個體將接受為其建立的類型並在本機存放區該類型的資料,而不是在建立的類型與基本物件類型之間進行轉換。型別參數 ItemType 充當代理,直到在執行個體化過程中指定了類型並將其用作內部項數組的類型(即 Push 方法的參數類型和 Pop 方法的傳回型別):
public class Stack<ItemType>
{
private ItemType[] items;
public void Push(ItemType data)
{
...
}
public ItemType Pop()
{
...
}
}
當程式按照以下樣本使用 Stack 類時,您可以指定泛型類使用的實際類型。本例使用執行個體化語句中的角括弧標記將原始的整數類型指定為參數,指示 Stack 類使用此類型:
Stack<int> stack = new Stack<int>();
stack.Push(3);
int x = stack.Pop();
執行此操作時,程式將建立 Stack 類的新執行個體,其中的每個 ItemType 都被提供的整數參數替換。實際上,當程式用整數參數建立 Stack 類的新執行個體時,在 Stack 類內部本機存放區的項目數組將為整數,而不是對象。程式還消除了與將整數壓入堆棧相關聯的裝箱問題。此外,當程式從堆棧彈出項目時,您無需將其顯式轉換為相應的類型,因為 Stack 類的當前特定執行個體會將整數本機存放區在其資料結構中。
如果希望程式在 Stack 類中儲存其他類型的項目,則必須建立一個 Stack 類的新執行個體並將新類型指定為參數。假設有一個簡單的 Customer 類型,希望程式使用 Stack Object Storage Service該類型。要實現此操作,只需執行個體化 Stack 類並將 Customer 對象作為其型別參數,即可輕鬆重複使用程式碼:
Stack<Customer> stack = new Stack<Customer>();
stack.Push(new Customer());
Customer c = stack.Pop();
當然,如果程式建立了一個將 Customer 類型作為參數的 Stack 類,則只能在該堆棧中儲存 Customer 類型。實際上,C# 中的泛型具有嚴格的類型,這意味著您不能在該堆棧中儲存整數,如以下樣本所示:
Stack<Customer> stack = new Stack<Customer>();
stack.Push(new Customer());
stack.Push(3) // 編譯時間錯誤
Customer c = stack.Pop(); // 不需要類型轉換。
泛型的優點
使用泛型,程式員只需編寫、測試和部署一次代碼,即可對各種不同的資料類型重複使用該代碼。第一個 Stack 樣本具備此功能,第二個 Stack 樣本允許程式重複使用對其應用程式效能影響不大的代碼。對於實值型別,第一個 Stack 樣本具有較大的效能問題,而第二個 Stack 樣本完全消除了這種問題,因為它去除了裝箱和向下的類型轉換。
而且,編譯時間還會對泛型進行檢查。當程式使用提供的型別參數執行個體化泛型類時,此型別參數只能是程式在類定義中指定的類型。例如,如果程式建立了一個 Customer 物件類型的 Stack,就無法將整數壓入堆棧。通過強制執行這種操作,可以產生更可靠的代碼。
此外,與其他嚴格的類型實現相比,泛型的 C# 實現降低了代碼的膨脹速度。使用泛型建立具有類型的集合,可以在保持操作效能優勢的同時避免建立每個類的特定變體。例如,程式可以建立一個參數化的 Stack 類,而無需建立用於儲存整數的 IntegerStack、用於儲存字串的 StringStack 以及用於儲存 Customer 類型的 CustomerStack。
這樣可以增加代碼的可讀性。只需建立一個 Stack 類,程式就可以將與某個堆棧相關聯的所有操作封裝在一個使用方便的類中。然後,在建立 Customer 類型的 Stack 時,儘管其中儲存了 Customer 類型,但顯而易見,程式使用的仍然是堆棧資料結構。
多個型別參數
泛型可以使用任意多個參數類型。上面的 Stack 樣本中只使用了一種類型。假設您建立了一個儲存值和鍵的簡單 Dictionary 類。在程式中可以通過聲明兩個參數(放在類定義的角括弧中並用逗號分隔)來定義一個泛型版本的 Dictionary 類:
public class Dictionary<KeyType, ValType>
{
public void Add(KeyType key, ValType val)
{
...
}
public ValType this[KeyType key]
{
...
}
}
使用該 Dictionary 類時,需要在執行個體化語句的角括弧中提供多個以逗號分隔的參數,並為 Add 函數和索引產生器提供正確類型的參數:
Dictionary<int, Customer> dict = new Dictionary<int, Customer>();
dict.Add(3, new Customer());
Customer c = dict.Get[3];
約束
通常情況下,程式並不僅僅局限於根據給定的型別參數儲存資料,而是經常需要使用型別參數的成員來執行程式泛型中的語句。
為什麼需要約束
假設在 Dictionary 類的 Add 方法中,您需要使用所提供的鍵的 CompareTo 方法比較項目,例如:
public class Dictionary<KeyType, ValType>
{
public void Add(KeyType key, ValType val)
{
...
switch(key.CompareTo(x))
{
}
...
}
}
遺憾的是,正如預期那樣,型別參數 KeyType 在編譯時間是泛型。如果這樣編寫,編譯器假設對型別參數為 KeyType 的 key 執行個體只能執行適用於基本物件類型(例如 ToString)的操作。結果,由於 CompareTo 方法未定義,編譯器將顯示編譯錯誤。然而,程式可以將 key 變數轉換為包含 CompareTo 方法的對象,例如 IComparable 介面。在以下樣本中,程式將 KeyType 參數類型的執行個體 key 顯式轉換為程式可以編譯的 IComparable 介面:
public class Dictionary<KeyType, ValType>
{
public void Add(KeyType key, ValType val)
{
...
switch(((IComparable) key).CompareTo(x))
{
}
...
}
}
然而,如果立即執行個體化 Dictionary 類而且提供的型別參數沒有實現 IComparable 介面,則程式將遇到執行階段錯誤,尤其是 InvalidCastException 異常。