Introduction to C# 2.0
C# 2.0 introduces several language extensions, the most important of which are Generics, Anonymous Methods, Iterators, and Partial Types.
C#2.0 介紹幾種語言擴展,泛型,匿名方法,迭代器 和、partial Types.
· Generics permit classes, structs, interfaces, delegates, and methods to be parameterized by the types of data they store and manipulate. Generics are useful because they provide stronger compile-time type checking, require fewer explicit conversions between data types, and reduce the need for boxing Operations and run-time type checks.
泛型允許類,結構,接口,代理還有方法被他們存儲和操作數據類型參數化。泛型相當有用,因為他們提供強制的編譯時類型檢查,要求更少的數據類型之間的顯式轉換,並減少裝箱拆箱的操作和運行時類型檢查。
· Anonymous methods allow code blocks to be written “in-line” where delegate values are expected. Anonymous methods are similar to lambda functions in the Lisp programming language. C# 2.0 supports the creation of “closures” where anonymous methods Access surrounding local variables and parameters.
· Iterators are methods that incrementally compute and yIEld a sequence of values. Iterators make it easy for a type to specify how the foreach statement will iterate over its elements.
· Partial types allow classes, structs, and interfaces to be broken into multiple pieces stored in different source files for easier development and maintenance. Additionally, partial types allow separation of Machine-generated and user-written parts of types so that it is easIEr to augment code generated by a tool.
This chapter gives an introduction to these new features. Following the introduction are four chapters that provide a complete technical specification of the features.
這個章節將介紹這些新特性。隨後的四個章節的介紹將提供有關特性的完整技術規范
The language extensions in C# 2.0 were designed to ensure maximum compatibility with existing code. For example, even though C# 2.0 gives special meaning to the words where, yield, and partial in certain contexts, these words can still be used as identifIErs. Indeed, C# 2.0 adds no new keywords as such keyWords could conflict with identifIErs in existing code.
c#2.0中的語言擴展最大程度上保證和現有代碼的兼容。舉例說,即使c#2.0指定以下詞如yIEld,partial在特定的上下文有特有的意義,他們依然可以作為標示符。甚至,c#2.0沒有加任何新的可能會在現有代碼沖突的關鍵詞
Generics
Generics permit classes, structs, interfaces, delegates, and methods to be parameterized by the types of data they store and manipulate. C# generics will be immediately familiar to users of generics in Eiffel or Ada, or to users of C++ templates, though they do not suffer many of the complications of the latter.
泛型允許類,結構,接口,代理和方法被他們存儲操作的數據類型參數化。C#泛型將很快被eiffel,ada的使用過泛型的用戶熟悉,或是使用過c++templates,盡管他們不需要忍受以後的多種編譯器。
Why generics?
Without generics, general purpose data structures can use type object to store data of any type. For example, the following simple Stack class stores its data in an object array, and its two methods, Push and Pop, use object to accept and return data, respectively:
如果沒有泛型,一般數據結構能使用類型對象去存儲任何數據類型。舉例,下面所描述的一個很簡單的棧的類存儲數據在對象數組中。它有兩個方法push和pop,使用對象分別地去接受和返回數據
public class Stack
{
object[] items;
int count;
public void Push(object item) {...}
public object Pop() {...}
}
While the use of type object makes the Stack class very flexible, it is not without drawbacks. For example, it is possible to push a value of any type, such a Customer instance, onto a stack. However, when a value is retrieved, the result of the Pop method must explicitly be cast back to the appropriate type, which is tedious to write and carrIEs a performance penalty for run-time type checking:
當使用對象類型的時候棧類的使用更靈活,它並非沒有缺陷。舉例說,它很可能壓入任何類型的值,如一個customer實例到一個棧。然而,當一個值返回,pop方法返回的結果必須顯式轉化成適當類型,不但編寫是乏味的並且在運行時的類型檢查降低性能。
Stack stack = new Stack();
stack.Push(new Customer());
Customer c = (Customer)stack.Pop();
If a value of a value type, such as an int, is passed to the Push method, it is automatically boxed. When the int is later retrIEved, it must be unboxed with an explicit type cast:
如果是一個值類型,如整型傳入push方法,它自動裝箱。當整型在後來返回的時候,它必須進行顯式的拆箱。
Stack stack = new Stack();
stack.Push(3);
int i = (int)stack.Pop();
Such boxing and unboxing Operations add performance overhead since they involve dynamic memory allocations and run-time type checks.
當他們處於動態內存分配和運行時類型檢查,裝箱拆箱操作將增加性能消耗。
A further issue with the Stack class is that it is not possible to enforce the kind of data placed on a stack. Indeed, a Customer instance can be pushed on a stack and then accidentally cast it to the wrong type after it is retrIEved:
進一步的關於棧的討論,強迫數據的類別壓入到棧是不可能的。事實上,一個customer實例能被壓入棧並且很有可能偶然在它返回時被轉化成錯誤的類型。
Stack stack = new Stack();
stack.Push(new Customer());
string s = (string)stack.Pop();
While the code above is an improper use of the Stack class, the code is technically speaking correct and a compile-time error is not reported. The problem does not become apparent until the code is executed, at which point an InvalidCastException is thrown.
The Stack class would clearly benefit from the ability to specify its element type. With generics, that becomes possible.
以上代碼從技術上說是正確的且在編譯時是不會報錯的,但對stack類的用法是不正確的。這個問題直到代碼執行才會顯示出來,並拋出InvalidCastException異常.
棧類應當受益於指定元素類型的能力。有了泛型以後,這個將成為可能。
Creating and using generics
創建和使用泛型
Generics provide a facility for creating types that have type parameters. The example below declares a generic Stack class with a type parameter T. The type parameter is specified in < and > delimiters after the class name. Rather than forcing conversions to and from object, instances of Stack accept the type for which they are created and store data of that type without conversion. The type parameter T acts as a placeholder until an actual type is specifIEd at use. Note that T is used as the element type for the internal items array, the type for the parameter to the Push method, and the return type for the Pop method:
泛型提供了一個便利的方法,通過類型參數去創建類型。下面的例子通過類型參數T聲明了一個泛型的棧。類型參數在類名後面<>分隔符中定義。Stack實例接受它創建和存儲的數據類型而不需要轉換遠勝於強制的對象轉換。
public class Stack
{
T[] items;
int count;
public void Push(T item) {...}
public T Pop() {...}
}
When the generic class Stack is used, the actual type to substitute for T is specifIEd. In the following example, int is given as the type argument for T:
當泛型類Stack被使用,替代T的真實類型將被指定。下面的例子裡,int 被指定代替T。
Stack stack = new Stack();
stack.Push(3);
int x = stack.Pop();
The Stack type is called a constructed type. In the Stack type, every occurrence of T is replaced with the type argument int. When an instance of Stack is created, the native storage of the items array is an int[] rather than object[], providing substantial storage efficiency compared to the non-generic Stack. Likewise, the Push and Pop methods of a Stack Operate on int values, making it a compile-time error to push values of other types onto the stack, and eliminating the need to explicitly cast values back to their original type when they’re retrIEved.
Stack類型被稱作構造類型. 在Stack裡,每次出現T將被類型參數int代替。當一個Stack實例被創建,本身存儲items數組是一個int型數組,比非泛型棧的對象數組提供真實性存儲效率。同樣的,Push和Pop方法操作int 值,如果push其他類型的數據給棧,將導致一個編譯時錯誤,它排除了當值被返回時所要求的顯式轉化成原型。
Generics provide strong typing, meaning for example that it is an error to push an int onto a stack of Customer objects. Just as a Stack is restricted to Operate only on int values, so is Stack restricted to Customer objects, and the compiler will report errors on the last two lines of the following example:
泛型提供強類型,意味著舉例說來 將一個整型數據壓入Customer 泛類型的棧。正像一個int泛型棧被嚴格約束只能操作int型,因此Customer型被嚴格要求操作Customer對象。下面例子的最後兩行,編譯器編譯的時候會報告錯誤。
Stack stack = new Stack();
stack.Push(new Customer());
Customer c = stack.Pop();
stack.Push(3); // Type mismatch error
int x = stack.Pop(); // Type mismatch error
Generic type declarations may have any number of type parameters. The Stack example above has only one type parameter, but a generic Dictionary class might have two type parameters, one for the type of the keys and one for the type of the values:
泛型聲明可以包括任何數目的類型參數。上面Stack的例子僅僅只有一個類型參數,但泛型Dictionary類可以含有兩個類型參數,一個是關鍵字的類型,一個是值的類型。
public class Dictionary
{
public void Add(K key, V value) {...}
public V this[K key] {...}
}
When Dictionary is used, two type arguments would have to be supplIEd:
當Dictionary被使用,必須提供兩種類型參數
Dictionary dict = new Dictionary();
dict.Add("Peter", new Customer());
Customer c = dict["Peter"];