C# 2.0 Specificationread.pudn.com/downloads104/doc/428713/C# Langua… · Web viewC# 2.0 introduces...

Click here to load reader

Transcript of C# 2.0 Specificationread.pudn.com/downloads104/doc/428713/C# Langua… · Web viewC# 2.0 introduces...

C# 2.0 Specification

=_Ref446425405>

{0>C#<}100{>C#<0}

{0>Version 2.0 Specification<}0{>2.0 版規格<0}

{0>July 2005<}0{>2005 年 7 月<0}

{0>=FeatureTitle>=DescriptionPlus>=AllHeadersAndTitle>

Notice<}100{>=FeatureTitle>=DescriptionPlus>=AllHeadersAndTitle>

Notice<0}

© 2005 {0>Microsoft Corporation.<}0{>Microsoft Corporation.<0}{0>All rights reserved.<}100{>All rights reserved.<0}

{0>Microsoft, Windows, Visual Basic, Visual C#, and Visual C++ are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries/regions.<}100{>Microsoft、Windows、Visual Basic、Visual C# 和 Visual C++ 均為 Microsoft Corporation 在美國及/或其他國家/地區的註冊商標或商標。<0}

{0>Other product and company names mentioned herein may be the trademarks of their respective owners.<}100{>文內所提及之其他產品和公司名稱得為其各自擁有者的商標。<0}

{0>Table of Contents<}100{>目錄<0}

119. Introduction to C# 2.0

19.1 Generics1

19.1.1 Why generics?1

19.1.2 Creating and using generics2

19.1.3 Generic type instantiations3

19.1.4 Constraints4

19.1.5 Generic methods5

19.2 Anonymous methods6

19.2.1 Method group conversions8

19.3 Iterators8

19.4 Partial types11

19.5 Nullable types12

20. Generics15

20.1 Generic class declarations15

20.1.1 Type parameters15

20.1.2 The instance type16

20.1.3 Base specification17

20.1.4 Members of generic classes17

20.1.5 Static fields in generic classes18

20.1.6 Static constructors in generic classes19

20.1.7 Accessing protected members19

20.1.8 Overloading in generic classes20

20.1.9 Parameter array methods and type parameters20

20.1.10 Overriding and generic classes20

20.1.11 Operators in generic classes21

20.1.12 Nested types in generic classes22

20.1.13 Application entry point23

20.2 Generic struct declarations23

20.3 Generic interface declarations23

20.3.1 Uniqueness of implemented interfaces24

20.3.2 Explicit interface member implementations24

20.4 Generic delegate declarations25

20.5 Constructed types26

20.5.1 Type arguments26

20.5.2 Open and closed types27

20.5.3 Base classes and interfaces of a constructed type27

20.5.4 Members of a constructed type28

20.5.5 Accessibility of a constructed type29

20.5.6 Conversions29

20.5.7 Using alias directives29

20.5.8 Attributes30

20.5.9 Arrays and the generic IList interface30

20.6 Generic methods31

20.6.1 Generic method signatures31

20.6.2 Virtual generic methods32

20.6.3 Calling generic methods33

20.6.4 Inference of type arguments34

20.6.5 Grammar ambiguities35

20.6.6 Using a generic method with a delegate36

20.6.7 Members that cannot be generic36

20.7 Constraints37

20.7.1 Satisfying constraints41

20.7.2 Member lookup on type parameters42

20.7.3 Type parameters and boxing42

20.7.4 Conversions involving type parameters43

20.8 Expressions and statements45

20.8.1 Object creation expressions45

20.8.2 The typeof operator45

20.8.3 Reference equality operators46

20.8.4 The is operator47

20.8.5 The as operator47

20.8.6 Exception statements47

20.8.7 The lock statement47

20.8.8 The using statement47

20.8.9 The foreach statement48

20.9 Revised lookup rules48

20.9.1 Namespace and type names48

20.9.2 Member lookup50

20.9.3 Applicable function member51

20.9.4 Better function member51

20.9.5 Simple names52

20.9.6 Member access53

20.9.7 Method invocations55

20.10 Right-shift grammar changes56

21. Anonymous methods59

21.1 Anonymous method expressions59

21.2 Anonymous method signatures59

21.3 Anonymous method conversions59

21.4 Anonymous method blocks61

21.5 Outer variables61

21.5.1 Captured outer variables61

21.5.2 Instantiation of local variables62

21.6 Anonymous method evaluation64

21.7 Delegate instance equality65

21.8 Definite assignment65

21.9 Method group conversions65

21.10 Delegate creation expressions67

21.11 Implementation example67

22. Iterators71

22.1 Iterator blocks71

22.1.1 Enumerator interfaces71

22.1.2 Enumerable interfaces71

22.1.3 Yield type71

22.1.4 This access72

22.2 Enumerator objects72

22.2.1 The MoveNext method72

22.2.2 The Current property73

22.2.3 The Dispose method74

22.3 Enumerable objects74

22.3.1 The GetEnumerator method74

22.4 The yield statement75

22.4.1 Definite assignment76

22.5 Implementation example76

23. Partial types83

23.1 Partial declarations83

23.1.1 Attributes83

23.1.2 Modifiers84

23.1.3 Type parameters and constraints84

23.1.4 Base class84

23.1.5 Base interfaces85

23.1.6 Members85

23.2 Name binding86

24. Nullable types87

24.1 Nullable types87

24.1.1 Members87

24.1.2 Default value88

24.1.3 The value type constraint88

24.2 Conversions88

24.2.1 Null literal conversions88

24.2.2 Nullable conversions88

24.2.3 Boxing and unboxing conversions89

24.2.4 Permitted user-defined conversions90

24.2.5 Evaluation of user-defined conversions90

24.2.6 Lifted conversions90

24.2.7 User-defined implicit conversions90

24.2.8 User-defined explicit conversions91

24.3 Expressions92

24.3.1 Lifted operators92

24.3.2 Permitted user-defined operators93

24.3.3 Operator overload resolution93

24.3.4 Equality operators and null93

24.3.5 The is operator94

24.3.6 The as operator94

24.3.7 Compound assignment94

24.3.8 The bool? type95

24.3.9 The null coalescing operator95

25. Other features97

25.1 Property accessor accessibility97

25.1.1 Accessor declarations97

25.1.2 Accessor usage98

25.1.3 Overriding and interface implementation98

25.2 Static classes99

25.2.1 Static class declarations99

25.2.2 Referencing static class types100

25.3 Namespace alias qualifiers100

25.3.1 Qualified alias member102

25.3.2 Uniqueness of aliases103

25.4 Extern aliases104

25.4.1 Extern alias directives105

25.5 Pragma directives106

25.5.1 Pragma warning107

25.6 Default value expression107

25.7 Conditional attribute classes108

25.8 Fixed size buffers109

25.8.1 Fixed size buffer declarations109

25.8.2 Fixed size buffers in expressions110

25.8.3 Fixed statements111

25.8.4 Definite assignment checking111

19. {0>=_Ref463345912>Introduction to C# 2.0<}0{>=_Ref463345912>C# 2.0 簡介<0}

{0>C# 2.0 introduces several language extensions, including Generics, Anonymous Methods, Iterators, Partial Types, and Nullable Types.<}0{>C# 2.0 引進數種語言擴充功能,其中包括泛型、匿名方法 (Anonymous Method)、Iterator、部分型別以及可為 Null 的型別 (Nullable Type)。<0}

· {0>Generics permit classes, structs, interfaces, delegates, and methods to be parameterized by the types of data they store and manipulate.<}0{>泛型允許類別 (Class)、結構 (Struct)、介面、委派 (Delegate) 和方法由它們所儲存和管理之資料的型別參數化。<0} {0>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.<}0{>泛型很有用,它提供功能更強的編譯時期型別檢查,並且在資料型別之間需要較少的明確轉換,而且能夠省去 Boxing 運算和執行階段型別檢查的需求。<0}

· {0>Anonymous methods allow code blocks to be written “in-line” where delegate values are expected.<}0{>匿名方法允許程式碼區塊「內嵌」在必須有委派值的地方。<0} {0>Anonymous methods are similar to lambda functions in the Lisp programming language.<}0{>匿名方法與 Lisp 程式設計語言中的 Lambda 函式相似。<0} {0>C# 2.0 supports the creation of “closures” where anonymous methods access surrounding local variables and parameters.<}0{>C# 2.0 支援「終止」的建立,其中匿名方法會存取周圍的區域變數和參數。<0}

· {0>Iterators are methods that incrementally compute and yield a sequence of values.<}0{>Iterator 是以累加方式計算並產生數值序列的方法。<0} {0>Iterators make it easy for a type to specify how the foreach statement will iterate over its elements.<}0{>Iterator 使型別可以輕易指定 foreach 陳述式 (Statement) 反覆查看其項目的方式。<0}

· {0>Partial types allow classes, structs, and interfaces to be broken into multiple pieces stored in different source files for easier development and maintenance.<}0{>部分型別允許類別、結構和介面分為數個部分,並儲存在不同的原始程式檔 (Source File) 中,以便用於開發和維護。<0} {0>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.<}0{>此外,部分型別還允許將型別的電腦產生部分和使用者撰寫部分分開,以便更容易地擴大工具所產生的程式碼。<0}

· {0>Nullable types represent values that possibly are unknown.<}0{>可為 Null 的型別代表可能為未知的值。<0} {0>A nullable type supports all values of its underlying type plus an additional null state.<}0{>可為 Null 的型別支援其基礎型別的所有值以及額外的 null 狀態。<0} {0>Any value type can be the underlying type of a nullable type.<}0{>任何實值型別 (Value Type) 都可以做為可為 Null 型別的基礎型別。<0} {0>A nullable type supports the same conversions and operators as its underlying type, but additionally provides null value propagation similar to SQL.<}0{>可為 Null 的型別支援與其基礎型別相同的轉換和運算子,但是會另外提供類似 SQL 的 null 值傳用。<0}

{0>This chapter gives an introduction to these new features.<}0{>本章提供這些新功能的簡介。<0} {0>Following the introduction are five chapters that provide a complete technical specification of the features.<}0{>簡介之後,是五個完整功能技術規格的章節。<0} {0>The final chapter describes a number of smaller extensions that are also included in C# 2.0.<}0{>最後一章則是說明一些包含在 C# 2.0 的小型擴充功能。<0}

{0>The language extensions in C# 2.0 were designed to ensure maximum compatibility with existing code.<}0{>C# 2.0 中的語言擴充功能,是設計用來確保與現有程式碼之間具有最大的相容性。<0} {0>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.<}0{>例如,即使 C# 2.0 在某些內容中給予 where、yield 和 partial 文字特殊的意義,這些文字仍可以當做識別項使用。<0} {0>Indeed, C# 2.0 adds no new keywords as such keywords could conflict with identifiers in existing code.<}0{>事實上,C# 2.0 並沒有加入新的關鍵字,因為這類關鍵字可能會與現有程式碼中的識別項發生衝突。<0}

{0>For the latest information on the C# language and instructions for providing feedback on this document, please visit the C# Language Home Page (http://msdn.microsoft.com/vcsharp/language).<}0{>如需 C# 語言的最新資訊以及針對此文件提供意見的指示,請造訪 C# 語言首頁 (http://www.microsoft.com/taiwan/vstudio/vcsharp/default.mspx)。<0}

19.1 {0>Generics<}0{>泛型<0}

{0>Generics permit classes, structs, interfaces, delegates, and methods to be parameterized by the types of data they store and manipulate.<}100{>泛型允許類別、結構、介面、委派和方法由它們所儲存和管理之資料的型別參數化。<0} {0>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.<}0{>Eiffel 或 Ada 的使用者,或是 C++ 樣板的使用者應該會對 C# 泛型感到很熟悉,不過 C# 泛型並不像 C++ 樣板那麼複雜。<0}

19.1.1 {0>Why generics?<}0{>為何要使用泛型?<0}

{0>Without generics, general purpose data structures can use type object to store data of any type.<}0{>在沒有泛型的情況下,一般用途的資料結構可以使用 object 型別儲存任何型別的資料。<0} {0>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:<}0{>例如,下列簡單的 Stack 類別會將其資料儲存在 object 陣列中,而它的兩個方法 Push 和 Pop,分別會使用 object 接受和傳回資料:<0}

public class Stack{

object[] items;

int count;

public void Push(object item) {...}

public object Pop() {...}}

{0>While the use of type object makes the Stack class very flexible, it is not without drawbacks.<}0{>雖然使用 object 型別讓 Stack 類別變得很有彈性,但是這樣做也有缺點。<0} {0>For example, it is possible to push a value of any type, such a Customer instance, onto a stack.<}0{>例如,您可以將任何型別的值 (例如 Customer 執行個體 (Instance)) 推送至堆疊。<0} {0>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:<}0{>但是,在擷取值時,您必須明確地將 Pop 方法的結果轉換回適當的型別,此撰寫作業相當冗長,而且會對執行階段型別檢查造成負面的效能影響:<0}

Stack stack = new Stack();stack.Push(new Customer());Customer c = (Customer)stack.Pop();

{0>If a value of a value type, such as an int, is passed to the Push method, it is automatically boxed.<}0{>如果實值型別的值 (例如 int) 是傳遞到 Push 方法,就會自動成為 Box。<0} {0>When the int is later retrieved, it must be unboxed with an explicit type cast:<}0{>之後,在擷取該 int 時,就必須以明確的型別轉換 (Type Cast) 進行 Unboxed:<0}

Stack stack = new Stack();stack.Push(3);int i = (int)stack.Pop();

{0>Such boxing and unboxing operations add performance overhead since they involve dynamic memory allocations and run-time type checks.<}0{>由於這種 Boxing 和 Unboxing 作業涉及動態記憶體配置和執行階段型別檢查,因此會增加效能的負荷。<0}

{0>A further issue with the Stack class is that it is not possible to enforce the kind of data placed on a stack.<}0{>Stack 類別的另一個問題是,它無法強制使用位於堆疊上的資料類型。<0} {0>Indeed, a Customer instance can be pushed on a stack and then accidentally cast it to the wrong type after it is retrieved:<}0{>實際上,Customer 執行個體可能推送到堆疊,然後在擷取之後,轉換成錯誤的型別:<0}

Stack stack = new Stack();stack.Push(new Customer());string s = (string)stack.Pop();

{0>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.<}0{>雖然上述程式碼是 Stack 類別的不當用法,但在技術上來說仍然是正確的,並且不會回報任何編譯時期錯誤。<0} {0>The problem does not become apparent until the code is executed, at which point an InvalidCastException is thrown.<}0{>這個問題在程式碼執行之前不會顯現,但程式碼執行時,會擲回 InvalidCastException。<0}

{0>The Stack class would clearly benefit from the ability to specify its element type.<}0{>如果 Stack 類別可以指定其項目型別,會明顯帶來好處,<0} {0>With generics, that becomes possible.<}0{>而使用泛型,正好可以達成這個目的。<0}

19.1.2 {0>Creating and using generics<}0{>建立和使用泛型<0}

{0>Generics provide a facility for creating types that have type parameters.<}0{>泛型提供建立具有型別參數之型別的功能。<0} {0>The example below declares a generic Stack class with a type parameter T.<}0{>下列範例使用型別參數 T 宣告泛型 Stack 類別。<0} {0>The type parameter is specified in < and > delimiters after the class name.<}0{>該型別參數是在類別名稱後的 < 和 > 分隔符號 (Delimiter) 中指定的。<0} {0>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.<}0{>Stack 的執行個體不會強制從 object 轉換或轉換成 object,反而會接受其建立時所用的型別,並儲存該型別的資料,而不經過轉換。<0} {0>The type parameter T acts as a placeholder until an actual type is specified at use.<}0{>在指定要使用的實際型別之前,型別參數 T 會做為替代符號 (Placeholder) 使用。<0} {0>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:<}0{>請注意,T 是用來做為內部 items 陣列的元素型別、Push 方法的參數型別,以及 Pop 方法的傳回型別:<0}

public class Stack{

T[] items;

int count;

public void Push(T item) {...}

public T Pop() {...}}

{0>When the generic class Stack is used, the actual type to substitute for T is specified.<}0{>使用泛型類別 Stack 時,會指定用於取代 T 的實際型別。<0} {0>In the following example, int is given as the type argument for T:<}0{>在下列範例中,int 即為 T 的型別引數:<0}

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

{0>The Stack type is called a constructed type.<}0{>Stack 型別稱為建構的型別。<0} {0>In the Stack type, every occurrence of T is replaced with the type argument int.<}0{>在 Stack 型別中,每個出現的 T 都會由 int 型別引數取代。<0} {0>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.<}0{>在建立 Stack 的執行個體時,items 陣列的原生儲存區 (Native Storage) 是 int[],而不是 object[],可提供相當於非泛型 Stack 的良好儲存效能。<0} {0>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.<}0{>同樣的,Stack 的 Push 和 Pop 方法會在 int 值上作業,將推送其他型別值到堆疊上的作業成為編譯時期錯誤,並排除在擷取值時必須明確轉換回原始型別的需求。<0}

{0>Generics provide strong typing, meaning for example that it is an error to push an int onto a stack of Customer objects.<}0{>泛型提供強式型別,例如推送 int 到 Customer 物件的堆疊就是個錯誤。<0} {0>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:<}0{>如同 Stack 限制只能在 int 值上作業,Stack 也只能在 Customer 物件上作業,並且編譯器 (Compiler) 將針對下列範例的最後兩行回報錯誤:<0}

Stack stack = new Stack();stack.Push(new Customer());Customer c = stack.Pop();stack.Push(3);

// Type mismatch errorint x = stack.Pop();

// Type mismatch error

{0>Generic type declarations can have any number of type parameters.<}0{>泛型型別宣告 (Type Declaration) 可以具有任意數目的型別參數。<0} {0>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:<}0{>上述的 Stack 範例只有一個型別參數,但泛型 Dictionary 類別可以有兩個型別參數,一個供索引鍵的型別使用,一個供值的型別使用:<0}

public class Dictionary{

public void Add(K key, V value) {...}

public V this[K key] {...}}

{0>When Dictionary is used, two type arguments would have to be supplied:<}0{>使用 Dictionary 時,必須提供兩個型別引數:<0}

Dictionary dict = new Dictionary();dict.Add("Peter", new Customer());Customer c = dict["Peter"];

19.1.3 {0>Generic type instantiations<}0{>泛型型別執行個體化<0}

{0>Similar to a non-generic type, the compiled representation of a generic type is intermediate language (IL) instructions and metadata.<}0{>與非泛型型別相似,泛型型別的編譯表示為中繼語言 (Intermediate Language,IL) 指令和中繼資料 (Metadata)。<0} {0>The representation of the generic type of course also encodes the existence and use of type parameters.<}0{>泛型型別的表示當然也會為型別參數的存在和使用進行編碼。<0}

{0>The first time an application creates an instance of a constructed generic type, such as Stack, the just-in-time (JIT) compiler of the .NET Common Language Runtime converts the generic IL and metadata to native code, substituting actual types for type parameters in the process.<}0{>應用程式第一次建立建構的泛型型別執行個體 (例如 Stack) 時,.NET Common Language Runtime 的 Just-in-Time (JIT) 編譯器會將泛型 IL 和中繼資料轉換為機器碼,並將處理序 (Process) 中的型別參數取代為實際型別。<0} {0>Subsequent references to that constructed generic type then use the same native code.<}0{>接著,該建構泛型型別的後續參考會使用相同的機器碼。<0} {0>The process of creating a specific constructed type from a generic type is known as a generic type instantiation.<}0{>從泛型型別建立特定建構型別的程序,稱為泛型型別執行個體化。<0}

{0>The .NET Common Language Runtime creates a specialized copy of the native code for each generic type instantiation with a value type, but shares a single copy of the native code for all reference types (since, at the native code level, references are just pointers with the same representation).<}0{>.NET Common Language Runtime 會為每個具有實值型別的泛型型別執行個體化建立一個特別的機器碼複本,但是所有的參考型別只共用單一機器碼複本 (因為在機器碼層級,參考只是具有相同表示的指標而已)。<0}

19.1.4 {0>Constraints<}0{>條件約束<0}

{0>Commonly, a generic class will do more than just store data based on a type parameter.<}0{>一般來說,泛型類別的功能不只是以型別參數為基礎來儲存資料而已。<0} {0>Often, the generic class will want to invoke methods on objects whose type is given by a type parameter.<}0{>通常,泛型類別會在由型別參數指定型別的物件上叫用 (Invoke) 方法。<0} {0>For example, an Add method in a Dictionary class might need to compare keys using a CompareTo method:<}0{>例如,Dictionary 類別中的 Add 方法可能必須使用 CompareTo 方法來比較索引鍵:<0}

public class Dictionary{

public void Add(K key, V value)

{

...

if (key.CompareTo(x) < 0) {...}

// Error, no CompareTo method

...

}}

{0>Since the type argument specified for K could be any type, the only members that can be assumed to exist on the key parameter are those declared by type object, such as Equals, GetHashCode, and ToString; a compile-time error therefore occurs in the example above.<}0{>由於指定給 K 的型別引數可以是任何型別,所以,只有那些由 object 型別宣告的成員才是假設可以存在於 key 參數上的唯一成員,例如 Equals、GetHashCode 和 ToString,因此上述範例會產生編譯時期錯誤。<0} {0>It is of course possible to cast the key parameter to a type that contains a CompareTo method.<}0{>當然,也可以將 key 參數轉換成包含 CompareTo 方法的型別。<0} {0>For example, the key parameter could be cast to IComparable:<}0{>例如,key 參數可以轉換成 IComparable:<0}

public class Dictionary{

public void Add(K key, V value)

{

...

if (((IComparable)key).CompareTo(x) < 0) {...}

...

}}

{0>While this solution works, it requires a dynamic type check at run-time, which adds overhead.<}0{>雖然這個方案可行,但它必須在執行階段執行動態型別檢查,而這會增加額外的負荷。<0} {0>It furthermore defers error reporting to run-time, throwing an InvalidCastException if a key doesn’t implement IComparable.<}0{>此外,它還會將錯誤報告延後到執行階段,如果索引鍵未實作 IComparable,便會擲回 InvalidCastException。<0}

{0>To provide stronger compile-time type checking and reduce type casts, C# permits an optional list of constraints to be supplied for each type parameter.<}0{>為了提供功能更佳的編譯時期型別檢查,並減少型別轉換,C# 允許為每個型別參數提供一個選擇性條件約束 (Constraint) 清單。<0} {0>A type parameter constraint specifies a requirement that a type must fulfill in order to be used as an argument for that type parameter.<}0{>型別參數條件約束指定了需求,型別必須滿足該需求,才能當做該型別參數的引數使用。<0} {0>Constraints are declared using the word where, followed by a type parameter and a colon, followed by a comma-separated list of class types, interface types, and type parameters (or the special reference type, value type, and constructor constraints{1>xe "constraint:constructor"<1}).<}0{>條件約束的宣告,是使用 where 文字,後面接著型別參數和冒號,再加上類別型別、介面型別和型別參數 (或特殊參考型別、實值型別和建構函式 (Constructor) 條件約束{1>xe "constraint:constructor"<1}) 的逗號分隔清單。<0}

{0>In order for the Dictionary class to ensure that keys always implement IComparable, the class declaration can specify a constraint for the type parameter K:<}0{>為了讓 Dictionary 類別確保索引鍵一定會實作 IComparable,類別宣告 (Class Declaration) 可以針對型別參數 K 指定條件約束:<0}

public class Dictionary where K: IComparable{

public void Add(K key, V value)

{

...

if (key.CompareTo(x) < 0) {...}

...

}}

{0>Given this declaration the compiler will ensure that any type argument supplied for K is a type that implements IComparable.<}0{>指定這個宣告之後,編譯器會確保所有提供給 K 的型別引數都是實作 IComparable 的型別。<0} {0>Furthermore, it is no longer necessary to explicitly cast the key parameter to IComparable before calling the CompareTo method; all members of a type given as a constraint for a type parameter are directly available on values of that type parameter type.<}0{>此外,也不再需要在呼叫 CompareTo 方法之前,將索引鍵參數明確轉換成 IComparable,因為指定做為型別參數條件約束之型別的所有成員,都可以直接在該型別參數的值上使用。<0}

{0>For a given type parameter, it is possible to specify any number of interfaces and type parameters as constraints, but no more than one class.<}0{>對於指定的型別參數,則可以指定任意數目的介面和型別參數做為條件約束,但不能指定一個以上的類別。<0} {0>Each constrained type parameter has a separate where clause.<}0{>每個具有條件約束的型別參數都有不同的 where 子句。<0} {0>In the example below, the type parameter K has two interface constraints, while the type parameter E has a class type constraint and a constructor constraint:<}0{>在下列範例中,型別參數 K 有兩個介面條件約束,而型別參數 E 會有一個類別型別條件約束和一個建構函式條件約束:<0}

public class EntityTable

where K: IComparable, IPersistable

where E: Entity, new(){

public void Add(K key, E entity)

{

...

if (key.CompareTo(x) < 0) {...}

...

}}

{0>The constructor constraint, new(), in the example above ensures that a type used as a type argument for E has a public, parameterless constructor, and it permits the generic class to use new E() to create instances of that type.<}0{>上述範例中的建構函式條件約束 new(),會確保做為 E 之型別引數使用的型別,具有公用的 (Public) 無參數建構函式,並且此條件約束允許泛型類別使用 new E() 建立該型別的執行個體。<0}

{0>Type parameter constrains should be used with care.<}0{>請小心使用型別參數條件約束。<0} {0>While they provide stronger compile-time type checking and in some cases improve performance, they also restrict the possible uses of a generic type.<}0{>雖然這種條件約束能夠提供功能更佳的編譯時期型別檢查,並且在某些情況下可以改善效能,卻也限制了泛型型別的可能用法。<0} {0>For example, a generic class List might constrain T to implement IComparable such that the list’s Sort method can compare items.<}0{>例如,泛型類別 List 可能會限制 T 實作 IComparable,使清單的 Sort 方法可以比較項目。<0} {0>However, doing so would preclude use of List for types that don’t implement IComparable, even if the Sort method is never actually called in those cases.<}0{>但是,這麼做會使未實作 IComparable 的型別無法使用 List,即使在那些情況下並未實際呼叫 Sort 方法也一樣。<0}

19.1.5 {0>Generic methods<}0{>泛型方法<0}

{0>In some cases a type parameter is not needed for an entire class, but only inside a particular method.<}0{>在某些情況下,並不是整個類別都需要某個型別參數,而是只有特定方法需要它。<0} {0>Often, this occurs when creating a method that takes a generic type as a parameter.<}0{>這通常發生在建立需要泛型型別做為參數的方法。<0} {0>For example, when using the Stack class described earlier, a common pattern might be to push multiple values in a row, and it might be convenient to write a method that does so in a single call.<}0{>例如,使用先前所述的 Stack 類別時,常見的模式可能是將多個值推送至某個資料列,此時撰寫可在單一呼叫中完成這個動作的方法會很方便。<0} {0>For a particular constructed type, such as Stack, the method would look like this:<}0{>對於特定的建構型別 (例如 Stack),方法可能撰寫成:<0}

void PushMultiple(Stack stack, params int[] values) {

foreach (int value in values) stack.Push(value);}

{0>This method can be used to push multiple int values onto a Stack:<}0{>這個方法可以用來將多個 int 值推送到 Stack:<0}

Stack stack = new Stack();PushMultiple(stack, 1, 2, 3, 4);

{0>However, the method above only works with the particular constructed type Stack.<}0{>但是,上述方法只能用在特定的建構型別 Stack。<0} {0>To have it work with any Stack, the method must be written as a generic method.<}0{>若要用在任何 Stack,這個方法必須撰寫為泛型方法。<0} {0>A generic method has one or more type parameters specified in < and > delimiters after the method name.<}0{>泛型方法在方法名稱之後,具有一或多個在 < 和 > 分隔符號中指定的型別參數。<0} {0>The type parameters can be used within the parameter list, return type, and body of the method.<}0{>型別參數可以用於該方法的參數清單、傳回型別和主體中。<0} {0>A generic PushMultiple method would look like this:<}0{>泛型 PushMultiple 方法可能撰寫成:<0}

void PushMultiple(Stack stack, params T[] values) {

foreach (T value in values) stack.Push(value);}

{0>Using this generic method, it is possible to push multiple items onto any Stack.<}0{>使用這個泛型方法,可以將多個項目推送到任何 Stack。<0} {0>When calling a generic method, type arguments are given in angle brackets in the method invocation.<}0{>在呼叫泛型方法時,型別參數是在方法引動過程的角括弧中指定的。<0}{0>For example:<}100{>例如:<0}

Stack stack = new Stack();PushMultiple(stack, 1, 2, 3, 4);

{0>This generic PushMultiple method is more reusable than the previous version, since it works on any Stack, but it appears to be less convenient to call, since the desired T must be supplied as a type argument to the method.<}0{>這個泛型 PushMultiple 方法的可重複使用性比先前版本來得高,因為它在任何 Stack 上都可使用,但是由於所需的 T 必須指定為方法的型別引數,所以可能比較不方便呼叫。<0} {0>In many cases, however, the compiler can deduce the correct type argument from the other arguments passed to the method, using a process called type inferencing.<}0{>然而,在很多情況下,編譯器可以使用稱為型別推斷的處理序,從其他傳送到方法的引數推算正確的型別引數。<0} {0>In the example above, since the first regular argument is of type Stack, and the subsequent arguments are of type int, the compiler can reason that the type parameter must be int.<}0{>在上述範例中,由於第一個規則引數是 Stack 型別,且後續的引數是 int 型別,編譯器可以推論出型別參數必定是 int。<0} {0>Thus, the generic PushMultiple method can be called without specifying the type parameter:<}0{>因此,不需要指定型別參數,就可以呼叫泛型 PushMultiple 方法:<0}

Stack stack = new Stack();PushMultiple(stack, 1, 2, 3, 4);

19.2 {0>Anonymous methods<}0{>匿名方法<0}

{0>Event handlers and other callbacks are often invoked exclusively through delegates and never directly.<}0{>事件處理常式和其他回呼 (Callback) 通常是透過委派以獨占方式叫用,且永遠不會直接叫用。<0} {0>Even so, it has thus far been necessary to place the code of event handlers and callbacks in distinct methods to which delegates are explictly created.<}0{>即使如此,還是必須將事件處理常式的程式碼和回呼放在明確建立委派的不同方法中。<0} {0>In contrast, anonymous methods allow the code associated with a delegate to be written “in-line” where the delegate is used, conveniently tying the code directly to the delegate instance.<}0{>相反地,匿名方法允許與委派關聯的程式碼「內嵌」在使用委派的地方,以便將程式碼直接結合到委派執行個體。<0} {0>Besides this convenience, anonymous methods have shared access to the local state of the containing function member.<}0{>除了這項方便性之外,匿名方法具有包含函式成員之本機狀態的共用存取。<0} {0>To achieve the same state sharing using named methods requires “lifting” local variables into fields in instances of manually authored helper classes.<}0{>若要使用具名方法達到相同的狀態共用,必須將區域變數「提昇」到手動撰寫之 Helper 類別執行個體中的欄位。<0}

{0>The following example shows a simple input form that contains a list box, a text box, and a button.<}0{>下列範例顯示包含清單方塊、文字方塊和按鈕的簡易輸入表單。<0} {0>When the button is clicked, an item containing the text in the text box is added to the list box.<}0{>按下按鈕時,包含文字方塊中之文字的項目就會加入至清單方塊。<0}

class InputForm: Form{

ListBox listBox;

TextBox textBox;

Button addButton;

public MyForm() {

listBox = new ListBox(...);

textBox = new TextBox(...);

addButton = new Button(...);

addButton.Click += new EventHandler(AddClick);

}

void AddClick(object sender, EventArgs e) {

listBox.Items.Add(textBox.Text);

}}

{0>Even though only a single statement is executed in response to the button’s Click event, that statement must be extracted into a separate method with a full parameter list, and an EventHandler delegate referencing that method must be manually created.<}0{>即使只會執行單一陳述式以回應按鈕的 Click 事件,仍必須利用完整的參數清單將該陳述式抽取至不同方法,並且必須以手動方式建立參考該方法的 EventHandler 委派。<0} {0>Using an anonymous method, the event handling code becomes significantly more succinct:<}0{>若是使用匿名方法,事件處理程式碼會明顯變得簡潔:<0}

class InputForm: Form{

ListBox listBox;

TextBox textBox;

Button addButton;

public MyForm() {

listBox = new ListBox(...);

textBox = new TextBox(...);

addButton = new Button(...);

addButton.Click += delegate {

listBox.Items.Add(textBox.Text);

};

}}

{0>An anonymous method consists of the keyword delegate, an optional parameter list, and a statement list enclosed in { and } delimiters.<}0{>匿名方法由包含在 { 和 } 分隔符號中的關鍵字 delegate、選擇性參數清單及陳述式清單所組成。<0} {0>The anonymous method in the previous example doesn’t use the parameters supplied by the delegate, and it can therefore omit the parameter list.<}0{>先前範例中的匿名方法未使用委派提供的參數,因此可以省略參數清單。<0} {0>To gain access to the parameters, the anonymous method can include a parameter list:<}0{>若要取得參數的存取權,匿名方法也可以包含參數清單:<0}

addButton.Click += delegate(object sender, EventArgs e) {

MessageBox.Show(((Button)sender).Text);};

{0>In the previous examples, an implicit conversion occurs from the anonymous method to the EventHandler delegate type (the type of the Click event).<}0{>在先前範例中,發生從匿名方法到 EventHandler 委派型別 (Click 事件的型別) 的隱含轉換。<0} {0>This implict conversion is possible because the parameter list and return type of the delegate type are compatible with the anonymous method.<}0{>因為委派型別的參數清單和傳回型別與匿名方法相容,所以有可能發生這個隱含轉換。<0} {0>The exact rules for compatibility are as follows:<}0{>相容性的確切規則如下:<0}

· {0>The parameter list of a delegate is compatible with an anonymous method if one of the following is true:<}0{>若下列任一條件成立,委派的參數清單就與匿名方法相容:<0}

· {0>The anonymous method has no parameter list and the delegate has no out parameters.<}0{>匿名方法沒有參數清單,並且委派沒有 out 參數。<0}

· {0>The anonymous method includes a parameter list that exactly matches the delegate’s parameters in number, types, and modifiers.<}0{>匿名方法所包含的參數清單,在數量、型別和修飾詞 (Modifier) 上完全與委派的參數相符。<0}

· {0>The return type of a delegate is compatible with an anonymous method if one of the following is true:<}0{>若下列任一條件成立,委派的傳回型別就與匿名方法相容:<0}

· {0>The delegate’s return type is void and the anonymous method has no return statements or only return statements with no expression.<}0{>委派的傳回型別為 void,且匿名方法沒有 return 陳述式,或是只有沒有運算式的 return 陳述式。<0}

· {0>The delegate’s return type is not void and the expressions associated with all return statements in the anonymous method can be implicitly converted to the return type of the delegate.<}0{>委派的傳回型別不是 void,且匿名方法中與所有 return 陳述式關聯的運算式都可以隱含轉換為委派的傳回型別。<0}

{0>Both the parameter list and the return type of a delegate must be compatible with an anonymous method before an implicit conversion to that delegate type can occur.<}0{>委派的參數清單和傳回型別必須都與匿名方法相容,才會發生轉換至該委派型別的隱含轉換。<0}

{0>The following example uses anonymous methods to write functions “in-line.”<}0{>下列範例使用匿名方法以「內嵌」方式撰寫函式。<0} {0>The anonymous methods are passed as parameters of a Function delegate type.<}0{>匿名方法會當做 Function 委派型別的參數傳遞。<0}

using System;

delegate double Function(double x);

class Test{

static double[] Apply(double[] a, Function f) {

double[] result = new double[a.Length];

for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);

return result;

}

static double[] MultiplyAllBy(double[] a, double factor) {

return Apply(a, delegate(double x) { return x * factor; });

}

static void Main() {

double[] a = {0.0, 0.5, 1.0};

double[] squares = Apply(a, delegate(double x) { return x * x; });

double[] doubles = MultiplyAllBy(a, 2.0);

}}

{0>The Apply method applies a given Function to the elements of a double[], returning a double[] with the results.<}100{>Apply 方法會將指定的 Function 套用到 double[] 的元素,傳回具有結果的 double[]。<0} {0>In the Main method, the second parameter passed to Apply is an anonymous method that is compatible with the Function delegate type.<}0{>在 Main 方法中,傳遞到 Apply 的第二個參數是,與 Function 委派型別相容的匿名方法。<0} {0>The anonymous method simply returns the square of its argument, and thus the result of that Apply invocation is a double[] containing the squares of the values in a.<}0{>匿名方法只會傳回其引數的平方,因此該 Apply 引動過程的結果為包含 a 中值的平方的 double[]。<0}

{0>The MultiplyAllBy method returns a double[] created by multiplying each of the values in the argument array a by a given factor.<}0{>MultiplyAllBy 方法會傳回將引數陣列 a 中的各值乘以指定 factor 所建立的 double[]。<0} {0>In order to produce its result, MultiplyAllBy invokes the Apply method, passing an anonymous method that multiplies the argument x by factor.<}0{>為了產生其結果,MultiplyAllBy 會叫用 Apply 方法,以傳遞將引數 x 乘以 factor 的匿名方法。<0}

{0>Local variables and parameters whose scope contains an anonymous method are called outer variables of the anonymous method.<}0{>範圍包含匿名方法的區域變數和參數稱為匿名方法的外部變數。<0} {0>In the MultiplyAllBy method, a and factor are outer variables of the anonymous method passed to Apply, and because the anonymous method references factor, factor is said to have been captured by the anonymous method.<}0{>在 MultiplyAllBy 方法中,a 和 factor 是傳遞到 Apply 之匿名方法的外部變數,因為該匿名方法參考 factor,所以 factor 可以說成已由該匿名方法擷取。<0} {0>Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated.<}0{>通常,區域變數的存留期 (Lifetime) 限制為只能執行與其關聯的區塊或陳述式。<0} {0>However, the lifetime of a captured outer variable is extended at least until the delegate referring to the anonymous method becomes eligible for garbage collection.<}0{>但是,擷取之外部變數的存留期至少會延伸到參考該匿名方法的委派變成可進行記憶體回收為止。<0}

19.2.1 {0>Method group conversions<}0{>方法群組轉換<0}

{0>As described in the previous section, an anonymous method can be implicitly converted to a compatible delegate type.<}0{>如前面章節所述,匿名方法可以隱含轉換成相容的委派型別。<0} {0>C# 2.0 permits this same type of conversion for a method group, allowing explicit delegate instantiations to be omitted in almost all cases.<}0{>C# 2.0 允許方法群組使用相同的型別轉換,使得幾乎在所有的情況下,都能省略明確的委派執行個體化。<0}{0>For example, the statements<}94{>例如,下列陳述式<0}

addButton.Click += new EventHandler(AddClick);

Apply(a, new Function(Math.Sin));

{0>can instead be written<}0{>可以改寫為<0}

addButton.Click += AddClick;

Apply(a, Math.Sin);

{0>When the shorter form is used, the compiler automatically infers which delegate type to instantiate, but the effects are otherwise the same as the longer form.<}0{>使用較簡短的形式時,編譯器會自動推斷要執行個體化的委派型別,但效果卻與較長形式相同。<0}

19.3 {0>Iterators<}0{>Iterator<0}

{0>The C# foreach statement is used to iterate over the elements of an enumerable collection.<}0{>C# foreach 陳述式是用來反覆查看可列舉集合的項目。<0} {0>In order to be enumerable, a collection must have a parameterless GetEnumerator method that returns an enumerator.<}0{>為了變成可列舉,集合必須具有會傳回列舉值的無參數 GetEnumerator 方法。<0} {0>Generally, enumerators are difficult to implement, but the task is significantly simplified with iterators.<}0{>一般而言,列舉值很難實作,但使用 Iterator 可大幅簡化這項工作。<0}

{0>An iterator is a statement block that yields an ordered sequence of values.<}0{>Iterator 是指會產生次序排列之值的陳述式區塊。<0} {0>An iterator is distinguished from a normal statement block by the presence of one or more yield statements:<}0{>Iterator 與一般陳述式區塊的不同之處在於,它有一或多個 yield 陳述式:<0}

· {0>The yield return statement produces the next value of the iteration.<}0{>yield return 陳述式會產生反覆運算的下一個值。<0}

· {0>The yield break statement indicates that the iteration is complete.<}0{>yield break 陳述式會指出反覆運算已完成。<0}

{0>An iterator can be used as the body of a function member as long as the return type of the function member is one of the enumerator interfaces or one of the enumerable interfaces:<}0{>只要函式成員的傳回型別是其中一個列舉值介面或可列舉介面,Iterator 就可以做為函式成員的主體:<0}

· {0>The enumerator interfaces are System.Collections.IEnumerator and types constructed from System.Collections.Generic.IEnumerator.<}0{>列舉值介面為 System.Collections.IEnumerator 以及從 System.Collections.Generic.IEnumerator 建構的型別。<0}

· {0>The enumerable interfaces are System.Collections.IEnumerable and types constructed from System.Collections.Generic.IEnumerable.<}0{>可列舉介面為 System.Collections.IEnumerable 以及從 System.Collections.Generic.IEnumerable 建構的型別。<0}

{0>It is important to understand that an iterator is not a kind of member, but is a means of implementing a function member.<}0{>請務必瞭解 Itereator 不是一種成員,而是一種實作函式成員的方式。<0} {0>A member implemented via an iterator can be overridden or overloaded by other members which may or may not be implemented with iterators.<}0{>透過 Iterator 實作的成員可以由其他成員覆寫或多載,而這些成員不一定會使用 Iterator 實作。<0}

{0>The following Stack class implements its GetEnumerator method using an iterator.<}0{>下列 Stack 類別使用 Iterator 實作其 GetEnumerator 方法。<0} {0>The iterator enumerates the elements of the stack in top to bottom order.<}0{>Iterator 會以由上至下的順序列舉堆疊的項目。<0}

using System.Collections.Generic;

public class Stack: IEnumerable{

T[] items;

int count;

public void Push(T data) {...}

public T Pop() {...}

public IEnumerator GetEnumerator() {

for (int i = count – 1; i >= 0; --i) {

yield return items[i];

}

}}

{0>The presence of the GetEnumerator method makes Stack an enumerable type, allowing instances of Stack to be used in a foreach statement.<}0{>GetEnumerator 方法的出現會使 Stack 成為可列舉型別,以允許 Stack 的執行個體用在 foreach 陳述式中。<0} {0>The following example pushes the values 0 through 9 onto an integer stack and then uses a foreach loop to display the values in top to bottom order.<}0{>下列範例會將 0 到 9 的值推送到整數堆疊,然後使用 foreach 迴圈 (Loop) 以由上至下的順序顯示這些值。<0}

using System;

class Test{

static void Main() {

Stack stack = new Stack();

for (int i = 0; i < 10; i++) stack.Push(i);

foreach (int i in stack) Console.Write("{0} ", i);

Console.WriteLine();

}}

{0>The output of the example is:<}84{>此範例的輸出為:<0}

9 8 7 6 5 4 3 2 1 0

{0>The foreach statement implicitly calls a collection’s parameterless GetEnumerator method to obtain an enumerator.<}0{>foreach 陳述式會隱含呼叫集合的無參數 GetEnumerator 方法,以取得列舉值。<0} {0>There can only be one such parameterless GetEnumerator method defined by a collection, yet it is often appropriate to have multiple ways of enumerating, and ways of controlling the enumeration through parameters.<}0{>這種由集合所定義的無參數 GetEnumerator 方法只能有一個,但通常有很多適當的列舉方式,以及透過參數控制列舉的方式。<0} {0>In such cases, a collection can use iterators to implement properties or methods that return one of the enumerable interfaces.<}0{>在這種情況下,集合可以使用 Iterator 實作屬性或方法,以便傳回其中一個可列舉介面。<0} {0>For example, Stack might introduce two new properties, TopToBottom and BottomToTop, of type IEnumerable:<}0{>例如,Stack 可以引入兩個 IEnumerable 型別的新屬性 TopToBottom 和 BottomToTop:<0}

using System.Collections.Generic;

public class Stack: IEnumerable{

T[] items;

int count;

public void Push(T data) {...}

public T Pop() {...}

public IEnumerator GetEnumerator() {

for (int i = count – 1; i >= 0; --i) {

yield return items[i];

}

}

public IEnumerable TopToBottom {

get {

return this;

}

}

public IEnumerable BottomToTop {

get {

for (int i = 0; i < count; i++) {

yield return items[i];

}

}

}}

{0>The get accessor for the TopToBottom property just returns this since the stack itself is an enumerable.<}0{>TopToBottom 屬性的 get 存取子只會傳回 this,因為此堆疊本身為可列舉值。<0} {0>The BottomToTop property returns an enumerable implemented with a C# iterator.<}0{>BottomToTop 屬性會傳回使用 C# Iterator 實作的可列舉值。<0} {0>The following example shows how the properties can be used to enumerate stack elements in either order:<}0{>下列範例顯示如何使用屬性以其中一種順序列舉堆疊項目:<0}

using System;

class Test{

static void Main() {

Stack stack = new Stack();

for (int i = 0; i < 10; i++) stack.Push(i);

foreach (int i in stack.TopToBottom) Console.Write("{0} ", i);

Console.WriteLine();

foreach (int i in stack.BottomToTop) Console.Write("{0} ", i);

Console.WriteLine();

}}

{0>Of course, these properties can be used outside of a foreach statement as well.<}0{>當然,這些屬性也可以在 foreach 陳述式外使用。<0} {0>The following example passes the results of invoking the properties to a separate Print method.<}0{>下列範例會將叫用屬性的結果傳遞至不同的 Print 方法。<0} {0>The example also shows an iterator used as the body of a FromToBy method that takes parameters:<}0{>此範例也會顯示做為使用參數之 FromToBy 方法主體的 Iterator:<0}

using System;using System.Collections.Generic;

class Test{

static void Print(IEnumerable collection) {

foreach (int i in collection) Console.Write("{0} ", i);

Console.WriteLine();

}

static IEnumerable FromToBy(int from, int to, int by) {

for (int i = from; i <= to; i += by) {

yield return i;

}

}

static void Main() {

Stack stack = new Stack();

for (int i = 0; i < 10; i++) stack.Push(i);

Print(stack.TopToBottom);

Print(stack.BottomToTop);

Print(FromToBy(10, 20, 2));

}}

{0>The output of the example is:<}100{>此範例的輸出為:<0}

9 8 7 6 5 4 3 2 1 00 1 2 3 4 5 6 7 8 910 12 14 16 18 20

{0>The generic and non-generic enumerable interfaces contain a single member, a GetEnumerator method that takes no arguments and returns an enumerator interface.<}0{>泛型和非泛型的可列舉介面包含單一成員,這個成員是不使用引數並傳回列舉值介面的 GetEnumerator 方法。<0} {0>An enumerable acts as an enumerator factory.<}0{>可列舉值會當做「列舉值處理站」(Enumerator Factory) 使用。<0} {0>Properly implemented enumerables generate independent enumerators each time their GetEnumerator method is called.<}0{>適當實作的可列舉值在每次呼叫其 GetEnumerator 方法時,都會產生獨立的列舉值。<0} {0>Assuming the internal state of the enumerable has not changed between two calls to GetEnumerator, the two enumerators returned should produce the same set of values in the same order.<}0{>假設可列舉值的內部狀態在兩次對 GetEnumerator 的呼叫之間沒有變更,這兩個傳回的列舉值應該會以相同的順序產生同一組值。<0} {0>This should hold even if the lifetime of the enumerators overlap as in the following code sample:<}0{>即使列舉值的存留期重疊,這仍成立,如下列程式碼範例所示:<0}

using System;using System.Collections.Generic;

class Test{

static IEnumerable FromTo(int from, int to) {

while (from <= to) yield return from++;

}

static void Main() {

IEnumerable e = FromTo(1, 10);

foreach (int x in e) {

foreach (int y in e) {

Console.Write("{0,3} ", x * y);

}

Console.WriteLine();

}

}}

{0>The code above prints a simple multiplication table of the integers 1 through 10. Note that the FromTo method is invoked only once to generate the enumerable e.<}0{>上述程式碼會列印出整數 1 到 10 的簡易乘法表。請注意,FromTo 方法只有叫用一次,用於產生可列舉值 e。<0} {0>However, e.GetEnumerator() is invoked multiple times (by the foreach statements) to generate multiple equivalent enumerators.<}0{>然而,e.GetEnumerator() 會叫用多次 (由 foreach 陳述式叫用),以產生多個相等的列舉值。<0} {0>These enumerators all encapsulate the iterator code specified in the declaration of FromTo.<}0{>這些列舉值都會封裝在 FromTo 宣告指定的 Iterator 程式碼中。<0} {0>Note that the iterator code modifies the from parameter.<}0{>請注意,該 Iterator 程式碼會修改 from 參數。<0} {0>Nevertheless, the enumerators act independently because each enumerator is given its own copy of the from and to parameters.<}0{>不過,列舉值是獨立作用的,因為每個列舉值都會有「自己的」from 和 to 參數。<0} {0>The sharing of transient state between enumerators is one of several common subtle flaws that should be avoided when implementing enumerables and enumerators.<}0{>在各列舉值間共用暫時性 (Transient) 狀態,是實作可列舉值和列舉值時應避免的幾個常見小缺點之一。<0} {0>C# iterators are designed to help avoid these problems and to implement robust enumerables and enumerators in a simple intuitive way.<}0{>C# Iterator 設計的目的,就是要協助避免這些問題,以及用簡單直覺的方式來實作穩固的可列舉值和列舉值。<0}

19.4 {0>Partial types<}0{>部分型別<0}

{0>While it is good programming practice to maintain all source code for a type in a single file, sometimes a type becomes large enough that this is an impractical constraint.<}0{>雖然在單一檔案中維護型別的所有原始程式碼是良好的程式設計行為,但有時型別會變得很大,以致於無法實行這樣的方式。<0} {0>Furthermore, programmers often use source code generators to produce the initial structure of an application, and then modify the resulting code.<}0{>此外,程式設計人員常會使用原始程式碼產生器來產生應用程式的初始結構,然後再修改產生的程式碼。<0} {0>Unfortunately, when source code is emitted again sometime in the future, existing modifications are overwritten.<}0{>不巧的是,如果日後再次發出原始程式碼,現有的修改就會遭到覆寫。<0}

{0>Partial types allow classes, structs, and interfaces to be broken into multiple pieces stored in different source files for easier development and maintenance.<}99{>部分型別允許類別、結構和介面分為數個部分,並儲存在不同的原始程式檔中,以便用於開發和維護。<0} {0>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.<}100{>此外,部分型別還允許將型別的電腦產生部分和使用者撰寫部分分開,以便更容易地擴大工具所產生的程式碼。<0}

{0>A new type modifier, partial, is used when defining a type in multiple parts.<}0{>在多個部分中定義型別時,就會使用新的型別修飾詞 (Type Modifier) partial。<0} {0>The following is an example of a partial class that is implemented in two parts.<}0{>下列是在兩個部分中實作的部分類別範例。<0} {0>The two parts may be in different source files, for example because the first part is machine generated by a database mapping tool and the second part is manually authored:<}0{>這兩個部分可能位於不同的原始程式檔中,舉例來說,第一個部分是由資料庫對應工具產生的電腦,而第二個部分是手動撰寫的:<0}

public partial class Customer{

private int id;

private string name;

private string address;

private List orders;

public Customer() {

...

}}

public partial class Customer{

public void SubmitOrder(Order order) {

orders.Add(order);

}

public bool HasOutstandingOrders() {

return orders.Count > 0;

}}

{0>When the two parts above are compiled together, the resulting code is the same as if the class had been written as a single unit:<}0{>當上述兩個部分一起編譯時,產生的結果就好像該類別已撰寫做為單一單位一樣:<0}

public class Customer{

private int id;

private string name;

private string address;

private List orders;

public Customer() {

...

}

public void SubmitOrder(Order order) {

orders.Add(order);

}

public bool HasOutstandingOrders() {

return orders.Count > 0;

}}

{0>All parts of a partial type must be compiled together such that the parts can be merged at compile-time.<}0{>部分型別的所有部分都必須一起編譯,如此,這些部分就可以在編譯時期合併。<0} {0>Partial types specifically do not allow already compiled types to be extended.<}0{>特別的是,部分型別不允許擴充已編譯的型別。<0}

19.5 {0>Nullable types<}0{>可為 Null 的型別<0}

{0>Support for nullability across all types, including value types, is essential when interacting with databases, yet general purpose programming languages have historically provided little or no support in this area.<}0{>與資料庫進行互動時,對所有型別 (包括實值型別) 支援可為 Null 的特性是很重要的,但是過去一般用途的程式設計語言卻一直沒有或很少提供這方面的支援。<0} {0>Many approaches exist for handling nulls and value types without direct language support, but all have shortcomings.<}0{>許多現有處理 null 和實值型別的方法都沒有直接的語言支援,且都有缺點。<0} {0>For example, one approach is to use a “special” value (such as −1 for integers) to indicate null, but this only works when an unused value can be identified.<}0{>例如,有一個方法是使用「特殊的」值 (例如,−1 代表整數) 表示 null,但這個方法只能在可以識別未使用的值時使用。<0} {0>Another approach is to maintain boolean null indicators in separate fields or variables, but this doesn’t work well for parameters and return values.<}0{>另一個方法是,在不同的欄位或變數中維護布林 (Boolean) null 指示器 (Indicator),但這用在參數和傳回值的效果並不好。<0} {0>A third approach is to use a set of user-defined nullable types, but this only works for a closed set of types.<}0{>第三個方法是使用一組使用者定義的可為 Null 的型別,但這只能用於關閉型別組合。<0} {0>C#’s nullable types solve this long standing problem by providing complete and integrated support for nullable forms of all value types.<}0{>C# 的可為 Null 的型別藉由針對所有實值型別之可為 Null 的形式提供完整及整合的支援,解決了這個長久以來存在的問題。<0}

{0>Nullable types are constructed using the ? type modifier.<}0{>可為 Null 的型別是使用 ? 型別修飾詞建構的。<0} {0>For example, int? is the nullable form of the predefined type int.<}0{>例如,int? 是預先定義之型別 int 的可為 Null 形式。<0} {0>A nullable type’s underlying type must be a non-nullable value type.<}0{>可為 Null 型別的基礎型別必須是不可為 Null 的實值型別。<0}

{0>A nullable type is a structure that combines a value of the underlying type with a boolean null indicator.<}0{>可為 Null 的型別是結合基礎型別的值與布林 null 指示器的結構。<0} {0>An instance of a nullable type has two public read-only properties: HasValue, of type bool, and Value, of the nullable type’s underlying type. HasValue is true for a non-null instance and false for a null instance.<}0{>可為 Null 型別的執行個體具有兩個公用的唯讀屬性:HasValue (屬於 bool 型別) 以及 Value (屬於可為 Null 型別的基礎型別)。非 null 執行個體的 HasValue 為 true,null 執行個體則為 false。<0} {0>When HasValue is true, the Value property returns the contained value.<}0{>當 HasValue 為 true 時,Value 屬性會傳回所包含的值,<0} {0>When HasValue is false, an attempt to access the Value property throws an exception.<}0{>而當 HasValue 為 false 時,嘗試存取 Value 屬性就會擲回例外狀況 (Exception)。<0}

{0>An implicit conversion exists from any non-nullable value type to a nullable form of that type.<}0{>任何不可為 Null 的實值型別會隱含轉換為該型別的可為 Null 形式。<0} {0>Furthermore, an implicit conversion exists from the null literal to any nullable type.<}0{>此外,該 null 常值 (Literal) 會隱含轉換為任何可為 Null 的型別。<0}{0>In the example<}100{>下面這個範例中:<0}

int? x = 123;int? y = null;

if (x.HasValue) Console.WriteLine(x.Value); if (y.HasValue) Console.WriteLine(y.Value);

{0>the int value 123 and the null literal are implicitly converted to the nullable type int?.<}0{>int 值 123 和 null 常值已隱含轉換為可為 Null 的型別 int?。<0} {0>The example outputs 123 for x, but the second Console.WriteLine isn’t executed because y.HasValue is false.<}0{>此範例會針對 x 輸出 123,但第二個 Console.WriteLine 不會執行,因為 y.HasValue 為 false。<0}

{0>Nullable conversions and lifted conversions permit predefined and user-defined conversions that operate on non-nullable value types to also be used with nullable forms of those types.<}0{>可為 Null 的轉換和提昇的轉換允許在不可為 null 的實值型別上作業的預先定義和使用者定義轉換,也可以搭配這些型別的可為 Null 形式使用。<0} {0>Likewise, lifted operators permit predefined and user-defined operators that work for non-nullable value types also work for nullable forms of those types.<}0{>同樣地,提昇的轉換允許用於不可為 null 的實值型別的預先定義和使用者定義運算子,也可以用於這些型別的可為 Null 形式。<0}

{0>For every predefined conversion from a non-nullable value type S to a non-nullable value type T, a predefined nullable conversion automatically exists from S? to T?.<}0{>對於每個從不可為 Null 的實值型別 S,轉換為不可為 Null 的實值型別 T 的預先定義轉換,會自動進行從 S? 到 T? 之預先定義的可為 Null 轉換。<0} {0>This nullable conversion is a null propagating form of the underlying conversion: It converts a null source value directly to a null target value, but otherwise performs the underlying non-nullable conversion.<}0{>這個可為 Null 的轉換是基礎轉換的 null 傳用形式:它會將 null 來源值直接轉換為 null 目標值,但另一方面卻會執行不可為 Null 的基礎轉換。<0} {0>Nullable conversions are furthermore provided from S to T? and from S? to T, the latter as an explicit conversion that throws an exception if the source value is null.<}0{>此外,您可以從 S 到 T? 進行可為 Null 的轉換,也可從 S? 轉換到 T,後者是明確的轉換,當來源值為 null 時會傳回例外狀況。<0}

{0>Some examples of nullable conversions are shown in the following.<}0{>下列是一些可為 Null 轉換的範例。<0}

int i = 123;int? x = i;

// int --> int?double? y = x;

// int? --> double?int? z = (int?)y;

// double? --> int?int j = (int)z;

// int? --> int

{0>A user-defined conversion operator has a lifted form when the source and target types are both non-nullable value types.<}0{>當來源和目標型別都是不可為 Null 的實值型別時,使用者定義的轉換運算子便具有提昇的形式。<0} {0>A ? modifier is added to the the source and target types to create the lifted form.<}0{>? 修飾詞會加入至來源和目標型別,以建立提昇的形式。<0} {0>Similar to predefined nullable conversions, lifted conversion operators propagate nulls.<}0{>提昇的轉換運算子和預先定義的可為 Null 轉換相似,會傳用 null。<0}

{0>A non-comparison operator has a lifted form when the operand types and result type are all non-nullable value types.<}0{>當運算元型別和結果型別都是不可為 Null 的實值型別時,非比較運算子便具有提昇的形式。<0} {0>For non-comparison operators, a ? modifier is added to each operand type and the result type to create the lifted form.<}0{>對於非比較運算子,? 修飾詞會加入至每個運算元型別和結果型別,以建立提昇的形式。<0} {0>For example, the lifted form of the predefined + operator that takes two int operands and returns an int is an operator that takes two int? operands and returns an int?.<}0{>例如,使用兩個 int 運算元並傳回 int 之預先定義 + 運算子的提昇形式,是使用兩個 int? 運算元並傳回 int? 的運算子。<0} {0>Similar to lifted conversions, lifted non-comparison operators are null propagating: If either operand of a lifted operator is null, the result is null.<}0{>提昇的非比較運算子和提昇的轉換相似,會傳用 null:如果提昇運算子的任一運算元為 null,結果就是 null。<0}

{0>The following example uses a lifted + operator to add two int? values:<}0{>下列範例使用提昇的 + 運算子加入兩個 int? 值:<0}

int? x = GetNullableInt();int? y = GetNullableInt();int? z = x + y;

{0>the assignment to z effectively corresponds to:<}0{>z 的設定會有效地對應至:<0}

int? z = x.HasValue && y.HasValue ? x.Value + y.Value : (int?)null;

{0>Because an implicit conversion exists from a non-nullable value type to its nullable form, a lifted operator is applicable when just one operand is of a nullable type.<}0{>因為不可為 Null 的實值型別會隱含轉換為其可為 Null 的形式,在只有一個運算元是可為 Null 的型別時,即可使用提昇的運算子。<0} {0>The following example uses the same lifted + operator as the example above:<}0{>下列範例使用與上述範例相同的提昇 + 運算子:<0}

int? x = GetNullableInt();int? y = x + 1;

{0>If x is null, y is assigned null.<}0{>如果 x 為 null,y 就會指派為 null。<0} {0>Otherwise, y is assigned the value of x plus one.<}0{>否則,y 會指派為 x 加 1 的值。<0}

{0>The null propagating semantics of C#’s nullable conversions, lifted conversions, and lifted non-comparison operators are very similar to the corresponding conversions and operators in SQL.<}0{>C# 之可為 Null 的轉換、提昇的轉換和提昇的非比較運算子的 null 傳用語意 (Semantics),與 SQL 中的對應轉換和運算子非常類似。<0} {0>However, C#’s lifted comparison operators produce regular boolean results rather than introducing SQL’s three-valued boolean logic.<}0{>但是,C# 之提昇的比較運算子會產生一般的布林結果,而非引入 SQL 的三數值布林邏輯。<0}

{0>A comparison operator (==, !=, <, >, <=, >=) has a lifted form when the operand types are both non-nullable value types and the result type is bool.<}0{>當運算元型別都是不可為 Null 的實值型別且結果型別為 bool 時,比較運算子 (==、!=、<、>、<=、>=) 便具有提昇的形式。<0} {0>The lifted form of a comparison operator is formed by adding a ? modifier to each operand type (but not to the result type).<}0{>比較運算子的提昇形式是藉由將 ? 修飾詞加入至每個運算元型別 (但不加入至結果型別) 而構成。<0} {0>Lifted forms of the == and != operators consider two null values equal, and a null value unequal to a non-null value.<}0{>== 和 != 運算子的提昇形式會將兩個 null 值視為相等,並將 null 值視為與非 null 值不相等。<0} {0>Lifted forms of the <, >, <=, and >= operators return false if one or both operands are null.<}0{>如果一或兩個運算元都為 null,則 <、>、<= 和 >= 運算子的提昇形式會傳回 false。<0}

{0>When one of the operands of the == or != operator is the null literal, the other operand may be of any nullable type regardless of whether the underlying value type actually declares that operator.<}0{>當 == 或 != 運算子的其中一個運算元為 null 常值時,不論基礎實值型別是否實際宣告該運算子,另一個運算元可以是任何可為 Null 的型別。<0} {0>In cases where no operator == or != implementation is available, a check of the operand’s HasValue property is substituted.<}0{>在沒有可用的運算子 == 或 != 實作 (Implementation) 的情況下,則改為檢查該運算元之 HasValue 屬性。<0} {0>The effect of this rule is that statements such as<}0{>這個規則的結果就是任何可為 Null 型別或參考型別的 x 都可以有如下陳述式<0}

if (x == null) Console.WriteLine("x is null");

if (x != null) Console.WriteLine("x is non-null");

{0>are permitted for an x of any nullable type or reference type, thus providing a common way of performing null checks for all types that can be null.<}0{>,因此提供了對可為 null 的所有型別執行 null 檢查的常用方式。<0}

{0>A new null coalescing operator, ??, is provided.<}0{>提供了新的 null 聯合運算子 ??。<0} {0>The result of a ?? b is a if a is non-null; otherwise, the result is b.<}0{>如果 a 為非 null,a ?? b 的結果為 a,否則,結果為 b。<0} {0>Intuitively, b supplies the value to use when a is null.<}0{>也就是說,當 a 為 null 時,b 就會提供要使用的值。<0}

{0>When a is of a nullable type and b is of a non-nullable type, a ?? b returns a non-nullable value, provided the appropriate implicit conversions exist between the operand types.<}0{>當 a 是可為 Null 的型別,而 b 是不可為 Null 的型別時,若運算元型別間已有適當的隱含轉換,a ?? b 會傳回不可為 Null 的值。<0}{0>In the example<}100{>下面這個範例中:<0}

int? x = GetNullableInt();int? y = GetNullableInt();int? z = x ?? y;int i = z ?? -1;

{0>the type of x ?? y is int?, but the type of z ?? -1 is int.<}0{>x ?? y 的型別為 int?,但 z ?? -1 的型別為 int。<0} {0>The latter operation is particularly convenient because it removes the ? from the type and at the same time supplies the default value to use in the null case.<}0{>後者的作業特別方便,因為它從型別移除了 ?,同時還提供在 null 情況下要使用的預設值。<0}

{0>The null coalescing operator also works for reference types.<}0{>null 聯合運算子也可以用於參考型別。<0}{0>The example<}100{>下列範例<0}

string s = GetStringValue();Console.WriteLine(s ?? "Unspecified");

{0>outputs the value of s, or outputs Unspecified if s is null.<}0{>輸出 s 的值,若 s 為 null,則輸出 Unspecified。<0}

20. {0>Generics<}100{>泛型<0}

20.1 {0>Generic class declarations<}0{>泛型類別宣告<0}=_Ref71123411>

{0>A generic class declaration is a declaration of a class that requires type arguments to be supplied in order to form actual types.<}0{>泛型類別宣告是必須提供型別引數才能構成實際型別之類別的宣告。<0}

{0>A class declaration can optionally define type parameters:<}0{>類別宣告可以選擇性地定義型別參數:<0}

{0>class-declaration:attributesopt class-modifiersopt class identifier type-parameter-listopt class-baseopt<}0{>class-declaration:attributesopt class-modifiersopt class identifier type-parameter-listopt class-baseopt<0}

{0>type-parameter-constraints-clausesopt class-body ;opt<}0{>type-parameter-constraints-clausesopt class-body ;opt<0}

{0>A class declaration cannot supply type-parameter-constraints-clauses (§20.7) unless it also supplies a type-parameter-list.<}0{>類別宣告無法提供 type-parameter-constraints-clauses (章節 20.7),除非它也提供 type-parameter-list。<0}

{0>A class declaration that supplies a type-parameter-list is a generic class declaration.<}0{>提供 type-parameter-list 的類別宣告就是泛型類別宣告。<0} {0>Additionally, any class nested inside a generic class declaration or a generic struct declaration is itself a generic class declaration, since type parameters for the containing type must be supplied to create a constructed type.<}0{>此外,任何以巢狀方式置於泛型類別宣告或泛型結構宣告內部的類別,本身就是泛型類別宣告,因為必須提供包含型別 (Containing Type) 的型別參數以建立建構的型別。<0}

{0>Generic class declarations follow the same rules as non-generic class declarations except where noted.<}0{>除特別註明之處,泛型類別宣告會遵循與非泛型類別宣告相同的規則。<0} {0>Generic class declarations can be nested inside non-generic class declarations.<}0{>泛型類別宣告能夠以巢狀方式置於非泛型類別宣告內部。<0}

{0>A generic class is referenced using a constructed type (§20.4).<}0{>您可以使用建構的型別 (章節 20.4) 參考泛型類別。<0} {0>Given the generic class declaration<}0{>指定以下泛型類別宣告<0}

class List {}

{0>some examples of constructed types are List, List and List>.<}0{>建構型別的一些範例為 List、List 和 List>。<0} {0>A constructed type that uses one or more type parameters, such as List, is called an open constructed type.<}0{>使用一或多個型別參數的建構型別 (例如 List),稱為開放式建構型別。<0} {0>A constructed type that uses no type parameters, such as List, is called a closed constructed type.<}0{>未使用任何型別參數的建構型別 (例如 List),稱為關閉建構型別。<0}

{0>Generic types can be “overloaded” on the number of type parameters; that is two type declarations within the same namespace or outer type declaration can use the same identifier as long as they have a different number of type parameters.<}0{>泛型型別可以「多載」於型別參數數目上;也就是說,相同命名空間 (Namespace) 或外部型別宣告中的兩個型別宣告只要其型別參數數目不同,就可以使用相同的識別項。<0}

class C {}

class C {}

struct C {}

// Error, C with two type parameters defined twice

class C {}

// Error, C with two type parameters defined twice

{0>The type lookup rules used during type name resolution (§20.9.1), simple name resolution (§20.9.5), and member access (§20.9.6) respect the number of type parameters.<}0{>型別名稱解析 (章節 20.9.1)、簡單名稱解析 (章節 20.9.5) 和成員存取 (章節 20.9.6) 期間使用的型別查詢規則涉及到型別參數的數目。<0}

{0>The base interfaces of a generic class declaration must satisfy the uniqueness rule described in §20.3.1.<}0{>泛型類別宣告的基底介面必須滿足章節 20.3.1 中所述的唯一性規則。<0}

20.1.1 {0>Type parameters<}0{>型別參數<0}=_Ref23663178>

{0>Type parameters can be supplied in a class declaration.<}0{>型別參數可以利用類別宣告的形式提供。<0} {0>Each type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type.<}0{>每個型別參數都是簡單的識別項,表示提供用來建立建構型別之型別引數的替代符號。<0} {0>A type parameter is a formal placeholder for a type that will be supplied later.<}0{>型別參數是稍後會提供之型別的正式替代符號。<0} {0>By constrast, a type argument (§20.5.1) is the actual type that is substituted for the type parameter when a constructed type is created.<}0{>相對而言,型別引數 (章節 20.5.1) 則是建立建構的型別時用於取代型別參數的實際型別。<0}

{0>type-parameter-list:< type-parameters ><}0{>type-parameter-list:< type-parameters ><0}

{0>type-parameters:attributesopt type-parametertype-parameters , attributesopt type-parameter<}0{>type-parameters:attributesopt type-parametertype-parameters , attributesopt type-parameter<0}

{0>type-parameter:identifier<}0{>type-parameter:identifier<0}

{0>Each type parameter in a class declaration defines a name in the declaration space (§3.3) of that class.<}0{>類別宣告中的每個型別參數都會在該類別的宣告空間 (章節 3.3) 中定義名稱。<0} {0>Thus, it cannot have the same name as another type parameter or a member declared in that class.<}0{>因此,不可能會有與該類別中所宣告之其他型別參數或成員相同的名稱。<0} {0>A type parameter cannot have the same name as the type itself.<}0{>型別參數的名稱不可與型別本身相同。<0}

{0>The scope (§3.7) of a type parameter on a class includes the class-base, type-parameter-constraints-clauses, and class-body.<}0{>類別上型別參數的範圍 (章節 3.7) 包括 class-base、type-parameter-constraints-clauses 和 class-body。<0} {0>Unlike members of a class, this scope does not extend to derived classes.<}0{>這個範圍與類別的成員不同,不會延伸到衍生類別 (Derived Class)。<0} {0>Within its scope, a type parameter can be used as a type.<}0{>在其範圍中,型別參數可以當做型別使用。<0}

{0>type:value-typereference-typetype-parameter<}91{>type:value-typereference-typetype-parameter<0}

{0>Since a type parameter can be instantiated with many different actual type arguments, type parameters have slightly different operations and restrictions than other types.<}0{>由於型別參數可以使用許多不同的實際型別引數執行個體化,因此,型別參數的作業和限制會與其他型別略有不同,<0} {0>These include:<}0{>其中包括:<0}

· {0>A type parameter cannot be used directly to declare a base class or interface (§20.1.3).<}0{>型別參數不能直接用來宣告基底類別 (Base Class) 或介面 (章節 20.1.3)。<0}

· {0>The rules for member lookup on type parameters depend on the constraints, if any, applied to the type parameter.<}0{>型別參數的成員查詢規則取決於套用至該型別參數的條件約束 (如果有)。<0} {0>They are detailed in §20.7.2.<}0{>這些規則在章節 20.7.2 中有詳細說明。<0}

· {0>The available conversions for a type parameter depend on the constraints, if any, applied to the type parameter.<}0{>型別參數的可用轉換取決於套用至該型別參數的條件約束 (如果有)。<0} {0>They are detailed in §20.7.4.<}0{>這些規則在章節 20.7.4 中有詳細說明。<0}

· {0>The literal null cannot be converted to a type given by a type parameter, except if the type parameter is known to be a reference type (§20.7.4).<}0{>常值 null 不能轉換為型別參數所指定的型別,除非已知該型別參數為參考型別 (章節 20.7.4)。<0} {0>However, a default value expression (§25.6) can be used instead.<}0{>但是,可以改用預設值運算式 (章節 25.6)。<0} {0>In addition, a value with a type given by a type parameter can be compared with null using == and != (§20.8.3) unless the type parameter has the value type constraint (§20.7.4).<}0{>此外,型別為型別參數指定的值「可以」使用 == 和 != (章節 20.8.3) 與 null 比較,除非該型別參數具有實值型別條件約束 (章節 20.7.4)。<0}

· {0>A new expression (§20.8.1) can only be used with a type parameter if the type parameter is constrained by a constructor-constraint or the value type constraint (§20.7).<}0{>new 運算式 (章節 20.8.1) 只能與由 constructor-constraint 或實值型別條件約束 (章節 20.7) 所約束的型別參數一起使用。<0}

· {0>A type parameter cannot be used anywhere within an attribute.<}0{>型別參數不能用在屬性中。<0}

· {0>A type parameter cannot be used in a member access or type name to identify a static member or a nested type (§20.9.1, §20.9.6).<}0{>型別參數不能用在成員存取或型別名稱中來指定靜態成員或巢狀型別 (章節 20.9.1、章節 20.9.6)。<0}

· {0>In unsafe code, a type parameter cannot be used as an unmanaged-type (§18.2).<}0{>在 unsafe 程式碼中,型別參數不可當做 unmanaged-type (章節 18.2) 使用。<0}

{0>As a type, type parameters are purely a compile-time construct.<}0{>做為型別時,型別參數只是編譯時期建構。<0} {0>At run-time, each type parameter is bound to a run-time type that was specified by supplying a type argument to the generic type declaration.<}0{>在執行階段,每個型別參數都繫結至一個執行階段型別,該型別藉由將型別引數提供給泛型型別宣告所指定。<0} {0>Thus, the type of a variable declared with a type parameter will, at run-time, be a closed constructed type (§20.5.2).<}0{>因此,在執行階段,使用型別參數宣告之變數的型別會是關閉建構型別 (章節 20.5.2)。<0} {0>The run-time execution of all statements and expressions involving type parameters uses the actual type that was supplied as the type argument for that parameter.<}0{>所有涉及型別參數之陳述式和運算式的執行階段執行,都使用提供做為該參數之型別引數的實際型別。<0}

20.1.2 {0>The instance type<}0{>執行個體型別<0}=_Ref24863728>

{0>Each class declaration has an associated constructed type, the instance type.<}0{>每個類別宣告都有關聯的建構型別,也就是執行參數型別。<0} {0>For a generic class declaration, the instance type is formed by creating a constructed type (§20.4) from the type declaration, with each of the supplied type arguments being the corresponding type parameter.<}0{>對於泛型類別宣告,執行個體型別的構成方式為:從型別宣告建立建構的型別 (章節 20.4),每個提供的型別引數都為對應的型別參數。<0} {0>Since the instance type uses the type parameters, it can only be used where the type parameters are in scope; that is, inside the class declaration.<}0{>因為執行個體型別使用型別參數,它只能用於型別參數在範圍內的地方;也就是說,在類別宣告之內。<0} {0>The instance type is the type of this f