類別Classes

類別是一種資料結構,其中可能包含 (常數和欄位的資料成員) 、函式成員 (方法、屬性、事件、索引子、運算子、實例的函式、析構函數和靜態函式) 和巢狀型別。A class is a data structure that may contain data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, destructors and static constructors), and nested types. 類別類型支援繼承,這是一種機制,可讓衍生類別擴充和特製化基類。Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

類別宣告Class declarations

Class_declaration 是宣告新類別) 的 type_declaration (類型宣告。A class_declaration is a type_declaration (Type declarations) that declares a new class.

class_declaration
    : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list?
      class_base? type_parameter_constraints_clause* class_body ';'?
    ;

Class_declaration 是由一組選擇性的 屬性 所組成 (屬性) ,後面接著一組選擇性的 class_modifier s (類別修飾詞) 、後面接著選擇性的修飾詞 partial ,後面接著關鍵字 class 以及命名類別的 識別碼,後面接著選擇性的 type_parameter_list (類型參數) ,後面接著選用的 class_base 規格 (類別基底規格 **) , 後面接著一組選擇性的 type_parameter_constraints_clause (類別 主體) ,後面接著一組選擇性的 class_body (型別 參數條件約束,後面接著一個分號。A class_declaration consists of an optional set of attributes (Attributes), followed by an optional set of class_modifier s (Class modifiers), followed by an optional partial modifier, followed by the keyword class and an identifier that names the class, followed by an optional type_parameter_list (Type parameters), followed by an optional class_base specification (Class base specification) , followed by an optional set of type_parameter_constraints_clause s (Type parameter constraints), followed by a class_body (Class body), optionally followed by a semicolon.

類別宣告無法提供 type_parameter_constraints_clause s,除非它也提供 type_parameter_listA class declaration cannot supply type_parameter_constraints_clause s unless it also supplies a type_parameter_list.

提供 type_parameter_list 的類別宣告是 泛型類別 宣告。A class declaration that supplies a type_parameter_list is a generic class declaration. 此外,在泛型類別宣告或泛型結構宣告中嵌套的任何類別本身都是泛型類別宣告,因為必須提供包含類型的型別參數來建立結構化的型別。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.

類別修飾詞Class modifiers

Class_declaration 可以選擇性地包含一連串的類別修飾詞:A class_declaration may optionally include a sequence of class modifiers:

class_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'abstract'
    | 'sealed'
    | 'static'
    | class_modifier_unsafe
    ;

在類別宣告中多次出現相同的修飾詞時,會發生編譯時期錯誤。It is a compile-time error for the same modifier to appear multiple times in a class declaration.

new可以在嵌套類別上使用修飾詞。The new modifier is permitted on nested classes. 它會指定類別依相同名稱隱藏繼承的成員,如 新的修飾詞中所述。It specifies that the class hides an inherited member by the same name, as described in The new modifier. 這是編譯時期錯誤,修飾詞會 new 出現在不是嵌套類別宣告的類別宣告上。It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

publicprotected 、和修飾詞 internal private 可控制類別的存取範圍。The public, protected, internal, and private modifiers control the accessibility of the class. 視發生類別宣告的內容而定, (宣告的 協助工具) ,可能不允許部分修飾詞。Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

abstract sealed 下列各節將討論、和修飾詞 staticThe abstract, sealed and static modifiers are discussed in the following sections.

抽象類別Abstract classes

abstract修飾詞是用來表示類別不完整,而且僅供做為基類使用。The abstract modifier is used to indicate that a class is incomplete and that it is intended to be used only as a base class. 抽象類別與非抽象類別的差異如下:An abstract class differs from a non-abstract class in the following ways:

  • 抽象類別無法直接具現化,而且是在抽象類別上使用運算子的編譯時期錯誤 newAn abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. 雖然可以有編譯時間類型為抽象的變數和值,但這類變數和值一定會是 null 或包含衍生自抽象型別的非抽象類別實例的參考。While it is possible to have variables and values whose compile-time types are abstract, such variables and values will necessarily either be null or contain references to instances of non-abstract classes derived from the abstract types.
  • 允許抽象類別 (但不需要) 包含抽象成員。An abstract class is permitted (but not required) to contain abstract members.
  • 抽象類別無法密封。An abstract class cannot be sealed.

當非抽象類別衍生自抽象類別時,非抽象類別必須包含所有繼承之抽象成員的實際實值,進而覆寫這些抽象成員。When a non-abstract class is derived from an abstract class, the non-abstract class must include actual implementations of all inherited abstract members, thereby overriding those abstract members. 在範例中In the example

abstract class A
{
    public abstract void F();
}

abstract class B: A
{
    public void G() {}
}

class C: B
{
    public override void F() {
        // actual implementation of F
    }
}

抽象類別 A 引進抽象方法 Fthe abstract class A introduces an abstract method F. 類別 B 引進了一個額外的方法 G ,但因為它不提供的實作為 F ,所以 B 也必須宣告為抽象。Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. 類別會 C 覆寫 F 並提供實際的實作為。Class C overrides F and provides an actual implementation. 因為中沒有任何抽象成員 C ,所以 C 允許 (但不需要) 為非抽象。Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

密封類別Sealed classes

sealed修飾詞是用來防止衍生自類別。The sealed modifier is used to prevent derivation from a class. 如果將密封類別指定為另一個類別的基類,則會發生編譯時期錯誤。A compile-time error occurs if a sealed class is specified as the base class of another class.

密封的類別也不能是抽象類別。A sealed class cannot also be an abstract class.

sealed修飾詞主要是用來防止非預期的衍生,但它也會啟用特定的執行時間優化。The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. 尤其是,因為密封的類別永遠都不會有任何衍生類別,所以可以將密封類別實例上的虛擬函式成員調用轉換成非虛擬調用。In particular, because a sealed class is known to never have any derived classes, it is possible to transform virtual function member invocations on sealed class instances into non-virtual invocations.

靜態類別Static classes

static修飾詞是用來標記要宣告為 靜態類別 的類別。The static modifier is used to mark the class being declared as a static class. 靜態類別無法具現化,不能當做型別使用,而且只能包含靜態成員。A static class cannot be instantiated, cannot be used as a type and can contain only static members. 只有靜態類別可包含擴充方法的宣告) (擴充方法Only a static class can contain declarations of extension methods (Extension methods).

靜態類別宣告受限於下列限制:A static class declaration is subject to the following restrictions:

  • 靜態類別不能包含 sealed 或修飾詞 abstractA static class may not include a sealed or abstract modifier. 不過請注意,由於靜態類別無法具現化或衍生自靜態類別,因此它的行為就像是 sealed 和 abstract 一樣。Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • 靜態類別不能包含 (類別基底規格) 的 Class_base 規格,而且無法明確指定基類或已執行介面的清單。A static class may not include a class_base specification (Class base specification) and cannot explicitly specify a base class or a list of implemented interfaces. 靜態類別會隱含繼承自型別 objectA static class implicitly inherits from type object.
  • 靜態類別只能包含靜態成員 , (靜態和實例成員) 。A static class can only contain static members (Static and instance members). 請注意,常數和巢狀型別會分類為靜態成員。Note that constants and nested types are classified as static members.
  • 靜態類別不能有具有或宣告存取範圍的成員 protected protected internalA static class cannot have members with protected or protected internal declared accessibility.

這是編譯時期錯誤,會違反上述任何一項限制。It is a compile-time error to violate any of these restrictions.

靜態類別沒有實例的函式。A static class has no instance constructors. 您無法在靜態類別中宣告實例的函式,也不會針對靜態類別提供預設的實例函式 (預設 的函式) 。It is not possible to declare an instance constructor in a static class, and no default instance constructor (Default constructors) is provided for a static class.

靜態類別的成員不會自動為靜態,而且成員宣告必須明確包含 static 修飾詞 (但) 的常數和巢狀型別除外。The members of a static class are not automatically static, and the member declarations must explicitly include a static modifier (except for constants and nested types). 當類別內嵌在靜態外部類別中時,除非它明確包含修飾詞,否則嵌套類別不是靜態類別 staticWhen a class is nested within a static outer class, the nested class is not a static class unless it explicitly includes a static modifier.

參考靜態類別類型Referencing static class types

如果有 namespace_or_type_name (命名空間和類型名稱) 可以參考靜態類別。A namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • Namespace_or_type_nameT 在表單的 namespace_or_type_nameT.I ,或The namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • Namespace_or_type_nameT typeof_expression (引數列出1) 表單 typeof(T)The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

如果 primary_expression (函式 成員) 可以參考靜態類別(如果A primary_expression (Function members) is permitted to reference a static class if

在任何其他內容中,它是參考靜態類別的編譯時期錯誤。In any other context it is a compile-time error to reference a static class. 例如,使用靜態類別做為基類、成員的組成型 別) ( 、泛型型別引數或型別參數條件約束的型別,就是一個錯誤。For example, it is an error for a static class to be used as a base class, a constituent type (Nested types) of a member, a generic type argument, or a type parameter constraint. 同樣地,靜態類別不能用在陣列類型、指標類型、 new 運算式、轉換運算式、 is 運算式、 as 運算式、 sizeof 運算式或預設值運算式中。Likewise, a static class cannot be used in an array type, a pointer type, a new expression, a cast expression, an is expression, an as expression, a sizeof expression, or a default value expression.

Partial 修飾詞Partial modifier

partial修飾詞是用來表示此 class_declaration 為部分類型宣告。The partial modifier is used to indicate that this class_declaration is a partial type declaration. 在封入命名空間或型別宣告內使用相同名稱的多個部分型別宣告,會結合來形成一種型別宣告,遵循 部分類型中指定的規則。Multiple partial type declarations with the same name within an enclosing namespace or type declaration combine to form one type declaration, following the rules specified in Partial types.

如果在不同的內容中產生或維護這些區段,則將類別宣告散發到不同的程式文字區段會很有用。Having the declaration of a class distributed over separate segments of program text can be useful if these segments are produced or maintained in different contexts. 例如,類別宣告的一個部分可能會產生電腦,而另一個部分則是手動撰寫。For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. 這兩者的文字分隔可防止某個更新與其他更新衝突。Textual separation of the two prevents updates by one from conflicting with updates by the other.

型別參數Type parameters

型別參數是簡單識別碼,表示提供來建立結構化型別之型別引數的預留位置。A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. 型別參數是類型的正式預留位置,稍後將會提供。A type parameter is a formal placeholder for a type that will be supplied later. 相較之下,型別引數 (型別引數) 是建立建立時,會取代型別參數的實際型別。By contrast, a type argument (Type arguments) is the actual type that is substituted for the type parameter when a constructed type is created.

type_parameter_list
    : '<' type_parameters '>'
    ;

type_parameters
    : attributes? type_parameter
    | type_parameters ',' attributes? type_parameter
    ;

type_parameter
    : identifier
    ;

類別宣告中的每個型別參數會在宣告空間中定義該 類別 (宣告) 的名稱。Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. 因此,它不能與另一個類型參數或該類別中宣告的成員具有相同的名稱。Thus, it cannot have the same name as another type parameter or a member declared in that class. 類型參數不能具有與類型本身相同的名稱。A type parameter cannot have the same name as the type itself.

類別基底規格Class base specification

類別宣告可能包含 class_base 規格,此規格會定義類別的直接基類,以及 (介面) 由類別直接實作為介面。A class declaration may include a class_base specification, which defines the direct base class of the class and the interfaces (Interfaces) directly implemented by the class.

class_base
    : ':' class_type
    | ':' interface_type_list
    | ':' class_type ',' interface_type_list
    ;

interface_type_list
    : interface_type (',' interface_type)*
    ;

在類別宣告中指定的基類可以是 () 的 結構 化類別型別。The base class specified in a class declaration can be a constructed class type (Constructed types). 基類本身不能是型別參數,但它可以包含範圍內的型別參數。A base class cannot be a type parameter on its own, though it can involve the type parameters that are in scope.

class Extend<V>: V {}            // Error, type parameter used as base class

基底類別Base classes

class_type 包含在 class_base 中時,它會指定要宣告之類別的直接基類。When a class_type is included in the class_base, it specifies the direct base class of the class being declared. 如果類別宣告沒有 class_base,或 class_base 只列出介面類別型,則會假設直接基類為 objectIf a class declaration has no class_base, or if the class_base lists only interface types, the direct base class is assumed to be object. 類別會繼承其直接基類的成員,如 繼承中所述。A class inherits members from its direct base class, as described in Inheritance.

在範例中In the example

class A {}

class B: A {}

類別稱為的 A 直接基類,也稱為 B B 衍生自 Aclass A is said to be the direct base class of B, and B is said to be derived from A. 由於不 A 會明確指定直接基類,因此它的直接基類是隱含的 objectSince A does not explicitly specify a direct base class, its direct base class is implicitly object.

針對已建立的類別型別,如果在泛型類別宣告中指定基類,則會藉由替代基底類別宣告中的每個 type_parameter ,來取得所建立之型別的基類(對應的 type_argument )。For a constructed class type, if a base class is specified in the generic class declaration, the base class of the constructed type is obtained by substituting, for each type_parameter in the base class declaration, the corresponding type_argument of the constructed type. 指定泛型類別宣告Given the generic class declarations

class B<U,V> {...}

class G<T>: B<string,T[]> {...}

結構化類型的基類 G<int>B<string,int[]>the base class of the constructed type G<int> would be B<string,int[]>.

類別類型的直接基類至少必須可以像類別型別本身一樣存取 (存取範圍網域) 。The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). 例如,它是 public 衍生自或類別之類別的編譯時期錯誤 private internalFor example, it is a compile-time error for a public class to derive from a private or internal class.

類別類型的直接基類不可以是下列其中一種類型: System.ArraySystem.DelegateSystem.MulticastDelegateSystem.EnumSystem.ValueTypeThe direct base class of a class type must not be any of the following types: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, or System.ValueType. 此外,泛型類別宣告不能用 System.Attribute 來做為直接或間接基類。Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

判斷類別的直接基類規格的意義時 A B ,的直接基類 B 會暫時假設為 objectWhile determining the meaning of the direct base class specification A of a class B, the direct base class of B is temporarily assumed to be object. 以直覺方式,這可確保基類規格的意義無法遞迴相依于本身。Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. 範例:The example:

class A<T> {
   public class B {}
}

class C : A<C.B> {}

發生錯誤,因為在基類規格中, A<C.B> 的直接基類會被 C 視為 object ,因此 (命名空間和類型名稱 的規則) 不 C 會被視為具有成員 Bis in error since in the base class specification A<C.B> the direct base class of C is considered to be object, and hence (by the rules of Namespace and type names) C is not considered to have a member B.

類別類型的基類是直接基類和其基類。The base classes of a class type are the direct base class and its base classes. 換句話說,基類的集合是直接基類關聯性的可轉移關閉。In other words, the set of base classes is the transitive closure of the direct base class relationship. 參考上述範例,的基類為 B AobjectReferring to the example above, the base classes of B are A and object. 在範例中In the example

class A {...}

class B<T>: A {...}

class C<T>: B<IComparable<T>> {...}

class D<T>: C<T[]> {...}

的基類為 D<int> C<int[]>B<IComparable<int[]>>Aobjectthe base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

除了類別之外 object ,每個類別類型都只有一個直接基類。Except for class object, every class type has exactly one direct base class. object類別沒有直接基類,而且是所有其他類別的最終基類。The object class has no direct base class and is the ultimate base class of all other classes.

當類別 B 衍生自類別時 A ,它會是相依于的編譯時期錯誤 A BWhen a class B derives from a class A, it is a compile-time error for A to depend on B. 類別 * 會 直接相依于 _ 其直接基類 (如果有任何) ,而且 *直接相依于* 在任何) 中,它會立即嵌套 (的類別。A class directly depends on _ its direct base class (if any) and _directly depends on*_ the class within which it is immediately nested (if any). 指定此定義時,類別所相依的一組完整類別就是 _ 的反組出、可轉移的關閉會 直接相依于* 關聯性。Given this definition, the complete set of classes upon which a class depends is the reflexive and transitive closure of the _ directly depends on* relationship.

範例The example

class A: A {}

是錯誤的,因為類別相依于本身。is erroneous because the class depends on itself. 同樣地,範例Likewise, the example

class A: B {}
class B: C {}
class C: A {}

發生錯誤,因為類別會迴圈相依于本身。is in error because the classes circularly depend on themselves. 最後,範例Finally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

會導致編譯時期錯誤 A ,因為相依于 B.C (其直接的基類) ,這取決於 (其直接封入 B 類別) ,後者會迴圈相依 Aresults in a compile-time error because A depends on B.C (its direct base class), which depends on B (its immediately enclosing class), which circularly depends on A.

請注意,類別不會相依于其內嵌套的類別。Note that a class does not depend on the classes that are nested within it. 在範例中In the example

class A
{
    class B: A {}
}

B 相依于 A (A ,因為它的直接基類和其直接封入類別) ,但不相依于 A (, B 因為 B 它不是基類,也不是) 的封入類別 AB depends on A (because A is both its direct base class and its immediately enclosing class), but A does not depend on B (since B is neither a base class nor an enclosing class of A). 因此,此範例是有效的。Thus, the example is valid.

不可能衍生自 sealed 類別。It is not possible to derive from a sealed class. 在範例中In the example

sealed class A {}

class B: A {}            // Error, cannot derive from a sealed class

類別 B 因為嘗試衍生自類別而發生錯誤 sealed Aclass B is in error because it attempts to derive from the sealed class A.

介面實作Interface implementations

Class_base 規格可能包含介面類別型的清單,在這種情況下,類別會直接執行指定的介面類別型。A class_base specification may include a list of interface types, in which case the class is said to directly implement the given interface types. 介面實現將在 介面執行中進一步討論。Interface implementations are discussed further in Interface implementations.

型別參數條件約束Type parameter constraints

泛型型別和方法宣告可以包含 type_parameter_constraints_clause s,選擇性地指定型別參數條件約束。Generic type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clause s.

type_parameter_constraints_clause
    : 'where' type_parameter ':' type_parameter_constraints
    ;

type_parameter_constraints
    : primary_constraint
    | secondary_constraints
    | constructor_constraint
    | primary_constraint ',' secondary_constraints
    | primary_constraint ',' constructor_constraint
    | secondary_constraints ',' constructor_constraint
    | primary_constraint ',' secondary_constraints ',' constructor_constraint
    ;

primary_constraint
    : class_type
    | 'class'
    | 'struct'
    ;

secondary_constraints
    : interface_type
    | type_parameter
    | secondary_constraints ',' interface_type
    | secondary_constraints ',' type_parameter
    ;

constructor_constraint
    : 'new' '(' ')'
    ;

每個 type_parameter_constraints_clause 都包含權杖 where ,後面接著類型參數的名稱,後面接著冒號和該型別參數的條件約束清單。Each type_parameter_constraints_clause consists of the token where, followed by the name of a type parameter, followed by a colon and the list of constraints for that type parameter. 每個類型參數最多隻能有一個 where 子句,而且可以依 where 任何順序列出子句。There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. getset 屬性存取子中的和標記一樣, where 標記不是關鍵字。Like the get and set tokens in a property accessor, the where token is not a keyword.

子句中提供的條件約束清單 where 可以包含下列任一元件,順序如下:單一主要條件約束、一或多個次要條件約束,以及函式條件約束 new()The list of constraints given in a where clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, new().

主要條件約束可以是類別類型或 參考型別條件約束 _ class 或實 _數值型別條件約束*_ structA primary constraint can be a class type or the reference type constraint _ class or the _value type constraint*_ struct. 次要條件約束可以是 _type_parameter * 或 interface_typeA secondary constraint can be a _type_parameter* or interface_type.

參考型別條件約束會指定用於型別參數的型別引數必須是參考型別。The reference type constraint specifies that a type argument used for the type parameter must be a reference type. 已知為參考型別的所有類別類型、介面類別型、委派類型、陣列類型和類型參數, (如下所定義) 滿足此條件約束。All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

實值型別條件約束指定用於型別參數的型別引數,必須是不可為 null 的實值型別。The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. 所有不可為 null 的結構類型、列舉型別,以及實值型別條件約束的型別參數都符合這個條件約束。All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. 請注意,雖然分類為實數值型別,但可為 null 的型別 (可為 null 的型別) 不符合實值型別條件約束。Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. 具有實數值型別條件約束的類型參數也不能有 constructor_constraintA type parameter having the value type constraint cannot also have the constructor_constraint.

指標類型絕對不能是型別引數,也不會被視為滿足參考型別或實值型別條件約束。Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

如果條件約束是類別類型、介面類別型或型別參數,則該型別會指定用於該型別參數的每個型別引數都必須支援的「基底類型」。If a constraint is a class type, an interface type, or a type parameter, that type specifies a minimal "base type" that every type argument used for that type parameter must support. 只要使用的是結構化型別或泛型方法,就會在編譯時期針對型別參數上的條件約束檢查型別引數。Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. 提供的型別引數必須符合滿足條件 約束所述的條件。The type argument supplied must satisfy the conditions described in Satisfying constraints.

Class_type 條件約束必須滿足下列規則:A class_type constraint must satisfy the following rules:

  • 型別必須是類別類型。The type must be a class type.
  • 類型不得為 sealedThe type must not be sealed.
  • 類型不可以是下列其中一種類型: System.ArraySystem.DelegateSystem.EnumSystem.ValueTypeThe type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • 類型不得為 objectThe type must not be object. 因為所有型別都 object 是衍生自,所以如果允許的話,這類條件約束將沒有任何作用。Because all types derive from object, such a constraint would have no effect if it were permitted.
  • 針對指定的型別參數,最多隻能有一個條件約束為類別類型。At most one constraint for a given type parameter can be a class type.

指定為 interface_type 條件約束的型別必須滿足下列規則:A type specified as an interface_type constraint must satisfy the following rules:

  • 型別必須是介面型別。The type must be an interface type.
  • 在給定子句中不能指定一次以上的類型 whereA type must not be specified more than once in a given where clause.

無論是哪一種情況,條件約束都可以牽涉到相關聯類型或方法宣告的任何型別參數做為結構化型別的一部分,而且可能牽涉到宣告的型別。In either case, the constraint can involve any of the type parameters of the associated type or method declaration as part of a constructed type, and can involve the type being declared.

任何指定為型別參數條件約束的類別或介面類別型,都必須至少為可存取的 (存取範圍條件約束 ,) 為所宣告的泛型型別或方法。Any class or interface type specified as a type parameter constraint must be at least as accessible (Accessibility constraints) as the generic type or method being declared.

指定為 type_parameter 條件約束的型別必須滿足下列規則:A type specified as a type_parameter constraint must satisfy the following rules:

  • 型別必須是型別參數。The type must be a type parameter.
  • 在給定子句中不能指定一次以上的類型 whereA type must not be specified more than once in a given where clause.

此外,類型參數的相依性圖形中必須沒有迴圈,其中的相依性是由定義的可轉移關聯性:In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

  • 如果使用型別參數 T 做為型別參數的條件約束, SS 取決於 TIf a type parameter T is used as a constraint for type parameter S then S depends on T.
  • 如果型別參數 S 相依于型別參數 T ,而且 T 相依于型別參數,則會 U S 取決於 UIf a type parameter S depends on a type parameter T and T depends on a type parameter U then S depends on U.

在指定此關聯性的情況下,型別參數的編譯時期錯誤相依于本身 (直接或間接) 。Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

相依型別參數之間的任何條件約束都必須一致。Any constraints must be consistent among dependent type parameters. 如果 type 參數 S 相依于型別參數, T 則:If type parameter S depends on type parameter T then:

  • T 不得有實數值型別條件約束。T must not have the value type constraint. 否則, T 實際上是密封的,因此會強制為與 S 相同的類型 T ,而不需要兩個型別參數。Otherwise, T is effectively sealed so S would be forced to be the same type as T, eliminating the need for two type parameters.
  • 如果 S 具有值型別條件約束,則 T 不能有 class_type 條件約束。If S has the value type constraint then T must not have a class_type constraint.
  • 如果 Sclass_type 條件約束 A ,而且 Tclass_type 條件約束, B 則必須有身分識別轉換或隱含參考轉換,或從轉換 A 成的 B 隱含 B 參考 AIf S has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.
  • 如果 S 也相依于型別參數 U ,而且 Uclass_type 條件約束 A ,而且 Tclass_type 條件約束, B 則必須有身分識別轉換或隱含參考轉換,或從轉換 A 成的 B 隱含參考 B AIf S also depends on type parameter U and U has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

它適用于 S 具有實值型別條件約束,且 T 具有參考型別條件約束的有效。It is valid for S to have the value type constraint and T to have the reference type constraint. T對類型 System.ObjectSystem.ValueTypeSystem.Enum 和任何介面類別型有效的限制。Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

如果 where 型別參數的子句包含) 的表單 (的函式條件約束,則可以 new() 使用 new 運算子來建立 (物件建立運算式) 的型別實例。If the where clause for a type parameter includes a constructor constraint (which has the form new()), it is possible to use the new operator to create instances of the type (Object creation expressions). 任何用於具有函式條件約束之類型參數的類型引數,都必須有公用無參數的函式 (此函式會隱含地存在於任何實值型別) 或為具有實值型別條件約束或函式條件約束的類型參數 (請參閱詳細資料) 的類型參數條件約束Any type argument used for a type parameter with a constructor constraint must have a public parameterless constructor (this constructor implicitly exists for any value type) or be a type parameter having the value type constraint or constructor constraint (see Type parameter constraints for details).

以下是條件約束的範例:The following are examples of constraints:

interface IPrintable
{
    void Print();
}

interface IComparable<T>
{
    int CompareTo(T value);
}

interface IKeyProvider<T>
{
    T GetKey();
}

class Printer<T> where T: IPrintable {...}

class SortedList<T> where T: IComparable<T> {...}

class Dictionary<K,V>
    where K: IComparable<K>
    where V: IPrintable, IKeyProvider<K>, new()
{
    ...
}

下列範例是錯誤的,因為它會在類型參數的相依性圖形中造成迴圈:The following example is in error because it causes a circularity in the dependency graph of the type parameters:

class Circular<S,T>
    where S: T
    where T: S                // Error, circularity in dependency graph
{
    ...
}

下列範例說明其他不正確狀況:The following examples illustrate additional invalid situations:

class Sealed<S,T>
    where S: T
    where T: struct        // Error, T is sealed
{
    ...
}

class A {...}

class B {...}

class Incompat<S,T>
    where S: A, T
    where T: B                // Error, incompatible class-type constraints
{
    ...
}

class StructWithClass<S,T,U>
    where S: struct, T
    where T: U
    where U: A                // Error, A incompatible with struct
{
    ...
}

型別參數的 有效基類 定義如下 TThe effective base class of a type parameter T is defined as follows:

  • 如果沒有 T 主要條件約束或類型參數條件約束,則其有效基類為 objectIf T has no primary constraints or type parameter constraints, its effective base class is object.
  • 如果 T 有實值型別條件約束,則其有效基類為 System.ValueTypeIf T has the value type constraint, its effective base class is System.ValueType.
  • 如果 Tclass_type 條件約束 C ,但沒有 type_parameter 條件約束,則其有效基類為 CIf T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • 如果沒有 T class_type 條件約束,但有一或多個 type_parameter 條件約束,則它的有效基類是最包含的類型 (在其 type_parameter 條件約束的有效基類集中) 的 提升轉換運算子If T has no class_type constraint but has one or more type_parameter constraints, its effective base class is the most encompassed type (Lifted conversion operators) in the set of effective base classes of its type_parameter constraints. 一致性規則可確保這種最包含的類型存在。The consistency rules ensure that such a most encompassed type exists.
  • 如果 T 同時有 class_type 條件約束和一或多個 type_parameter 條件約束,則它的有效基類是最包含的型別, (從 class_type 條件約束和其 T type_parameter 條件約束的有效基類所組成的集合中) 的轉換運算子。If T has both a class_type constraint and one or more type_parameter constraints, its effective base class is the most encompassed type (Lifted conversion operators) in the set consisting of the class_type constraint of T and the effective base classes of its type_parameter constraints. 一致性規則可確保這種最包含的類型存在。The consistency rules ensure that such a most encompassed type exists.
  • 如果 T 有參考型別條件約束,但沒有 class_type 條件約束,則其有效基類為 objectIf T has the reference type constraint but no class_type constraints, its effective base class is object.

基於這些規則的目的,如果 T 有 value_type 的條件約束 V ,請改用屬於 class_type 的最特定基底類型 VFor the purpose of these rules, if T has a constraint V that is a value_type, use instead the most specific base type of V that is a class_type. 這絕對不會發生在明確指定的條件約束中,但當泛型方法的條件約束隱含地由覆寫方法宣告或介面方法的明確執行繼承時,可能會發生此情況。This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method.

這些規則可確保有效的基類一律是 class_typeThese rules ensure that the effective base class is always a class_type.

類型參數的 有效介面集 T 定義如下:The effective interface set of a type parameter T is defined as follows:

  • 如果沒有 T secondary_constraints,則其有效的介面集是空的。If T has no secondary_constraints, its effective interface set is empty.
  • 如果 Tinterface_type 條件約束,但沒有 type_parameter 條件約束,則其有效的介面集就是其 interface_type 條件約束的集合。If T has interface_type constraints but no type_parameter constraints, its effective interface set is its set of interface_type constraints.
  • 如果沒有 T interface_type 條件約束,但有 type_parameter 條件約束,則它的有效介面集是其 type_parameter 條件約束的有效介面集的聯集。If T has no interface_type constraints but has type_parameter constraints, its effective interface set is the union of the effective interface sets of its type_parameter constraints.
  • 如果 T 同時有 interface_type 條件約束和 type_parameter 條件約束,它的有效介面集就是其 interface_type 條件約束的聯集,以及其 type_parameter 條件約束的有效介面集。If T has both interface_type constraints and type_parameter constraints, its effective interface set is the union of its set of interface_type constraints and the effective interface sets of its type_parameter constraints.

如果型別參數有參考型別條件約束,或其有效基類不是或,則 已知為參考型object System.ValueTypeA type parameter is known to be a reference type if it has the reference type constraint or its effective base class is not object or System.ValueType.

條件約束類型參數類型的值,可以用來存取條件約束所隱含的實例成員。Values of a constrained type parameter type can be used to access the instance members implied by the constraints. 在範例中In the example

interface IPrintable
{
    void Print();
}

class Printer<T> where T: IPrintable
{
    void PrintOne(T x) {
        x.Print();
    }
}

的方法 IPrintable 可以直接在上叫用, x 因為 T 會限制為一律執行 IPrintablethe methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

類別主體Class body

類別的 class_body 定義該類別的成員。The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

部分型別Partial types

類型宣告可以分割成多個 部分類型 宣告。A type declaration can be split across multiple partial type declarations. 您可以遵循本節中的規則,從元件的元件中建立型別宣告,此時在程式的編譯時間與執行時間處理的其餘部分期間,會將它視為單一宣告。The type declaration is constructed from its parts by following the rules in this section, whereupon it is treated as a single declaration during the remainder of the compile-time and run-time processing of the program.

如果包含修飾詞, class_declarationstruct_declarationinterface_declaration 代表部分類型宣告 partialA class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partial 不是關鍵字,而且只有在其中一個關鍵字之前出現 classstructinterface 在類型宣告中或在方法宣告中的型別之前,才做為修飾詞 voidpartial is not a keyword, and only acts as a modifier if it appears immediately before one of the keywords class, struct or interface in a type declaration, or before the type void in a method declaration. 在其他內容中,它可以用來做為一般識別碼。In other contexts it can be used as a normal identifier.

部分類型宣告的每個部分都必須包含 partial 修飾詞。Each part of a partial type declaration must include a partial modifier. 它必須具有相同的名稱,而且必須在相同的命名空間或類型宣告中宣告為其他部分。It must have the same name and be declared in the same namespace or type declaration as the other parts. partial修飾詞表示類型宣告的其他部分可能存在於其他位置,但這類額外的部分不是必要的,它適用于具有單一宣告的型別,以包含修飾詞 partialThe partial modifier indicates that additional parts of the type declaration may exist elsewhere, but the existence of such additional parts is not a requirement; it is valid for a type with a single declaration to include the partial modifier.

部分類型的所有部分都必須一起編譯,讓元件可以在編譯時期合併成單一類型宣告。All parts of a partial type must be compiled together such that the parts can be merged at compile-time into a single type declaration. 部分類型特別不允許擴充已編譯的類型。Partial types specifically do not allow already compiled types to be extended.

您可以使用修飾詞,在多個部分中宣告巢狀型別 partialNested types may be declared in multiple parts by using the partial modifier. 通常,包含的型別也會使用宣告 partial ,而嵌套型別的每個部分則是在包含型別的不同部分中宣告。Typically, the containing type is declared using partial as well, and each part of the nested type is declared in a different part of the containing type.

partial委派或列舉宣告上不允許修飾詞。The partial modifier is not permitted on delegate or enum declarations.

屬性Attributes

部分類型的屬性是藉由將每個元件的屬性(未指定的順序)組合來決定。The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. 如果屬性放置在多個元件上,它就相當於在此類型上指定屬性多次。If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. 例如,兩個部分:For example, the two parts:

[Attr1, Attr2("hello")]
partial class A {}

[Attr3, Attr2("goodbye")]
partial class A {}

相當於宣告,例如:are equivalent to a declaration such as:

[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}

類型參數上的屬性會以類似的方式結合。Attributes on type parameters combine in a similar fashion.

修飾詞Modifiers

當部分類型宣告包含 (、、和修飾詞的協助工具規格時 public protected internal private) 它必須與包含協助工具規格的所有其他部分一致。When a partial type declaration includes an accessibility specification (the public, protected, internal, and private modifiers) it must agree with all other parts that include an accessibility specification. 如果部分類型的部分包含存取範圍規格,則會提供類型 (宣告的 輔助 功能) 的適當預設存取範圍。If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

如果嵌套型別的一或多個部分宣告包含 new 修飾詞,則如果嵌套的型別隱藏繼承的成員 (隱藏 繼承的成員) ,則不會報告任何警告。If one or more partial declarations of a nested type include a new modifier, no warning is reported if the nested type hides an inherited member (Hiding through inheritance).

如果類別的一或多個部分宣告包含 abstract 修飾詞,則類別會被視為抽象 (抽象類別) 。If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). 否則,會將類別視為非抽象。Otherwise, the class is considered non-abstract.

如果類別的一或多個部分宣告包含 sealed 修飾詞,就會將類別視為密封 (sealed 類別) 。If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). 否則,會將類別視為未密封。Otherwise, the class is considered unsealed.

請注意,類別不可同時為 abstract 和 sealed。Note that a class cannot be both abstract and sealed.

unsafe 部分類型宣告上使用修飾詞時,只會將該特定部分視為不安全的內容, (不安全 的內容) 。When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (Unsafe contexts).

型別參數和條件約束Type parameters and constraints

如果泛型型別是在多個部分中宣告,則每個元件都必須陳述型別參數。If a generic type is declared in multiple parts, each part must state the type parameters. 每個部分都必須有相同數目的型別參數,且每個類型參數的名稱必須相同,順序也是如此。Each part must have the same number of type parameters, and the same name for each type parameter, in order.

當部分泛型型別宣告包含 (where 子句) 的條件約束時,條件約束必須與包含條件約束的所有其他部分一致。When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. 具體而言,每個包含條件約束的元件都必須具有相同類型參數集的條件約束,而且每個類型參數的主要、次要和函式條件約束集合必須相等。Specifically, each part that includes constraints must have constraints for the same set of type parameters, and for each type parameter the sets of primary, secondary, and constructor constraints must be equivalent. 如果兩組條件約束包含相同的成員,則相等。Two sets of constraints are equivalent if they contain the same members. 如果部分泛型型別的部分未指定型別參數條件約束,則類型參數會被視為不受限制。If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

範例The example

partial class Dictionary<K,V>
    where K: IComparable<K>
    where V: IKeyProvider<K>, IPersistable
{
    ...
}

partial class Dictionary<K,V>
    where V: IPersistable, IKeyProvider<K>
    where K: IComparable<K>
{
    ...
}

partial class Dictionary<K,V>
{
    ...
}

是正確的,因為包含 (前兩個) 之條件約束的部分,會將相同的一組相同的主要、次要和函式條件約束,分別指定給同一組類型參數。is correct because those parts that include constraints (the first two) effectively specify the same set of primary, secondary, and constructor constraints for the same set of type parameters, respectively.

基底類別Base class

當部分類別宣告包含基類規格時,它必須與所有包含基類規格的其他部分一致。When a partial class declaration includes a base class specification it must agree with all other parts that include a base class specification. 如果部分類別的部分未包含基類規格,基類就會變成 System.Object) (基類If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

基底介面Base interfaces

在多個部分中宣告之型別的基底介面集合,就是每個元件上所指定之基底介面的聯集。The set of base interfaces for a type declared in multiple parts is the union of the base interfaces specified on each part. 特定基底介面的每個部分只能命名一次,但多個部分可以將相同的基底介面命名為 (s) 。A particular base interface may only be named once on each part, but it is permitted for multiple parts to name the same base interface(s). 任何指定基底介面的成員都只能有一個執行。There must only be one implementation of the members of any given base interface.

在範例中In the example

partial class C: IA, IB {...}

partial class C: IC {...}

partial class C: IA, IB {...}

類別的基底介面集 CIAIBICthe set of base interfaces for class C is IA, IB, and IC.

一般而言,每個元件都提供在該元件上宣告 () 介面的實值;不過,這不是必要條件。Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. 元件可能會針對不同元件上所宣告的介面提供實作為:A part may provide the implementation for an interface declared on a different part:

partial class X
{
    int IComparable.CompareTo(object o) {...}
}

partial class X: IComparable
{
    ...
}

成員Members

除了部分方法 (部分方法) 之外,在多個部分中宣告之類型的成員集合,只是在每個部分中宣告之成員集合的聯集。With the exception of partial methods (Partial methods), the set of members of a type declared in multiple parts is simply the union of the set of members declared in each part. 類型宣告的所有部分主體都會共用相同的宣告 空間 (宣告) ,而且每個 成員 (範圍的範圍) 會延伸至所有部分的主體。The bodies of all parts of the type declaration share the same declaration space (Declarations), and the scope of each member (Scopes) extends to the bodies of all the parts. 任何成員的存取範圍定義域一律包含封入類型的所有部分; private 在某個部分中宣告的成員可以從另一個部分自由存取。The accessibility domain of any member always includes all the parts of the enclosing type; a private member declared in one part is freely accessible from another part. 除非該成員是具有修飾詞的型別,否則,在多個類型的部分中宣告相同的成員是編譯時期錯誤 partialIt is a compile-time error to declare the same member in more than one part of the type, unless that member is a type with the partial modifier.

partial class A
{
    int x;                     // Error, cannot declare x more than once

    partial class Inner        // Ok, Inner is a partial type
    {
        int y;
    }
}

partial class A
{
    int x;                     // Error, cannot declare x more than once

    partial class Inner        // Ok, Inner is a partial type
    {
        int z;
    }
}

型別中的成員順序對 c # 程式碼來說很重要,但在與其他語言和環境互動時可能很重要。The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. 在這些情況下,在多個部分宣告的型別內的成員順序是未定義的。In these cases, the ordering of members within a type declared in multiple parts is undefined.

部分方法Partial methods

部分方法可以定義在類型宣告的一個部分中,並在另一個部分中執行。Partial methods can be defined in one part of a type declaration and implemented in another. 執行是選擇性的;如果沒有元件實作為部分方法,則部分方法宣告和它的所有呼叫都會從元件組合所產生的類型宣告中移除。The implementation is optional; if no part implements the partial method, the partial method declaration and all calls to it are removed from the type declaration resulting from the combination of the parts.

部分方法無法定義存取修飾詞,而是隱含的 privatePartial methods cannot define access modifiers, but are implicitly private. 其傳回型別必須是 void ,而且它們的參數不能有 out 修飾元。Their return type must be void, and their parameters cannot have the out modifier. 只有當識別碼出現在型別前面時,才會將識別碼辨識 partial 為方法宣告中的特殊關鍵字, void 否則就可以用來做為一般識別碼。The identifier partial is recognized as a special keyword in a method declaration only if it appears right before the void type; otherwise it can be used as a normal identifier. 部分方法無法明確地執行介面方法。A partial method cannot explicitly implement interface methods.

部分方法宣告有兩種:如果方法宣告的主體是分號,宣告就稱為 *定義部分方法 宣告 _。There are two kinds of partial method declarations: If the body of the method declaration is a semicolon, the declaration is said to be a *defining partial method declaration _. 如果主體是以 _block * 的形式提供,就會將宣告視為實作為 部分方法 宣告。If the body is given as a _block*, the declaration is said to be an implementing partial method declaration. 在類型宣告的各個部分中,只能有一個定義具有指定簽章的部分方法宣告,而且只能有一個執行中的部分方法宣告與指定的簽章。Across the parts of a type declaration there can be only one defining partial method declaration with a given signature, and there can be only one implementing partial method declaration with a given signature. 如果指定了實部分方法宣告,則必須有對應的定義部分方法宣告,且宣告必須符合下列各項的指定:If an implementing partial method declaration is given, a corresponding defining partial method declaration must exist, and the declarations must match as specified in the following:

  • 宣告必須有相同的修飾詞 (但不一定會以相同的順序) 、方法名稱、類型參數數目和參數數目。The declarations must have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters.
  • 宣告中的對應參數必須有相同的修飾詞 (但不一定會以相同的順序) ,而且相同的類型 () 類型參數名稱的模數差異。Corresponding parameters in the declarations must have the same modifiers (although not necessarily in the same order) and the same types (modulo differences in type parameter names).
  • 宣告中的對應型別參數必須有相同的條件約束 () 類型參數名稱的模數差異。Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

執行中的部分方法宣告可以與對應的定義部分方法宣告出現在相同的部分。An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

只有定義部分方法會參與多載解析。Only a defining partial method participates in overload resolution. 因此,不論是否有指定的實作為宣告,調用運算式都可以解析為部分方法的調用。Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. 由於部分方法一律會傳回 void ,因此這類調用運算式一律會是運算式語句。Because a partial method always returns void, such invocation expressions will always be expression statements. 此外,由於部分方法是隱含的 private ,因此這類語句一律會出現在宣告部分方法的型別宣告的其中一個部分內。Furthermore, because a partial method is implicitly private, such statements will always occur within one of the parts of the type declaration within which the partial method is declared.

如果部分類型宣告的部分包含指定部分方法的實作為宣告,則叫用它的任何運算式語句只會從組合類型宣告中移除。If no part of a partial type declaration contains an implementing declaration for a given partial method, any expression statement invoking it is simply removed from the combined type declaration. 因此,調用運算式(包括任何組成的運算式)在執行時間不會有任何作用。Thus the invocation expression, including any constituent expressions, has no effect at run-time. 部分方法本身也會被移除,且不會是組合型別宣告的成員。The partial method itself is also removed and will not be a member of the combined type declaration.

如果有指定部分方法的實作為宣告,則會保留部分方法的調用。If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. 部分方法會引發類似于執行部分方法宣告的方法宣告,但下列情況除外:The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • partial不包含修飾詞The partial modifier is not included
  • 產生的方法宣告中的屬性是定義的結合屬性,以及以未指定循序執行的部分方法宣告。The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. 不會移除重複專案。Duplicates are not removed.
  • 產生之方法宣告的參數上的屬性是定義之對應參數的結合屬性,以及以未指定循序執行的部分方法宣告。The attributes on the parameters of the resulting method declaration are the combined attributes of the corresponding parameters of the defining and the implementing partial method declaration in unspecified order. 不會移除重複專案。Duplicates are not removed.

如果針對部分方法 M 指定了定義宣告,但不是實作為宣告,則適用下列限制:If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

部分方法適用于允許類型宣告的某個部分自訂另一個元件的行為,例如,工具所產生的行為。Partial methods are useful for allowing one part of a type declaration to customize the behavior of another part, e.g., one that is generated by a tool. 請考慮下列部分類別聲明:Consider the following partial class declaration:

partial class Customer
{
    string name;

    public string Name {
        get { return name; }
        set {
            OnNameChanging(value);
            name = value;
            OnNameChanged();
        }

    }

    partial void OnNameChanging(string newName);

    partial void OnNameChanged();
}

如果編譯此類別時沒有任何其他部分,則會移除定義的部分方法宣告及其調用,而且產生的合併類別宣告將等同于下列專案:If this class is compiled without any other parts, the defining partial method declarations and their invocations will be removed, and the resulting combined class declaration will be equivalent to the following:

class Customer
{
    string name;

    public string Name {
        get { return name; }
        set { name = value; }
    }
}

不過,假設有另一個元件會提供部分方法的實作為宣告:Assume that another part is given, however, which provides implementing declarations of the partial methods:

partial class Customer
{
    partial void OnNameChanging(string newName)
    {
        Console.WriteLine("Changing " + name + " to " + newName);
    }

    partial void OnNameChanged()
    {
        Console.WriteLine("Changed to " + name);
    }
}

然後,產生的組合類別宣告將等同于下列專案:Then the resulting combined class declaration will be equivalent to the following:

class Customer
{
    string name;

    public string Name {
        get { return name; }
        set {
            OnNameChanging(value);
            name = value;
            OnNameChanged();
        }

    }

    void OnNameChanging(string newName)
    {
        Console.WriteLine("Changing " + name + " to " + newName);
    }

    void OnNameChanged()
    {
        Console.WriteLine("Changed to " + name);
    }
}

名稱系結Name binding

雖然可延伸型別的每個部分都必須在相同的命名空間中宣告,但這些元件通常是在不同的命名空間宣告中所撰寫。Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. 因此, using 每個部分可能會有不同的指示詞 (使用 指示詞) 。Thus, different using directives (Using directives) may be present for each part. 在一個部分中解讀 (類型) 推斷 的簡單名稱時,只 using 會考慮命名空間宣告 (s) 封入該部分的指示詞。When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. 這可能會導致相同的識別碼在不同的部分具有不同的意義:This may result in the same identifier having different meanings in different parts:

namespace N
{
    using List = System.Collections.ArrayList;

    partial class A
    {
        List x;                // x has type System.Collections.ArrayList
    }
}

namespace N
{
    using List = Widgets.LinkedList;

    partial class A
    {
        List y;                // y has type Widgets.LinkedList
    }
}

類別成員Class members

類別的成員是由其 class_member_declaration 所引進的成員,以及繼承自直接基類的成員所組成。The members of a class consist of the members introduced by its class_member_declaration s and the members inherited from the direct base class.

class_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | destructor_declaration
    | static_constructor_declaration
    | type_declaration
    ;

類別類型的成員分成下列類別:The members of a class type are divided into the following categories:

  • 常數,表示與類別相關聯的常數值 (常數) 。Constants, which represent constant values associated with the class (Constants).
  • 欄位,也就是類別的變數 (欄位) 。Fields, which are the variables of the class (Fields).
  • 方法,可執行類別 (方法) 所能執行的計算和動作。Methods, which implement the computations and actions that can be performed by the class (Methods).
  • 屬性,定義已命名的特性以及與讀取和寫入這些特性相關聯的動作 (屬性) 。Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • 事件,定義可由類別產生的通知 (事件) 。Events, which define notifications that can be generated by the class (Events).
  • 索引子,可讓您以相同的方式來編制類別的實例索引, (以語法的方式) (索引子) 的陣列。Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • 運算子,定義可以套用至類別實例的運算式運算子) (運算子Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • 實例的函式,可執行初始化類別實例 (實例 的函式所需的動作) Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • 在) (的 析構 函數永久捨棄類別實例之前,會執行要執行之動作的析構函數。Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • 靜態的函式,它會執行初始化類別本身 (靜態 的函式) 所需的動作。Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • 類型,代表類別 (巢狀型別) 的本機類型。Types, which represent the types that are local to the class (Nested types).

可以包含可執行程式碼的成員統稱為類別類型的 函數成員Members that can contain executable code are collectively known as the function members of the class type. 類別類型的函式成員是該類別類型的方法、屬性、事件、索引子、運算子、實例的函式和靜態函式。The function members of a class type are the methods, properties, events, indexers, operators, instance constructors, destructors, and static constructors of that class type.

Class_declaration 會) 建立新的宣告空間 (宣告,而立即包含 class_declarationclass_member_declaration 會將新成員引入這個宣告空間中。A class_declaration creates a new declaration space (Declarations), and the class_member_declaration s immediately contained by the class_declaration introduce new members into this declaration space. 下列規則適用于 class_member_declaration s:The following rules apply to class_member_declaration s:

  • 實例的函式、析構函數和靜態的函式必須與直接封入類別的名稱相同。Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. 所有其他成員都必須有與直接封入類別名稱不同的名稱。All other members must have names that differ from the name of the immediately enclosing class.
  • 常數、欄位、屬性、事件或類型的名稱必須與在相同類別中宣告之所有其他成員的名稱不同。The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • 方法的名稱必須與在相同類別中宣告的所有其他非方法的名稱不同。The name of a method must differ from the names of all other non-methods declared in the same class. 此外,簽章 (簽章 方法的多載) 必須與在相同類別中宣告之所有其他方法的簽章不同,而且在相同類別中宣告的兩個方法可能不會有和完全不同的簽章 ref outIn addition, the signature (Signatures and overloading) of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.
  • 實例函式的簽章必須與在相同類別中宣告的所有其他實例的簽章不同,而且在相同類別中宣告的兩個函式不能有與完全不同的簽章 ref outThe signature of an instance constructor must differ from the signatures of all other instance constructors declared in the same class, and two constructors declared in the same class may not have signatures that differ solely by ref and out.
  • 索引子的簽章必須與在相同類別中宣告之所有其他索引子的簽章不同。The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • 運算子的簽章必須與在相同類別中宣告之所有其他運算子的簽章不同。The signature of an operator must differ from the signatures of all other operators declared in the same class.

類別型別的繼承成員 (繼承) 不屬於類別的宣告空間的一部分。The inherited members of a class type (Inheritance) are not part of the declaration space of a class. 因此,衍生的類別可以宣告與繼承成員具有相同名稱或簽章的成員, (它實際上會隱藏繼承的成員) 。Thus, a derived class is allowed to declare a member with the same name or signature as an inherited member (which in effect hides the inherited member).

實例類型The instance type

每個類別宣告都有相關聯的系結類型 (系結 和未 系結的型別) 的 實例類型Each class declaration has an associated bound type (Bound and unbound types), the instance type. 若為泛型類別宣告,則會建立實例類型,方法是從型別宣告建立 (的 結構 化型別) ,其中每個提供的型別引數都是對應的型別參數。For a generic class declaration, the instance type is formed by creating a constructed type (Constructed types) from the type declaration, with each of the supplied type arguments being the corresponding type parameter. 由於實例型別使用型別參數,因此只能在類型參數位於範圍中時使用。也就是說,在類別宣告內。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. 實例類型是在 this 類別宣告內撰寫之程式碼的型別。The instance type is the type of this for code written inside the class declaration. 針對非泛型類別,實例類型只是宣告的類別。For non-generic classes, the instance type is simply the declared class. 以下顯示數個類別宣告及其實例類型:The following shows several class declarations along with their instance types:

class A<T>                           // instance type: A<T>
{
    class B {}                       // instance type: A<T>.B
    class C<U> {}                    // instance type: A<T>.C<U>
}

class D {}                           // instance type: D

結構化類型的成員Members of constructed types

您可以針對成員宣告中的每個 type_parameter ,以替代結構類型的對應 type_argument ,來取得所建立之非繼承成員的型別。The non-inherited members of a constructed type are obtained by substituting, for each type_parameter in the member declaration, the corresponding type_argument of the constructed type. 替代程式是以型別宣告的語義意義為基礎,而不是單純的文字替代。The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

例如,假設有泛型類別宣告For example, given the generic class declaration

class Gen<T,U>
{
    public T[,] a;
    public void G(int i, T t, Gen<U,T> gt) {...}
    public U Prop { get {...} set {...} }
    public int H(double d) {...}
}

結構化類型 Gen<int[],IComparable<string>> 具有下列成員:the constructed type Gen<int[],IComparable<string>> has the following members:

public int[,][] a;
public void G(int i, int[] t, Gen<IComparable<string>,int[]> gt) {...}
public IComparable<string> Prop { get {...} set {...} }
public int H(double d) {...}

泛型類別宣告中的成員類型 a Gen 是「二維陣列」 T ,因此在上述的型別中,成員的型別 a 是「一維陣列的二維陣列」 intint[,][]The type of the member a in the generic class declaration Gen is "two-dimensional array of T", so the type of the member a in the constructed type above is "two-dimensional array of one-dimensional array of int", or int[,][].

在實例函式成員內,的類型 this 是實例類型, (包含宣告的 實例類型) 。Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

泛型類別的所有成員都可以使用來自任何封入類別的型別參數,不論是直接或作為結構化型別的一部分。All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. 當特定封閉的結構類型 (開啟和關閉 型別時,) 用於執行時間,則每次使用型別參數都會取代為所提供的實際型別引數。When a particular closed constructed type (Open and closed types) is used at run-time, each use of a type parameter is replaced with the actual type argument supplied to the constructed type. 例如:For example:

class C<V>
{
    public V f1;
    public C<V> f2 = null;

    public C(V x) {
        this.f1 = x;
        this.f2 = this;
    }
}

class Application
{
    static void Main() {
        C<int> x1 = new C<int>(1);
        Console.WriteLine(x1.f1);        // Prints 1

        C<double> x2 = new C<double>(3.1415);
        Console.WriteLine(x2.f1);        // Prints 3.1415
    }
}

繼承Inheritance

類別會 繼承 其直接基底類別型別的成員。A class inherits the members of its direct base class type. 繼承表示類別會隱含地包含其直接基類型別的所有成員,但實例的函式、析構函式和基類的靜態函式除外。Inheritance means that a class implicitly contains all members of its direct base class type, except for the instance constructors, destructors and static constructors of the base class. 繼承的一些重要層面如下:Some important aspects of inheritance are:

  • 繼承是可轉移的。Inheritance is transitive. 如果 C 衍生自 B ,而且 B 衍生自 A ,則會 C 繼承在中宣告的成員,以及中宣告 B 的成員 AIf C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A.
  • 衍生類別會擴充其直接基類。A derived class extends its direct base class. 衍生類別可以在其繼承的成員中新增新的成員,但無法移除所繼承成員的定義。A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • 實例的函式、析構函數和靜態的函式不會被繼承,但是所有其他成員都是,不論其宣告的存取範圍 (成員存取) 。Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). 不過,根據其宣告的存取範圍,繼承的成員可能無法在衍生類別中存取。However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • 衍生的類別可以藉由宣告具有相同名稱或簽章的新成員,隱藏透過繼承) 繼承的成員 (隱藏。A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. 不過請注意,隱藏繼承的成員並不會移除該成員,它只會讓該成員直接透過衍生類別來存取。Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • 類別的實例包含一組在類別及其基類中宣告的所有實例欄位,以及隱含的轉換 (隱含的參考轉換) 存在於衍生類別類型與其任何基類類型之間。An instance of a class contains a set of all instance fields declared in the class and its base classes, and an implicit conversion (Implicit reference conversions) exists from a derived class type to any of its base class types. 因此,某些衍生類別之實例的參考可以視為其任何基類之實例的參考。Thus, a reference to an instance of some derived class can be treated as a reference to an instance of any of its base classes.
  • 類別可以宣告虛擬方法、屬性和索引子,而衍生類別可以覆寫這些函數成員的實作為。A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. 這可讓類別展現多型行為,其中函數成員調用所執行的動作會根據執行該函式成員之實例的執行時間類型而有所不同。This enables classes to exhibit polymorphic behavior wherein the actions performed by a function member invocation varies depending on the run-time type of the instance through which that function member is invoked.

已建立之類別型別的繼承成員是 (基類) 的直屬基類型別成員,它是藉由將所結構化類型的型別引數替換成 class_base 規格中的每個對應型別參數,來找到。The inherited member of a constructed class type are the members of the immediate base class type (Base classes), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the class_base specification. 接著,這些成員會藉由替代成員宣告中的每個 type_parameter ,來轉換 class_base 規格的對應 type_argumentThese members, in turn, are transformed by substituting, for each type_parameter in the member declaration, the corresponding type_argument of the class_base specification.

class B<U>
{
    public U F(long index) {...}
}

class D<T>: B<T[]>
{
    public T G(string s) {...}
}

在上述範例中,使用型別 D<int> 參數的型別引數來取代所取得的非繼承成員 public int G(string s) int TIn the above example, the constructed type D<int> has a non-inherited member public int G(string s) obtained by substituting the type argument int for the type parameter T. D<int> 也有來自類別宣告的繼承成員 BD<int> also has an inherited member from the class declaration B. 這個繼承的成員是藉由 B<int[]> D<int> int T 在基類規格中替代來判斷的基類型別來決定 B<T[]>This inherited member is determined by first determining the base class type B<int[]> of D<int> by substituting int for T in the base class specification B<T[]>. 然後,當的型別引數為 B int[] 時,會將取代為,並產生 U public U F(long index) 繼承的成員 public int[] F(long index)Then, as a type argument to B, int[] is substituted for U in public U F(long index), yielding the inherited member public int[] F(long index).

新的修飾詞The new modifier

允許 class_member_declaration 宣告與繼承成員具有相同名稱或簽章的成員。A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. 發生這種情況時,即表示衍生類別成員 隱藏 基類成員。When this occurs, the derived class member is said to hide the base class member. 隱藏繼承的成員不會被視為錯誤,但會導致編譯器發出警告。Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. 若要隱藏警告,衍生類別成員的宣告可以包含修飾詞, new 以表示衍生成員要隱藏基底成員。To suppress the warning, the declaration of the derived class member can include a new modifier to indicate that the derived member is intended to hide the base member. 本主題將進一步討論如何 透過繼承進行隱藏This topic is discussed further in Hiding through inheritance.

如果 new 修飾詞包含在未隱藏繼承成員的宣告中,則會發出該效果的警告。If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. 移除修飾詞會隱藏這個警告 newThis warning is suppressed by removing the new modifier.

存取修飾詞Access modifiers

Class_member_declaration 可以有其中一種宣告的輔助 功能 (宣告的協助工具) : publicprotected internalprotectedinternalprivateA class_member_declaration can have any one of the five possible kinds of declared accessibility (Declared accessibility): public, protected internal, protected, internal, or private. 除了組合之外 protected internal ,它也是編譯時期錯誤,可指定一個以上的存取修飾詞。Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. class_member_declaration 不包含任何存取修飾詞時, private 會假設為。When a class_member_declaration does not include any access modifiers, private is assumed.

組成類型Constituent types

成員宣告中使用的類型稱為該成員的組成類型。Types that are used in the declaration of a member are called the constituent types of that member. 可能的組成類型是常數、欄位、屬性、事件或索引子的類型、方法或運算子的傳回類型,以及方法、索引子、運算子或實例的函式的參數類型。Possible constituent types are the type of a constant, field, property, event, or indexer, the return type of a method or operator, and the parameter types of a method, indexer, operator, or instance constructor. 成員的組成型別至少必須是可存取的,因為該成員本身 (存取範圍條件約束) 。The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

靜態和實例成員Static and instance members

類別的成員可以是 *靜態成員 _ 或 _ 實例成員 *。Members of a class are either static members _ or _instance members**. 一般來說,將靜態成員視為屬於類別型別和實例成員,做為) 類別型別 (實例的物件,是很有用的。Generally speaking, it is useful to think of static members as belonging to class types and instance members as belonging to objects (instances of class types).

當欄位、方法、屬性、事件、運算子或函式宣告包含修飾詞時 static ,它會宣告一個靜態成員。When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. 此外,常數或型別宣告也會隱含地宣告靜態成員。In addition, a constant or type declaration implicitly declares a static member. 靜態成員具有下列特性:Static members have the following characteristics:

  • M 表單的 Member_access (成員存取) 中參考靜態成員時 E.ME 必須代表包含的型別 MWhen a static member M is referenced in a member_access (Member access) of the form E.M, E must denote a type containing M. 這是用來表示實例的編譯時期錯誤 EIt is a compile-time error for E to denote an instance.
  • 靜態欄位只會識別一個儲存位置,以供特定封閉式類別型別的所有實例共用。A static field identifies exactly one storage location to be shared by all instances of a given closed class type. 無論指定的封閉類別類型有多少個實例,靜態欄位都只會有一個複本。No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.
  • 靜態函式成員 (方法、屬性、事件、運算子或函式) 不會在特定的實例上運作,而是 this 在這類函數成員中參考的編譯時期錯誤。A static function member (method, property, event, operator, or constructor) does not operate on a specific instance, and it is a compile-time error to refer to this in such a function member.

當欄位、方法、屬性、事件、索引子、函式或函式宣告不包含修飾詞時 static ,它會宣告實例成員。When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (實例成員有時稱為非靜態成員。 ) 實例成員具有下列特性:(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • M 表單的 Member_access (成員存取) 中參考實例成員時 E.ME 必須代表包含的型別實例 MWhen an instance member M is referenced in a member_access (Member access) of the form E.M, E must denote an instance of a type containing M. 這是用來表示類型的系結階段錯誤 EIt is a binding-time error for E to denote a type.
  • 類別的每個實例都包含類別的一組個別的實例欄位。Every instance of a class contains a separate set of all instance fields of the class.
  • 實例函數成員 (方法、屬性、索引子、實例的函式或函式) 在類別的指定實例上運作,而且這個實例可以 this (此存取) 存取。An instance function member (method, property, indexer, instance constructor, or destructor) operates on a given instance of the class, and this instance can be accessed as this (This access).

下列範例說明用來存取靜態和實例成員的規則:The following example illustrates the rules for accessing static and instance members:

class Test
{
    int x;
    static int y;

    void F() {
        x = 1;            // Ok, same as this.x = 1
        y = 1;            // Ok, same as Test.y = 1
    }

    static void G() {
        x = 1;            // Error, cannot access this.x
        y = 1;            // Ok, same as Test.y = 1
    }

    static void Main() {
        Test t = new Test();
        t.x = 1;          // Ok
        t.y = 1;          // Error, cannot access static member through instance
        Test.x = 1;       // Error, cannot access instance member through type
        Test.y = 1;       // Ok
    }
}

F方法會顯示在實例函數成員中, Simple_name (簡單名稱) 可用來存取實例成員和靜態成員。The F method shows that in an instance function member, a simple_name (Simple names) can be used to access both instance members and static members. G方法會顯示在靜態函式成員中,透過 simple_name 存取實例成員時,會發生編譯時期錯誤。The G method shows that in a static function member, it is a compile-time error to access an instance member through a simple_name. Main方法會顯示在 Member_access (成員存取) 中,必須透過實例來存取實例成員,而且必須透過類型存取靜態成員。The Main method shows that in a member_access (Member access), instance members must be accessed through instances, and static members must be accessed through types.

巢狀型別Nested types

在類別或結構宣告中宣告的型別稱為 *nested 型 別 _。A type declared within a class or struct declaration is called a *nested type _. 在編譯單位或命名空間內宣告的型別稱為 _ *非嵌套型 別 * *。A type that is declared within a compilation unit or namespace is called a _*non-nested type**.

在範例中In the example

using System;

class A
{
    class B
    {
        static void F() {
            Console.WriteLine("A.B.F");
        }
    }
}

類別 B 是嵌套的型別,因為它是在類別內宣告 A ,而類別 A 則是非嵌套型別,因為它是在編譯單位中宣告的。class B is a nested type because it is declared within class A, and class A is a non-nested type because it is declared within a compilation unit.

完整名稱Fully qualified name

針對巢狀型別) 完整名稱 (完整 名稱, S.N 其中 S 是宣告類型之類型的完整名稱 NThe fully qualified name (Fully qualified names) for a nested type is S.N where S is the fully qualified name of the type in which type N is declared.

已宣告存取範圍Declared accessibility

非巢狀型別可以有 public 或宣告 internal 存取範圍,且 internal 預設會宣告存取範圍。Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. 巢狀型別也可以有這些形式的宣告存取範圍,以及一或多個其他形式的宣告存取範圍,取決於包含的型別是否為類別或結構而定:Nested types can have these forms of declared accessibility too, plus one or more additional forms of declared accessibility, depending on whether the containing type is a class or struct:

  • 在類別中宣告的嵌套型別可以有五種形式的宣告存取範圍 (publicprotected internalprotectedinternal 或) , private 而且像其他類別成員一樣,預設為宣告的 private 協助工具。A nested type that is declared in a class can have any of five forms of declared accessibility (public, protected internal, protected, internal, or private) and, like other class members, defaults to private declared accessibility.
  • 在結構中宣告的嵌套型別可以有三種形式的宣告存取範圍 (publicinternal 或) , private 而且像其他結構成員一樣,預設為宣告的 private 協助工具。A nested type that is declared in a struct can have any of three forms of declared accessibility (public, internal, or private) and, like other struct members, defaults to private declared accessibility.

範例The example

public class List
{
    // Private data structure
    private class Node
    { 
        public object Data;
        public Node Next;

        public Node(object data, Node next) {
            this.Data = data;
            this.Next = next;
        }
    }

    private Node first = null;
    private Node last = null;

    // Public interface
    public void AddToFront(object o) {...}
    public void AddToBack(object o) {...}
    public object RemoveFromFront() {...}
    public object RemoveFromBack() {...}
    public int Count { get {...} }
}

宣告私用的嵌套類別 Nodedeclares a private nested class Node.

隱藏Hiding

巢狀型別可以隱藏) 基底成員隱藏 (名稱A nested type may hide (Name hiding) a base member. new可以在嵌套的類型宣告上使用修飾詞,以便明確地表示隱藏。The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. 範例The example

using System;

class Base
{
    public static void M() {
        Console.WriteLine("Base.M");
    }
}

class Derived: Base 
{
    new public class M 
    {
        public static void F() {
            Console.WriteLine("Derived.M.F");
        }
    }
}

class Test 
{
    static void Main() {
        Derived.M.F();
    }
}

顯示 M 隱藏中所定義之方法的嵌套類別 M Baseshows a nested class M that hides the method M defined in Base.

這種存取權this access

巢狀型別和其包含類型並沒有與 this_access (此存取) 相關的特殊關聯性。A nested type and its containing type do not have a special relationship with regard to this_access (This access). 具體而言, this 巢狀型別內不能用來參考包含型別的實例成員。Specifically, this within a nested type cannot be used to refer to instance members of the containing type. 當巢狀型別需要存取其包含類型的實例成員時,可以提供包含類型之實例的,做為巢狀型別的函式引數,以提供存取權 thisIn cases where a nested type needs access to the instance members of its containing type, access can be provided by providing the this for the instance of the containing type as a constructor argument for the nested type. 下列範例The following example

using System;

class C
{
    int i = 123;

    public void F() {
        Nested n = new Nested(this);
        n.G();
    }

    public class Nested
    {
        C this_c;

        public Nested(C c) {
            this_c = c;
        }

        public void G() {
            Console.WriteLine(this_c.i);
        }
    }
}

class Test
{
    static void Main() {
        C c = new C();
        c.F();
    }
}

顯示這項技術。shows this technique. 的實例會 C 建立的實例 Nested ,並將其本身傳給的函式,以便 this Nested 為實例成員提供後續的存取權 CAn instance of C creates an instance of Nested and passes its own this to Nested's constructor in order to provide subsequent access to C's instance members.

存取包含類型的私用和受保護成員Access to private and protected members of the containing type

巢狀型別可以存取其包含類型可存取的所有成員,包括具有和宣告協助工具之包含型別的成員 private protectedA nested type has access to all of the members that are accessible to its containing type, including members of the containing type that have private and protected declared accessibility. 範例The example

using System;

class C 
{
    private static void F() {
        Console.WriteLine("C.F");
    }

    public class Nested 
    {
        public static void G() {
            F();
        }
    }
}

class Test 
{
    static void Main() {
        C.Nested.G();
    }
}

顯示 C 包含嵌套類別的類別 Nestedshows a class C that contains a nested class Nested. 在中 Nested ,此方法會 G 呼叫中定義的靜態方法 F C ,並具有私用宣告的 F 存取範圍。Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

巢狀型別也可以存取其包含類型之基底類型中所定義的受保護成員。A nested type also may access protected members defined in a base type of its containing type. 在範例中In the example

using System;

class Base 
{
    protected void F() {
        Console.WriteLine("Base.F");
    }
}

class Derived: Base 
{
    public class Nested 
    {
        public void G() {
            Derived d = new Derived();
            d.F();        // ok
        }
    }
}

class Test 
{
    static void Main() {
        Derived.Nested n = new Derived.Nested();
        n.G();
    }
}

嵌套類別會藉 Derived.Nested 由呼叫的實例,存取基類中所定義的受保護方法 F Derived Base Derivedthe nested class Derived.Nested accesses the protected method F defined in Derived's base class, Base, by calling through an instance of Derived.

泛型類別中的巢狀型別Nested types in generic classes

泛型類別宣告可以包含巢狀型別宣告。A generic class declaration can contain nested type declarations. 封入類別的型別參數可以用於巢狀型別內。The type parameters of the enclosing class can be used within the nested types. 嵌套型別宣告可以包含僅適用于巢狀型別的其他型別參數。A nested type declaration can contain additional type parameters that apply only to the nested type.

泛型類別宣告中包含的每個類型宣告都是隱含的泛型型別宣告。Every type declaration contained within a generic class declaration is implicitly a generic type declaration. 撰寫嵌套于泛型型別內之類型的參考時,必須命名包含的型別引數,包括其型別引數。When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. 不過,在外部類別中,可以在不限定的情況下使用巢狀型別。在建立巢狀型別時,可以隱含地使用外部類別的實例類型。However, from within the outer class, the nested type can be used without qualification; the instance type of the outer class can be implicitly used when constructing the nested type. 下列範例顯示三種不同的正確方式來參考建立自的建立型別, Inner 前兩個是相等的:The following example shows three different correct ways to refer to a constructed type created from Inner; the first two are equivalent:

class Outer<T>
{
    class Inner<U>
    {
        public static void F(T t, U u) {...}
    }

    static void F(T t) {
        Outer<T>.Inner<string>.F(t, "abc");      // These two statements have
        Inner<string>.F(t, "abc");               // the same effect

        Outer<int>.Inner<string>.F(3, "abc");    // This type is different

        Outer.Inner<string>.F(t, "abc");         // Error, Outer needs type arg
    }
}

雖然這是不良的程式設計樣式,但嵌套型別中的型別參數可以隱藏在外部型別中宣告的成員或型別參數:Although it is bad programming style, a type parameter in a nested type can hide a member or type parameter declared in the outer type:

class Outer<T>
{
    class Inner<T>        // Valid, hides Outer's T
    {
        public T t;       // Refers to Inner's T
    }
}

保留的成員名稱Reserved member names

為了加速基礎 c # 執行時間的執行,每個屬於屬性、事件或索引子的來源成員宣告都必須根據成員宣告的種類、其名稱和類型,保留兩個方法簽章。To facilitate the underlying C# run-time implementation, for each source member declaration that is a property, event, or indexer, the implementation must reserve two method signatures based on the kind of the member declaration, its name, and its type. 這是一個編譯時期錯誤,程式會宣告其簽章符合其中一個保留簽章的成員,即使基礎執行時間的執行不使用這些保留。It is a compile-time error for a program to declare a member whose signature matches one of these reserved signatures, even if the underlying run-time implementation does not make use of these reservations.

保留的名稱不會引入宣告,因此不會參與成員查閱。The reserved names do not introduce declarations, thus they do not participate in member lookup. 不過,宣告相關聯的保留方法簽章會參與繼承 (繼承) ,並可 new (新的修飾 詞) 來隱藏該修飾詞。However, a declaration's associated reserved method signatures do participate in inheritance (Inheritance), and can be hidden with the new modifier (The new modifier).

這些名稱的保留有三個用途:The reservation of these names serves three purposes:

  • 允許基礎執行使用一般識別碼作為 get 或 set 存取 c # 語言功能的方法名稱。To allow the underlying implementation to use an ordinary identifier as a method name for get or set access to the C# language feature.
  • 允許其他語言使用一般識別碼作為方法名稱,以取得或設定 c # 語言功能的存取權。To allow other languages to interoperate using an ordinary identifier as a method name for get or set access to the C# language feature.
  • 若要協助確保由另一個符合的編譯器接受的來源可由其他人接受,請在所有 c # 執行中讓保留成員名稱的細節保持一致。To help ensure that the source accepted by one conforming compiler is accepted by another, by making the specifics of reserved member names consistent across all C# implementations.

(析構 函式的函式宣告) 也會讓簽章保留 (針對) 所保留的成員名稱The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

保留給屬性的成員名稱Member names reserved for properties

若為 P 類型 (屬性) 的屬性 T ,則會保留下列簽章:For a property P (Properties) of type T, the following signatures are reserved:

T get_P();
void set_P(T value);

這兩個簽章都會保留,即使屬性為唯讀或僅限寫入也是一樣。Both signatures are reserved, even if the property is read-only or write-only.

在範例中In the example

using System;

class A
{
    public int P {
        get { return 123; }
    }
}

class B: A
{
    new public int get_P() {
        return 456;
    }

    new public void set_P(int value) {
    }
}

class Test
{
    static void Main() {
        B b = new B();
        A a = b;
        Console.WriteLine(a.P);
        Console.WriteLine(b.P);
        Console.WriteLine(b.get_P());
    }
}

類別會 A 定義唯讀屬性 P ,進而保留和方法的簽章 get_P set_Pa class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. 類別 B 衍生自 A ,並隱藏這兩個保留的簽章。A class B derives from A and hides both of these reserved signatures. 此範例會產生輸出:The example produces the output:

123
123
456

保留給事件的成員名稱Member names reserved for events

如果是 E 委派類型) 事件 (事件 T ,則會保留下列簽章:For an event E (Events) of delegate type T, the following signatures are reserved:

void add_E(T handler);
void remove_E(T handler);

保留給索引子的成員名稱Member names reserved for indexers

若為具有參數清單之類型的索引子 (索引子) T L ,則會保留下列簽章:For an indexer (Indexers) of type T with parameter-list L, the following signatures are reserved:

T get_Item(L);
void set_Item(L, T value);

這兩個簽章都會保留,即使索引子為唯讀或僅限寫入。Both signatures are reserved, even if the indexer is read-only or write-only.

此外, Item 也會保留成員名稱。Furthermore the member name Item is reserved.

保留給析構的成員名稱Member names reserved for destructors

若為 包含) 之 (的析構函 式的類別,則會保留下列簽章:For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

常數Constants

*常數 _ 是代表常數值的類別成員:可在編譯時期計算的值。A *constant _ is a class member that represents a constant value: a value that can be computed at compile-time. _Constant_declaration * 引進了一或多個指定類型的常數。A _constant_declaration* introduces one or more constants of a given type.

constant_declaration
    : attributes? constant_modifier* 'const' type constant_declarators ';'
    ;

constant_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Constant_declaration 可能包含一組 屬性 (屬性) 、 new (新修飾詞) 的修飾詞,以及四個存取修飾詞 (存取修飾詞) 的有效組合。A constant_declaration may include a set of attributes (Attributes), a new modifier (The new modifier), and a valid combination of the four access modifiers (Access modifiers). 屬性和修飾詞適用于 constant_declaration 所宣告的所有成員。The attributes and modifiers apply to all of the members declared by the constant_declaration. 即使常數視為靜態成員, constant_declaration 也不需要或不允許修飾詞 staticEven though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. 在常數宣告中多次出現相同的修飾詞時,會發生錯誤。It is an error for the same modifier to appear multiple times in a constant declaration.

Constant_declaration 別會指定宣告所引進之成員的型別。The type of a constant_declaration specifies the type of the members introduced by the declaration. 類型後面接著 constant_declarator s 的清單,其中每個都引進一個新的成員。The type is followed by a list of constant_declarator s, each of which introduces a new member. Constant_declarator 是由命名成員的 識別碼 所組成,後面接著 " = " token,後面接著一個 constant_expression (常數運算式) ,可提供成員的值。A constant_declarator consists of an identifier that names the member, followed by an "=" token, followed by a constant_expression (Constant expressions) that gives the value of the member.

常數宣告中指定的 別必須是、、、、、、、、、、、、、、 sbyte byte short ushort int uint long ulong char float double decimal bool string enum_typereference_typeThe type specified in a constant declaration must be sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, an enum_type, or a reference_type. 每個 constant_expression 都必須產生目標型別的值,或可透過隱含轉換來轉換成目標型別的類型 (隱含 轉換) 。Each constant_expression must yield a value of the target type or of a type that can be converted to the target type by an implicit conversion (Implicit conversions).

常數的 類型 至少必須可以像常數本身一樣存取 (存取範圍條件約束) 。The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

常數的值是在使用 simple_name (簡單名稱) 或 member_access (成員存取) 的運算式中取得。The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

常數本身可以參與 constant_expressionA constant can itself participate in a constant_expression. 因此,常數可以用於需要 constant_expression 的任何結構中。Thus, a constant may be used in any construct that requires a constant_expression. 這類結構的範例包括 case 標籤、 goto case 語句、 enum 成員宣告、屬性和其他常數宣告。Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

常數運算式中所述, constant_expression 是可在編譯時期完整評估的運算式。As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. 因為建立非 null 值的 reference_type 的唯一方法 string 是套用 new 運算子,而且因為 new constant_expression 中不允許運算子,所以不是 reference_type s 的常數的唯一可能值 stringnullSince the only way to create a non-null value of a reference_type other than string is to apply the new operator, and since the new operator is not permitted in a constant_expression, the only possible value for constants of reference_type s other than string is null.

如果需要常數值的符號名稱,但常數宣告中不允許該值的型別,或在編譯時期無法透過 constant_expression 計算該值,則 readonly 可以改為使用欄位 (Readonly 欄位) 。When a symbolic name for a constant value is desired, but when the type of that value is not permitted in a constant declaration, or when the value cannot be computed at compile-time by a constant_expression, a readonly field (Readonly fields) may be used instead.

宣告多個常數的常數宣告相當於多個具有相同屬性、修飾詞和類型之單一常數的宣告。A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. 例如:For example

class A
{
    public const double X = 1.0, Y = 2.0, Z = 3.0;
}

相當於is equivalent to

class A
{
    public const double X = 1.0;
    public const double Y = 2.0;
    public const double Z = 3.0;
}

常數可相依于相同程式內的其他常數,只要相依性不是迴圈本質。Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. 編譯器會自動排文,以適當的順序評估常數宣告。The compiler automatically arranges to evaluate the constant declarations in the appropriate order. 在範例中In the example

class A
{
    public const int X = B.Z + 1;
    public const int Y = 10;
}

class B
{
    public const int Z = A.Y + 1;
}

編譯器會先評估,然後評估、 A.Y B.Z 最後評估 A.X 、產生值 101112the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. 常數宣告可能會相依于其他程式的常數,但這類相依性只會以單向的方式出現。Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. 參考上述範例,如果 AB 是在不同的程式中宣告,則可能會相依于 A.X B.Z ,但 B.Z 之後可能不會同時 A.Y 相依于。Referring to the example above, if A and B were declared in separate programs, it would be possible for A.X to depend on B.Z, but B.Z could then not simultaneously depend on A.Y.

欄位Fields

*Field _ 是代表與物件或類別相關聯之變數的成員。A *field _ is a member that represents a variable associated with an object or class. _Field_declaration * 引進了一或多個指定類型的欄位。A _field_declaration* introduces one or more fields of a given type.

field_declaration
    : attributes? field_modifier* type variable_declarators ';'
    ;

field_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'readonly'
    | 'volatile'
    | field_modifier_unsafe
    ;

variable_declarators
    : variable_declarator (',' variable_declarator)*
    ;

variable_declarator
    : identifier ('=' variable_initializer)?
    ;

variable_initializer
    : expression
    | array_initializer
    ;

Field_declaration 可能包含一組 屬性 (屬性) 、 new 修飾詞 (新的修飾詞) 、四個存取修飾詞 (存取修飾詞) 的有效組合,以及 (static 靜態和實例欄位) 的修飾詞。A field_declaration may include a set of attributes (Attributes), a new modifier (The new modifier), a valid combination of the four access modifiers (Access modifiers), and a static modifier (Static and instance fields). 此外, field_declaration 可能會將修飾詞 readonly (Readonly 欄位) 或修飾詞 volatile (Volatile 欄位) 但不能同時包含兩者。In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. 屬性和修飾詞適用于 field_declaration 所宣告的所有成員。The attributes and modifiers apply to all of the members declared by the field_declaration. 在欄位宣告中多次出現相同的修飾詞時,會發生錯誤。It is an error for the same modifier to appear multiple times in a field declaration.

Field_declaration 別會指定宣告所引進之成員的型別。The type of a field_declaration specifies the type of the members introduced by the declaration. 類型後面接著 variable_declarator s 的清單,其中每個都引進一個新的成員。The type is followed by a list of variable_declarator s, each of which introduces a new member. Variable_declarator 是由命名該成員的 識別碼 所組成,可選擇性地接著 " = " token 和 variable_initializer (變數初始化運算式) ,以提供該成員的初始值。A variable_declarator consists of an identifier that names that member, optionally followed by an "=" token and a variable_initializer (Variable initializers) that gives the initial value of that member.

欄位的 類型 至少必須可以像欄位本身一樣存取 (存取範圍條件約束) 。The type of a field must be at least as accessible as the field itself (Accessibility constraints).

您可以使用 simple_name (簡單名稱) 或 member_access (成員存取) ,在運算式中取得欄位的值。The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). 非唯讀欄位的值會使用 指派 (指派運算子) 來修改。The value of a non-readonly field is modified using an assignment (Assignment operators). 您可以使用後置遞增和遞減運算子來取得和修改非唯讀欄位的值 , (後置遞增和 遞減運算子) 和前置遞增和遞減運算子 (首碼遞增和 遞減運算子) 。The value of a non-readonly field can be both obtained and modified using postfix increment and decrement operators (Postfix increment and decrement operators) and prefix increment and decrement operators (Prefix increment and decrement operators).

宣告多個欄位的欄位宣告相當於具有相同屬性、修飾詞和類型之單一欄位的多個宣告。A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. 例如:For example

class A
{
    public static int X = 1, Y, Z = 100;
}

相當於is equivalent to

class A
{
    public static int X = 1;
    public static int Y;
    public static int Z = 100;
}

靜態和實例欄位Static and instance fields

當欄位宣告包含修飾詞時,宣告所 static 引進的欄位會是 *靜態欄位 _。When a field declaration includes a static modifier, the fields introduced by the declaration are *static fields _. 如果沒有任何修飾詞,宣告所 static 引進的欄位就會是 *實例欄位*When no static modifier is present, the fields introduced by the declaration are instance fields. 靜態欄位和實例欄位是 c # 支援的數種變數 (變數) 的兩種變數,有時分別稱為 *靜態變數* 和 _ 執行個體變數 *。Static fields and instance fields are two of the several kinds of variables (Variables) supported by C#, and at times they are referred to as static variables and _*instance variables**, respectively.

靜態欄位不是特定實例的一部分;相反地,它會在關閉類型的所有實例之間共用 () 的 開啟和關閉類型A static field is not part of a specific instance; instead, it is shared amongst all instances of a closed type (Open and closed types). 無論已建立的封閉類別型別的實例數目為何,相關聯的應用程式域只會有一個靜態欄位複本。No matter how many instances of a closed class type are created, there is only ever one copy of a static field for the associated application domain.

例如:For example:

class C<V>
{
    static int count = 0;

    public C() {
        count++;
    }

    public static int Count {
        get { return count; }
    }
}

class Application
{
    static void Main() {
        C<int> x1 = new C<int>();
        Console.WriteLine(C<int>.Count);        // Prints 1

        C<double> x2 = new C<double>();
        Console.WriteLine(C<int>.Count);        // Prints 1

        C<int> x3 = new C<int>();
        Console.WriteLine(C<int>.Count);        // Prints 2
    }
}

實例欄位屬於實例。An instance field belongs to an instance. 具體而言,類別的每個實例都包含該類別的一組個別實例欄位。Specifically, every instance of a class contains a separate set of all the instance fields of that class.

當表單 member_access (成員存取) 中參考欄位時 E.M ,如果 M 是靜態欄位,則 E 必須代表包含的型別 M ,而且如果 M 是實例欄位,則 E 必須代表包含的型別的實例 MWhen a field is referenced in a member_access (Member access) of the form E.M, if M is a static field, E must denote a type containing M, and if M is an instance field, E must denote an instance of a type containing M.

靜態和實例成員之間的差異會在 靜態和實例成員中進一步討論。The differences between static and instance members are discussed further in Static and instance members.

唯讀欄位Readonly fields

field_declaration 包含修飾詞時,宣告所 readonly 引進的欄位就是 唯讀欄位When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. 唯讀欄位的直接指派只能做為該宣告的一部分,或在實例的函式或相同類別中的靜態函式中。Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class. (可以在這些內容中將 readonly 欄位指派給多次。具體來說 ) , readonly 只允許在下列內容中直接指派給欄位:(A readonly field can be assigned to multiple times in these contexts.) Specifically, direct assignments to a readonly field are permitted only in the following contexts:

  • 在介紹欄位 (的 variable_declarator 中,將 variable_initializer 包含在宣告) 中。In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • 針對實例欄位,在包含欄位宣告之類別的實例函式中:針對靜態欄位,在包含欄位宣告之類別的靜態函式中。For an instance field, in the instance constructors of the class that contains the field declaration; for a static field, in the static constructor of the class that contains the field declaration. 這些也是唯一將 readonly 欄位傳遞為 out 或參數的有效內容 refThese are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

嘗試指派給欄位, readonlyout ref 在任何其他內容中將它當作或參數傳遞,是編譯時期錯誤。Attempting to assign to a readonly field or pass it as an out or ref parameter in any other context is a compile-time error.

使用常數的靜態唯讀欄位Using static readonly fields for constants

static readonly當需要常數值的符號名稱,但在宣告中不允許值的類型 const ,或在編譯時期無法計算值時,欄位會很有用。A static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration, or when the value cannot be computed at compile-time. 在範例中In the example

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);

    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

Black、、 White RedGreenBlue 成員無法宣告為 const 成員,因為其值無法在編譯時期計算。the Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. 不過, static readonly 改為宣告它們的效果也相同。However, declaring them static readonly instead has much the same effect.

常數和靜態唯讀欄位的版本控制Versioning of constants and static readonly fields

常數和唯讀欄位具有不同的二進位版本設定語義。Constants and readonly fields have different binary versioning semantics. 當運算式參考常數時,會在編譯時期取得常數的值,但是當運算式參考 readonly 欄位時,就不會在執行時間之前取得此欄位的值。When an expression references a constant, the value of the constant is obtained at compile-time, but when an expression references a readonly field, the value of the field is not obtained until run-time. 請考慮包含兩個不同程式的應用程式:Consider an application that consists of two separate programs:

using System;

namespace Program1
{
    public class Utils
    {
        public static readonly int X = 1;
    }
}

namespace Program2
{
    class Test
    {
        static void Main() {
            Console.WriteLine(Program1.Utils.X);
        }
    }
}

Program1Program2 命名空間代表分別編譯的兩個程式。The Program1 and Program2 namespaces denote two programs that are compiled separately. 因為 Program1.Utils.X 是宣告為靜態唯讀欄位,所以語句的輸出值 Console.WriteLine 在編譯時期不是已知的,而是在執行時間取得。Because Program1.Utils.X is declared as a static readonly field, the value output by the Console.WriteLine statement is not known at compile-time, but rather is obtained at run-time. 因此,如果的值已 X 變更並重新 Program1 編譯,則 Console.WriteLine 語句會輸出新的值,即使未重新編譯也一樣 Program2Thus, if the value of X is changed and Program1 is recompiled, the Console.WriteLine statement will output the new value even if Program2 isn't recompiled. 不過, X 已是常數,在 X 編譯時就會取得的值, Program2 而且在重新編譯之前,不會受到變更的影響 Program1 Program2However, had X been a constant, the value of X would have been obtained at the time Program2 was compiled, and would remain unaffected by changes in Program1 until Program2 is recompiled.

Volatile 欄位Volatile fields

field_declaration 包含修飾詞時 volatile ,該宣告引進的欄位就是 volatile 欄位When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

針對非暫時性欄位,重新排序指令的優化技術會導致非預期且無法預期的結果,這些程式會在不同步的情況下存取欄位,例如 lock_statement (lock 語句) 所提供的同步處理。For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock_statement (The lock statement). 這些優化可以由編譯器、執行時間系統或硬體來執行。These optimizations can be performed by the compiler, by the run-time system, or by hardware. 針對 volatile 欄位,這類重新排列優化會受到限制:For volatile fields, such reordering optimizations are restricted:

  • 讀取 volatile 欄位稱為「 暫時性讀取」。A read of a volatile field is called a volatile read. Volatile 讀取具有「取得語義」;也就是說,它保證會在指令順序中發生的任何記憶體參考之前發生。A volatile read has "acquire semantics"; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.
  • 寫入 volatile 欄位稱為「 暫時性寫入」。A write of a volatile field is called a volatile write. Volatile 寫入具有「發行語義」;也就是說,在指令序列中寫入指令之前的任何記憶體參考之後,一定會發生此問題。A volatile write has "release semantics"; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence.

這些限制可確保所有的執行緒將會觀察任何其他執行緒執行的 volatile 寫入會按照其執行的順序進行。These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. 不需要符合規範的執行,就能提供單一的變動寫入順序,如所有線程的執行所見。A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. Volatile 欄位的型別必須是下列其中一項:The type of a volatile field must be one of the following:

  • Reference_typeA reference_type.
  • 類型、、、、、、、、、 byte sbyte short ushort int uint char float bool System.IntPtrSystem.UIntPtrThe type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • 具有 bytesbyteshortushortintuint 之列舉基底類型的 enum_type。An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

範例The example

using System;
using System.Threading;

class Test
{
    public static int result;   
    public static volatile bool finished;

    static void Thread2() {
        result = 143;    
        finished = true; 
    }

    static void Main() {
        finished = false;

        // Run Thread2() in a new thread
        new Thread(new ThreadStart(Thread2)).Start();

        // Wait for Thread2 to signal that it has a result by setting
        // finished to true.
        for (;;) {
            if (finished) {
                Console.WriteLine("result = {0}", result);
                return;
            }
        }
    }
}

產生下列輸出:produces the output:

result = 143

在此範例中,方法 Main 會啟動執行方法的新執行緒 Thread2In this example, the method Main starts a new thread that runs the method Thread2. 這個方法會將值儲存在名為的非暫時性欄位 result 中,然後儲存 true 在 volatile 欄位中 finishedThis method stores a value into a non-volatile field called result, then stores true in the volatile field finished. 主執行緒會等待欄位 finished 設定為 true ,然後讀取欄位 resultThe main thread waits for the field finished to be set to true, then reads the field result. 由於 finished 已宣告 volatile ,主執行緒必須 143 從欄位讀取該值 resultSince finished has been declared volatile, the main thread must read the value 143 from the field result. 如果欄位 finished 尚未宣告,則會 volatile 允許在 result 存放區之後讓主執行緒看到存放區 finished ,然後讓主執行緒從欄位中讀取值,進而 0 result 讓主執行緒看到該欄位。If the field finished had not been declared volatile, then it would be permissible for the store to result to be visible to the main thread after the store to finished, and hence for the main thread to read the value 0 from the field result. 宣告 finishedvolatile 欄位可以防止任何這類不一致的情況。Declaring finished as a volatile field prevents any such inconsistency.

欄位初始化Field initialization

欄位的初始值(不論是靜態欄位或實例欄位)是預設值, (預設 值) 欄位類型的值。The initial value of a field, whether it be a static field or an instance field, is the default value (Default values) of the field's type. 在發生此預設初始化之前,不可能觀察到欄位的值,因此欄位永遠不會「未初始化」。It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". 範例The example

using System;

class Test
{
    static bool b;
    int i;

    static void Main() {
        Test t = new Test();
        Console.WriteLine("b = {0}, i = {1}", b, t.i);
    }
}

產生下列輸出produces the output

b = False, i = 0

由於 bi 都會自動初始化為預設值。because b and i are both automatically initialized to default values.

變數初始化運算式Variable initializers

欄位宣告可能包含 variable_initializer s。Field declarations may include variable_initializer s. 針對靜態欄位,變數初始化運算式會對應至在類別初始化期間執行的指派語句。For static fields, variable initializers correspond to assignment statements that are executed during class initialization. 針對實例欄位,變數初始化運算式會對應至建立類別實例時所執行的指派語句。For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

範例The example

using System;

class Test
{
    static double x = Math.Sqrt(2.0);
    int i = 100;
    string s = "Hello";

    static void Main() {
        Test a = new Test();
        Console.WriteLine("x = {0}, i = {1}, s = {2}", x, a.i, a.s);
    }
}

產生下列輸出produces the output

x = 1.4142135623731, i = 100, s = Hello

因為 x 當執行 i s 實例欄位初始化運算式時,當靜態欄位初始化運算式執行和指派給和時,會發生指派。because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

所有欄位(包括具有變數 初始化運算式 的欄位)中所述的預設值初始化都會發生。The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. 因此,當初始化類別時,該類別中的所有靜態欄位都會先初始化為其預設值,然後靜態欄位初始化運算式會以文字循序執行。Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. 同樣地,當建立類別的實例時,該實例中的所有實例欄位都會先初始化為其預設值,然後以文字循序執行實例欄位初始化運算式。Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order.

具有變數初始化運算式的靜態欄位可能會在其預設值狀態中觀察到。It is possible for static fields with variable initializers to be observed in their default value state. 不過,強烈建議您不要這麼做,因為這是樣式。However, this is strongly discouraged as a matter of style. 範例The example

using System;

class Test
{
    static int a = b + 1;
    static int b = a + 1;

    static void Main() {
        Console.WriteLine("a = {0}, b = {1}", a, b);
    }
}

展示此行為。exhibits this behavior. 雖然 a 和 b 的迴圈定義,但程式是有效的。Despite the circular definitions of a and b, the program is valid. 它會產生輸出It results in the output

a = 1, b = 2

由於靜態欄位 ab 會在其初始化 0 int 運算式執行之前初始化,以 () 的預設值。because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. 當執行的初始化運算式時 a ,的值 b 為零,因此 a 會初始化為 1When the initializer for a runs, the value of b is zero, and so a is initialized to 1. 當執行的初始化運算式時 b ,的值 a 已經是 1 ,因此 b 會初始化為 2When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

靜態欄位初始化Static field initialization

類別的靜態欄位變數初始化運算式會對應到指派順序,這些指派是以它們出現在類別宣告中的文字順序來執行。The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. 如果靜態 (函式) 存在於類別中 ,靜態欄位 初始化運算式的執行會在執行該靜態的函式之前立即發生。If a static constructor (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. 否則,在第一次使用該類別的靜態欄位之前,會在執行相依的時間執行靜態欄位初始化運算式。Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. 範例The example

using System;

class Test 
{ 
    static void Main() {
        Console.WriteLine("{0} {1}", B.Y, A.X);
    }

    public static int F(string s) {
        Console.WriteLine(s);
        return 1;
    }
}

class A
{
    public static int X = Test.F("Init A");
}

class B
{
    public static int Y = Test.F("Init B");
}

可能會產生下列輸出:might produce either the output:

Init A
Init B
1 1

或輸出:or the output:

Init B
Init A
1 1

因為執行時間 XY 的初始化運算式可能會以任一順序出現,所以只會限制在這些欄位的參考之前發生。because the execution of X's initializer and Y's initializer could occur in either order; they are only constrained to occur before the references to those fields. 不過,在此範例中:However, in the example:

using System;

class Test
{
    static void Main() {
        Console.WriteLine("{0} {1}", B.Y, A.X);
    }

    public static int F(string s) {
        Console.WriteLine(s);
        return 1;
    }
}

class A
{
    static A() {}

    public static int X = Test.F("Init A");
}

class B
{
    static B() {}

    public static int Y = Test.F("Init B");
}

輸出必須是:the output must be:

Init B
Init A
1 1

由於靜態函式執行時的規則會依照 靜態 函式中的定義來執行 () 提供該 (靜態的函式 B ,因此 B 靜態欄位初始化運算式) 必須在 A 其靜態的函式和欄位初始化運算式之前執行。because the rules for when static constructors execute (as defined in Static constructors) provide that B's static constructor (and hence B's static field initializers) must run before A's static constructor and field initializers.

實例欄位初始化Instance field initialization

類別的實例欄位變數初始化運算式會對應到一系列的指派,這些指派會在進入任何實例的函式時立即執行, (該類別的函式 初始化運算式) 。The instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors (Constructor initializers) of that class. 變數初始化運算式會以它們出現在類別宣告中的文字順序來執行。The variable initializers are executed in the textual order in which they appear in the class declaration. 類別實例的建立和初始化程式會在 實例的函式中進一步說明。The class instance creation and initialization process is described further in Instance constructors.

實例欄位的變數初始化運算式無法參考所建立的實例。A variable initializer for an instance field cannot reference the instance being created. 因此,它是 this 在變數初始化運算式中參考的編譯時期錯誤,因為它是變數初始化運算式透過 simple_name 參考任何實例成員的編譯時期錯誤。Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name. 在範例中In the example

class A
{
    int x = 1;
    int y = x + 1;        // Error, reference to instance member of this
}

的變數初始化運算式 y 會產生編譯時期錯誤,因為它參考所建立之實例的成員。the variable initializer for y results in a compile-time error because it references a member of the instance being created.

方法Methods

*方法 _ 是一個成員,它會執行可由物件或類別執行的計算或動作。A *method _ is a member that implements a computation or action that can be performed by an object or class. 方法是使用 _method_declaration * s 來宣告:Methods are declared using _method_declaration*s:

method_declaration
    : method_header method_body
    ;

method_header
    : attributes? method_modifier* 'partial'? return_type member_name type_parameter_list?
      '(' formal_parameter_list? ')' type_parameter_constraints_clause*
    ;

method_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | 'async'
    | method_modifier_unsafe
    ;

return_type
    : type
    | 'void'
    ;

member_name
    : identifier
    | interface_type '.' identifier
    ;

method_body
    : block
    | '=>' expression ';'
    | ';'
    ;

Method_declaration 可能包含一組 屬性 (屬性) 和四個存取修飾詞的有效組合 (存取修飾詞) 、 (新的修飾詞) 、 (new static 靜態和實例方法) 、 (虛擬方法) 、 (覆寫方法) 、 (密封方法) 、 (抽象方法) , virtual override sealed abstract 以及 (extern 外部方法) 修飾詞。A method_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

如果下列所有條件都成立,宣告就會有有效的修飾片語合:A declaration has a valid combination of modifiers if all of the following are true:

  • 宣告包含存取修飾詞的有效組合 (存取 修飾詞) 。The declaration includes a valid combination of access modifiers (Access modifiers).
  • 宣告不會多次包含相同的修飾詞。The declaration does not include the same modifier multiple times.
  • 宣告最多包含下列其中一個修飾詞: staticvirtualoverrideThe declaration includes at most one of the following modifiers: static, virtual, and override.
  • 宣告最多包含下列其中一個修飾詞: newoverrideThe declaration includes at most one of the following modifiers: new and override.
  • 如果宣告包含修飾詞 abstract ,則宣告不會包含下列任何修飾詞: staticvirtual sealedexternIf the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • 如果宣告包含修飾詞 private ,則宣告不會包含下列任何修飾詞: virtualoverrideabstractIf the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • 如果宣告包含修飾詞 sealed ,則宣告也會包含修飾詞 overrideIf the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • 如果宣告包含修飾詞 partial ,則不會包含下列任何修飾詞: newpublicprotectedinternalprivate 、、、、 virtual sealed override abstractexternIf the declaration includes the partial modifier, then it does not include any of the following modifiers: new, public, protected, internal, private, virtual, sealed, override, abstract, or extern.

具有修飾詞的方法 async 是非同步函式,並遵循 非同步函式中所述的規則。A method that has the async modifier is an async function and follows the rules described in Async functions.

方法宣告的 return_type 指定方法所計算和傳回的數值型別。The return_type of a method declaration specifies the type of the value computed and returned by the method. 如果方法沒有傳回值,則 return_type voidThe return_type is void if the method does not return a value. 如果宣告包含修飾詞 partial ,則傳回型別必須是 voidIf the declaration includes the partial modifier, then the return type must be void.

Member_name 指定方法的名稱。The member_name specifies the name of the method. 除非方法是明確的介面成員執行 (明確的介面成員 執行) ,否則 member_name 只是一個 識別碼Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. 針對明確的介面成員執行, member_name 包含 interface_type ,後面接著 " . " 和 識別碼For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

選擇性 type_parameter_list 指定) (類型參數 之方法的型別參數。The optional type_parameter_list specifies the type parameters of the method (Type parameters). 如果指定 type_parameter_list ,則方法為 *泛型方法 _。If a type_parameter_list is specified the method is a *generic method _. 如果方法具有修飾詞 extern ,就不能指定 _type_parameter_list *。If the method has an extern modifier, a _type_parameter_list* cannot be specified.

選擇性 formal_parameter_list 指定) (方法參數 的方法參數。The optional formal_parameter_list specifies the parameters of the method (Method parameters).

選擇性的 type_parameter_constraints_clause s 指定個別型別參數的條件約束) (類型參數條件約束 ,而且只有在也提供 type_parameter_list 時,才能指定該方法,而且該方法沒有 override 修飾詞。The optional type_parameter_constraints_clause s specify constraints on individual type parameters (Type parameter constraints) and may only be specified if a type_parameter_list is also supplied, and the method does not have an override modifier.

在方法的 formal_parameter_list 中所參考的 return_type 和每個類型至少必須可以像方法本身一樣存取 (協助工具條件約束) 。The return_type and each of the types referenced in the formal_parameter_list of a method must be at least as accessible as the method itself (Accessibility constraints).

Method_body 可以是分號、語句主體 _ 或 _運算式主體*The method_body is either a semicolon, a statement body _ or an _expression body*. 語句主體是由 _block * 所組成,可指定叫用方法時要執行的語句。A statement body consists of a _block*, which specifies the statements to execute when the method is invoked. 運算式主體是由 => 後面接著 運算式 和分號所組成,而且表示叫用方法時要執行的單一運算式。An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the method is invoked.

針對 abstractextern 方法, method_body 只包含分號。For abstract and extern methods, the method_body consists simply of a semicolon. 針對 partial 方法, method_body 可能包含分號、區塊主體或運算式主體。For partial methods the method_body may consist of either a semicolon, a block body or an expression body. 針對其他所有方法, method_body 是區塊主體或運算式主體。For all other methods, the method_body is either a block body or an expression body.

如果 method_body 包含分號,則宣告可能不會包含 async 修飾詞。If the method_body consists of a semicolon, then the declaration may not include the async modifier.

方法的名稱、類型參數清單和型式參數清單會定義方法 (簽章 多載) 的簽章。The name, the type parameter list and the formal parameter list of a method define the signature (Signatures and overloading) of the method. 具體而言,方法的簽章是由其名稱、型別參數的數目,以及其型式參數的數目、修飾詞和類型所組成。Specifically, the signature of a method consists of its name, the number of type parameters and the number, modifiers, and types of its formal parameters. 基於這些目的,在型式參數型別中所發生之方法的任何型別參數,都是由其名稱所識別,但是在方法的型別引數清單中,它的序數位置。傳回類型不是方法簽章的一部分,也不是型別參數或正式參數的名稱。For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type argument list of the method.The return type is not part of a method's signature, nor are the names of the type parameters or the formal parameters.

方法的名稱必須與在相同類別中宣告的所有其他非方法的名稱不同。The name of a method must differ from the names of all other non-methods declared in the same class. 此外,方法的簽章必須與在相同類別中宣告之所有其他方法的簽章不同,而且在相同類別中宣告的兩個方法可能不會有與不同的簽章 ref outIn addition, the signature of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.

方法的 type_parameter 在整個 method_declaration 的範圍內,而且可以用來在 return_typemethod_bodytype_parameter_constraints_clause 中的整個範圍內形成型別,而不是在 屬性 中。The method's type_parameter s are in scope throughout the method_declaration, and can be used to form types throughout that scope in return_type, method_body, and type_parameter_constraints_clause s but not in attributes.

所有正式參數和型別參數都必須有不同的名稱。All formal parameters and type parameters must have different names.

方法參數Method parameters

方法的 formal_parameter_list 會宣告方法的參數(如果有的話)。The parameters of a method, if any, are declared by the method's formal_parameter_list.

formal_parameter_list
    : fixed_parameters
    | fixed_parameters ',' parameter_array
    | parameter_array
    ;

fixed_parameters
    : fixed_parameter (',' fixed_parameter)*
    ;

fixed_parameter
    : attributes? parameter_modifier? type identifier default_argument?
    ;

default_argument
    : '=' expression
    ;

parameter_modifier
    : 'ref'
    | 'out'
    | 'this'
    ;

parameter_array
    : attributes? 'params' array_type identifier
    ;

正式參數清單是由一或多個以逗號分隔的參數所組成,而這些參數只有最後一個可能是 parameter_arrayThe formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Fixed_parameter 是由一組選擇性的 屬性 所組成 (屬性) 、選擇性 ref out 或修飾詞 this 別、識別碼 和選擇性 default_argumentA fixed_parameter consists of an optional set of attributes (Attributes), an optional ref, out or this modifier, a type, an identifier and an optional default_argument. 每個 fixed_parameter 會宣告具有指定名稱之指定類型的參數。Each fixed_parameter declares a parameter of the given type with the given name. this修飾詞會將方法指定為擴充方法,而且只允許用於靜態方法的第一個參數。The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method. 擴充方法會進一步說明擴充方法。Extension methods are further described in Extension methods.

具有 default_argumentfixed_parameter 稱為 *選擇性參數 ,而 不含 default_argument 的 fixed_parameter * 是 *必要參數A fixed_parameter with a default_argument is known as an optional parameter _, whereas a _fixed_parameter without a default_argument is a *required parameter. 必要的參數不能出現在 _formal_parameter_list * 中的選擇性參數之後。A required parameter may not appear after an optional parameter in a _formal_parameter_list*.

refout 參數不可有 default_argumentA ref or out parameter cannot have a default_argument. Default_argument 中的 運算式 必須是下列其中一項:The expression in a default_argument must be one of the following:

  • constant_expressiona constant_expression
  • 形式的運算式, new S() 其中 S 是實值型別。an expression of the form new S() where S is a value type
  • 形式的運算式, default(S) 其中 S 是實值型別。an expression of the form default(S) where S is a value type

運算式 必須以隱含方式轉換成參數的類型,或可為 null 的轉換。The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

如果在實 (部分) 方法 的情況下,執行部分方法宣告中有選擇性參數,則明確的介面成員執行 (明確 的介面成員執行) 或在單一參數索引子宣告 (索引子) 編譯器應該會發出警告,因為這些成員絕對不能以允許省略引數的方式叫用。If optional parameters occur in an implementing partial method declaration (Partial methods) , an explicit interface member implementation (Explicit interface member implementations) or in a single-parameter indexer declaration (Indexers) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted.

Parameter_array 是由一組選擇性的 屬性 所組成 (屬性) 、 params 修飾詞、 array_type識別碼A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. 參數陣列會宣告具有指定名稱之指定陣列類型的單一參數。A parameter array declares a single parameter of the given array type with the given name. 參數陣列的 array_type 必須是) (陣列類型 的一維陣列類型。The array_type of a parameter array must be a single-dimensional array type (Array types). 在方法調用中,參數陣列允許指定指定陣列類型的單一引數,或允許指定陣列元素類型的零或多個引數。In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. 參數陣列中會進一步說明參數陣列。Parameter arrays are described further in Parameter arrays.

Parameter_array 可能會發生在選擇性參數之後,但不能有預設值--省略 parameter_array 的引數會改為建立空的陣列。A parameter_array may occur after an optional parameter, but cannot have a default value -- the omission of arguments for a parameter_array would instead result in the creation of an empty array.

下列範例說明不同類型的參數:The following example illustrates different kinds of parameters:

public void M(
    ref int      i,
    decimal      d,
    bool         b = false,
    bool?        n = false,
    string       s = "Hello",
    object       o = null,
    T            t = default(T),
    params int[] a
) { }

在的 formal_parameter_listMi 是必要的 ref 參數, d 是必要的值參數、 bso 而且是 t 選擇性的值參數,而且 a 是參數陣列。In the formal_parameter_list for M, i is a required ref parameter, d is a required value parameter, b, s, o and t are optional value parameters and a is a parameter array.

方法宣告會為參數、類型參數和區域變數建立個別的宣告空間。A method declaration creates a separate declaration space for parameters, type parameters and local variables. 名稱會透過型別參數清單和方法的 區塊 中的區域變數宣告,引入這個宣告空間中。Names are introduced into this declaration space by the type parameter list and the formal parameter list of the method and by local variable declarations in the block of the method. 方法宣告空間的兩個成員有相同的名稱時,會發生錯誤。It is an error for two members of a method declaration space to have the same name. 這是方法宣告空間和區域變數宣告空間的錯誤,以包含具有相同名稱的元素。It is an error for the method declaration space and the local variable declaration space of a nested declaration space to contain elements with the same name.

方法叫用 (方法 調用,) 會建立該調用的特定複本、方法的正式參數和區域變數,以及調用的引數清單會將值或變數參考指派給新建立的型式參數。A method invocation (Method invocations) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. 在方法的 區塊 內,型式參數可以在 simple_name 運算式中參考 (簡單名稱) 的識別碼。Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

正式參數有四種類型:There are four kinds of formal parameters:

  • 值參數,不含任何修飾詞的宣告。Value parameters, which are declared without any modifiers.
  • 參考參數,以修飾詞宣告 refReference parameters, which are declared with the ref modifier.
  • 以修飾詞宣告的輸出參數 outOutput parameters, which are declared with the out modifier.
  • 以修飾詞宣告的參數陣列 paramsParameter arrays, which are declared with the params modifier.

如同簽章 多載中所述, ref 和修飾詞是方法簽章的 out 一部分,但是 params 修飾詞則不是。As described in Signatures and overloading, the ref and out modifiers are part of a method's signature, but the params modifier is not.

值參數Value parameters

使用 no 修飾詞宣告的參數是值參數。A parameter declared with no modifiers is a value parameter. 值參數對應于從方法調用中提供的對應引數取得初始值的區域變數。A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

當正式參數是值參數時,方法調用中的對應引數必須是可隱含轉換的運算式, (隱含轉換) 為型式參數類型。When a formal parameter is a value parameter, the corresponding argument in a method invocation must be an expression that is implicitly convertible (Implicit conversions) to the formal parameter type.

允許方法將新值指派給 value 參數。A method is permitted to assign new values to a value parameter. 這類指派只會影響 value 參數所代表的本機儲存位置,而不會影響方法調用中提供的實際引數。Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation.

傳址參數Reference parameters

使用修飾詞宣告的參數 ref 是參考參數。A parameter declared with a ref modifier is a reference parameter. 與值參數不同的是,參考參數不會建立新的儲存位置。Unlike a value parameter, a reference parameter does not create a new storage location. 相反地,參考參數表示與方法調用中的引數指定的相同儲存位置。Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

當型式參數是參考參數時,方法調用中的對應引數必須由關鍵字組成, ref 後面接著 Variable_reference (精確的規則,以判斷 與正式參數相同類型的明確指派) 。When a formal parameter is a reference parameter, the corresponding argument in a method invocation must consist of the keyword ref followed by a variable_reference (Precise rules for determining definite assignment) of the same type as the formal parameter. 必須先明確指派變數,才能將變數作為參考參數傳遞。A variable must be definitely assigned before it can be passed as a reference parameter.

在方法中,一律會將參考參數視為明確指派。Within a method, a reference parameter is always considered definitely assigned.

宣告為反覆運算器 (反覆運算 器的方法) 不能有參考參數。A method declared as an iterator (Iterators) cannot have reference parameters.

範例The example

using System;

class Test
{
    static void Swap(ref int x, ref int y) {
        int temp = x;
        x = y;
        y = temp;
    }

    static void Main() {
        int i = 1, j = 2;
        Swap(ref i, ref j);
        Console.WriteLine("i = {0}, j = {1}", i, j);
    }
}

產生下列輸出produces the output

i = 2, j = 1

針對中的調用 Swap Mainx 表示 iy 表示 jFor the invocation of Swap in Main, x represents i and y represents j. 因此,叫用具有交換和值的效果 i jThus, the invocation has the effect of swapping the values of i and j.

在採用參考參數的方法中,有多個名稱可代表相同的儲存位置。In a method that takes reference parameters it is possible for multiple names to represent the same storage location. 在範例中In the example

class A
{
    string s;

    void F(ref string a, ref string b) {
        s = "One";
        a = "Two";
        b = "Three";
    }

    void G() {
        F(ref s, ref s);
    }
}

的調用會 FG 的參考傳遞給 s abthe invocation of F in G passes a reference to s for both a and b. 因此,對於該調用,名稱 sab 全都參考相同的儲存位置,而這三個指派全都修改了實例欄位 sThus, for that invocation, the names s, a, and b all refer to the same storage location, and the three assignments all modify the instance field s.

輸出參數Output parameters

使用修飾詞宣告的參數 out 是輸出參數。A parameter declared with an out modifier is an output parameter. 類似于參考參數,輸出參數不會建立新的儲存位置。Similar to a reference parameter, an output parameter does not create a new storage location. 相反地,output 參數表示與方法調用中的引數指定的相同儲存位置。Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

當正式參數是輸出參數時,方法調用中的對應引數必須由關鍵字組成, out 後面接著 Variable_reference (精確的規則,以判斷 與正式參數相同類型的明確指派) 。When a formal parameter is an output parameter, the corresponding argument in a method invocation must consist of the keyword out followed by a variable_reference (Precise rules for determining definite assignment) of the same type as the formal parameter. 變數在可作為輸出參數傳遞之前,不需要明確指派,但是在將變數作為輸出參數傳遞的調用之後,會將變數視為明確指派。A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned.

在方法中,就像區域變數一樣,一開始會將輸出參數視為未指派,且必須在使用其值之前明確指派。Within a method, just like a local variable, an output parameter is initially considered unassigned and must be definitely assigned before its value is used.

方法的每個輸出參數都必須在方法傳回之前明確指派。Every output parameter of a method must be definitely assigned before the method returns.

宣告為部分方法 (部分 方法) 或反覆運算器 (反覆運算 器的方法) 不能有輸出參數。A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

輸出參數通常用於產生多個傳回值的方法。Output parameters are typically used in methods that produce multiple return values. 例如:For example:

using System;

class Test
{
    static void SplitPath(string path, out string dir, out string name) {
        int i = path.Length;
        while (i > 0) {
            char ch = path[i - 1];
            if (ch == '\\' || ch == '/' || ch == ':') break;
            i--;
        }
        dir = path.Substring(0, i);
        name = path.Substring(i);
    }

    static void Main() {
        string dir, name;
        SplitPath("c:\\Windows\\System\\hello.txt", out dir, out name);
        Console.WriteLine(dir);
        Console.WriteLine(name);
    }
}

此範例會產生輸出:The example produces the output:

c:\Windows\System\
hello.txt

請注意, dirname 變數可以在傳遞給之前解除指派 SplitPath ,而且會被視為在呼叫之後被明確指派。Note that the dir and name variables can be unassigned before they are passed to SplitPath, and that they are considered definitely assigned following the call.

參數陣列Parameter arrays

使用修飾詞宣告的參數 params 是參數陣列。A parameter declared with a params modifier is a parameter array. 如果型式參數清單包含參數陣列,它必須是清單中的最後一個參數,而且必須是一維陣列型別。If a formal parameter list includes a parameter array, it must be the last parameter in the list and it must be of a single-dimensional array type. 例如, string[] 型別和 string[][] 可以做為參數陣列的型別,但是型別不能用 string[,]For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. 您無法結合 params 修飾詞與修飾詞 refoutIt is not possible to combine the params modifier with the modifiers ref and out.

參數陣列允許以兩種方法中的其中一種方式來指定引數:A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • 為參數陣列提供的引數可以是單一運算式, (隱含轉換) 為參數陣列類型的隱含轉換。The argument given for a parameter array can be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. 在此情況下,參數陣列的運作方式與值參數完全相同。In this case, the parameter array acts precisely like a value parameter.
  • 另外,叫用也可以指定參數陣列的零或多個引數,其中每個引數都是隱含可轉換的運算式 (隱含轉換) 為參數陣列的元素類型。Alternatively, the invocation can specify zero or more arguments for the parameter array, where each argument is an expression that is implicitly convertible (Implicit conversions) to the element type of the parameter array. 在此情況下,叫用會使用與引數數目對應的長度來建立參數陣列類型的實例,並使用指定的引數值初始化陣列實例的專案,並使用新建立的陣列實例作為實際的引數。In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument.

除了允許在調用中使用不定數目的引數之外,參數陣列也相當於值參數 ( 參數) 屬於相同類型的值。Except for allowing a variable number of arguments in an invocation, a parameter array is precisely equivalent to a value parameter (Value parameters) of the same type.

範例The example

using System;

class Test
{
    static void F(params int[] args) {
        Console.Write("Array contains {0} elements:", args.Length);
        foreach (int i in args) 
            Console.Write(" {0}", i);
        Console.WriteLine();
    }

    static void Main() {
        int[] arr = {1, 2, 3};
        F(arr);
        F(10, 20, 30, 40);
        F();
    }
}

產生下列輸出produces the output

Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:

第一個調用只會將 F 陣列傳遞 a 為值參數。The first invocation of F simply passes the array a as a value parameter. 的第二個調用 F 會自動建立具有指定專案值的四個元素 int[] ,並將該陣列實例傳遞為值參數。The second invocation of F automatically creates a four-element int[] with the given element values and passes that array instance as a value parameter. 同樣地,的第三個調用 F 會建立零個元素 int[] ,並將該實例作為值參數傳遞。Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. 第二個和第三個調用正好等同于撰寫:The second and third invocations are precisely equivalent to writing:

F(new int[] {10, 20, 30, 40});
F(new int[] {});

執行多載解析時,具有參數陣列的方法可能適用于其一般格式或其展開形式 (適用的函式 成員) 。When performing overload resolution, a method with a parameter array may be applicable either in its normal form or in its expanded form (Applicable function member). 只有當方法的一般形式不適用,而且只有在具有與展開表單相同之簽章的適用方法尚未在相同類型中宣告時,才可以使用展開的方法格式。The expanded form of a method is available only if the normal form of the method is not applicable and only if an applicable method with the same signature as the expanded form is not already declared in the same type.

範例The example

using System;

class Test
{
    static void F(params object[] a) {
        Console.WriteLine("F(object[])");
    }

    static void F() {
        Console.WriteLine("F()");
    }

    static void F(object a0, object a1) {
        Console.WriteLine("F(object,object)");
    }

    static void Main() {
        F();
        F(1);
        F(1, 2);
        F(1, 2, 3);
        F(1, 2, 3, 4);
    }
}

產生下列輸出produces the output

F();
F(object[]);
F(object,object);
F(object[]);
F(object[]);

在此範例中,具有參數陣列的方法中,有兩個可能的展開形式已包含在類別中做為一般方法。In the example, two of the possible expanded forms of the method with a parameter array are already included in the class as regular methods. 因此在執行多載解析時,不會考慮這些展開的表單,而第一個和第三個方法調用則是選取一般方法。These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. 當類別宣告具有參數陣列的方法時,也不會將某些展開的表單包含為一般方法。When a class declares a method with a parameter array, it is not uncommon to also include some of the expanded forms as regular methods. 如此一來,當叫用具有參數陣列的擴充形式的方法時,就可以避免配置所發生的陣列實例。By doing so it is possible to avoid the allocation of an array instance that occurs when an expanded form of a method with a parameter array is invoked.

當參數陣列的類型為時 object[] ,方法的一般形式和單一參數的所需格式之間可能會有不明確的情況 objectWhen the type of a parameter array is object[], a potential ambiguity arises between the normal form of the method and the expended form for a single object parameter. 混淆的原因是, object[] 本身可以隱含地轉換成類型 objectThe reason for the ambiguity is that an object[] is itself implicitly convertible to type object. 但是,不清楚的是,因為可以視需要插入轉換來解決問題。The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

範例The example

using System;

class Test
{
    static void F(params object[] args) {
        foreach (object o in args) {
            Console.Write(o.GetType().FullName);
            Console.Write(" ");
        }
        Console.WriteLine();
    }

    static void Main() {
        object[] a = {1, "Hello", 123.456};
        object o = a;
        F(a);
        F((object)a);
        F(o);
        F((object[])o);
    }
}

產生下列輸出produces the output

System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double

在的第一個和最後一個調用中 F ,適用的一般形式 F 為,因為引數類型到參數類型的隱含轉換存在, (兩者都屬於類型 object[]) 。In the first and last invocations of F, the normal form of F is applicable because an implicit conversion exists from the argument type to the parameter type (both are of type object[]). 因此,多載解析會選取的一般形式 F ,並以一般值參數的形式傳遞引數。Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. 在第二個和第三個調用中,的一般形式 F 不適用,因為沒有從引數類型到參數類型的隱含轉換, (類型 object 無法隱含地轉換成類型 object[]) 。In the second and third invocations, the normal form of F is not applicable because no implicit conversion exists from the argument type to the parameter type (type object cannot be implicitly converted to type object[]). 不過,的擴充形式 F 適用,因此是由多載解析所選取。However, the expanded form of F is applicable, so it is selected by overload resolution. 如此一來,就會透過叫用 object[] 來建立一個元素,並使用指定的引數值來初始化陣列的單一元素 (這本身就是) 的參考 object[]As a result, a one-element object[] is created by the invocation, and the single element of the array is initialized with the given argument value (which itself is a reference to an object[]).

靜態和執行個體方法Static and instance methods

當方法宣告包含修飾詞時 static ,該方法就稱為靜態方法。When a method declaration includes a static modifier, that method is said to be a static method. 當沒有任何修飾詞時 static ,方法會被視為實例方法。When no static modifier is present, the method is said to be an instance method.

靜態方法不會在特定的實例上運作,而是 this 在靜態方法中參考的編譯時期錯誤。A static method does not operate on a specific instance, and it is a compile-time error to refer to this in a static method.

實例方法會在類別的指定實例上運作,而該實例可以 this (此存取) 的方式來存取。An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

在表單的 member_access (成員存取) 中參考方法時 E.M ,如果 M 是靜態方法,則 E 必須代表包含的型別 M ,而且如果 M 是實例方法,則 E 必須代表包含的型別的實例 MWhen a method is referenced in a member_access (Member access) of the form E.M, if M is a static method, E must denote a type containing M, and if M is an instance method, E must denote an instance of a type containing M.

靜態和實例成員之間的差異會在 靜態和實例成員中進一步討論。The differences between static and instance members are discussed further in Static and instance members.

虛擬方法Virtual methods

當實例方法宣告包含修飾詞時 virtual ,該方法就稱為虛擬方法。When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. 如果沒有任何修飾詞,則會將 virtual 方法稱為非虛擬方法。When no virtual modifier is present, the method is said to be a non-virtual method.

非虛擬方法的實作為非變異:不論方法是在其宣告所在之類別的實例上叫用,或衍生類別的實例,都是一樣的。The implementation of a non-virtual method is invariant: The implementation is the same whether the method is invoked on an instance of the class in which it is declared or an instance of a derived class. 相反地,衍生類別可以取代虛擬方法的執行。In contrast, the implementation of a virtual method can be superseded by derived classes. 取代繼承虛擬方法之執行的 程式,稱為覆寫該方法 (覆 寫方法) 。The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

在虛擬方法調用中,該調用發生所在之實例的 *執行時間類型 _ 決定要叫用的實際方法執行。In a virtual method invocation, the *run-time type _ of the instance for which that invocation takes place determines the actual method implementation to invoke. 在非虛擬方法調用中,實例的 _ 編譯時間類型* 是決定因素。In a non-virtual method invocation, the _ compile-time type* of the instance is the determining factor. 確切的說,當名為的方法 NA 在具有編譯時間類型的實例和執行時間類型的引數清單中叫用時 C R (其中 RC 或衍生自 C) 的類別,則會以下列方式處理調用:In precise terms, when a method named N is invoked with an argument list A on an instance with a compile-time type C and a run-time type R (where R is either C or a class derived from C), the invocation is processed as follows:

  • 首先,會將多載解析套用至 CNA ,以便 M 從在中宣告和繼承的方法集合中選取特定的方法 CFirst, overload resolution is applied to C, N, and A, to select a specific method M from the set of methods declared in and inherited by C. 方法調用中會說明這一點。This is described in Method invocations.
  • 如果 M 是非虛擬方法,則會叫用 MThen, if M is a non-virtual method, M is invoked.
  • 否則, M 就是虛擬方法,而且會叫用與相關的最衍生的實作為 M ROtherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

對於在類別中宣告或繼承的每個虛擬方法,會有與該類別相關之方法的 最衍生執行For every virtual method declared in or inherited by a class, there exists a most derived implementation of the method with respect to that class. 關於類別的虛擬方法最衍生的實 M 作為判斷方式如下 RThe most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • 如果 R 包含的簡介宣告 virtual ,則 M 這是的最衍生的實作為 MIf R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • 否則,如果 R 包含 overrideM ,則這是的最衍生的實作為 MOtherwise, if R contains an override of M, then this is the most derived implementation of M.
  • 否則,的最高衍生實 M R 作為與的直接基類有關的最衍生實作為 M ROtherwise, the most derived implementation of M with respect to R is the same as the most derived implementation of M with respect to the direct base class of R.

下列範例說明虛擬和非虛擬方法之間的差異:The following example illustrates the differences between virtual and non-virtual methods:

using System;

class A
{
    public void F() { Console.WriteLine("A.F"); }

    public virtual void G() { Console.WriteLine("A.G"); }
}

class B: A
{
    new public void F() { Console.WriteLine("B.F"); }

    public override void G() { Console.WriteLine("B.G"); }
}

class Test
{
    static void Main() {
        B b = new B();
        A a = b;
        a.F();
        b.F();
        a.G();
        b.G();
    }
}

在此範例中, A 引進了非虛擬方法 F 和虛擬方法 GIn the example, A introduces a non-virtual method F and a virtual method G. 類別 B 引進了新的非虛擬方法 F ,因而隱藏了繼承的 F 方法,也會覆寫繼承的方法 GThe class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. 此範例會產生輸出:The example produces the output:

A.F
B.F
B.G
B.G

請注意,語句會叫用 a.G() B.G ,而不是 A.GNotice that the statement a.G() invokes B.G, not A.G. 這是因為實例的執行時間類型 (是 B) ,而不是) 之實例 (的編譯時間類型 A ,會決定要叫用的實際方法執行。This is because the run-time type of the instance (which is B), not the compile-time type of the instance (which is A), determines the actual method implementation to invoke.

由於允許方法隱藏繼承的方法,因此類別可以包含數個具有相同簽章的虛擬方法。Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. 這並不表示有不明確的問題,因為所有的衍生方法都是隱藏的。This does not present an ambiguity problem, since all but the most derived method are hidden. 在範例中In the example

using System;

class A
{
    public virtual void F() { Console.WriteLine("A.F"); }
}

class B: A
{
    public override void F() { Console.WriteLine("B.F"); }
}

class C: B
{
    new public virtual void F() { Console.WriteLine("C.F"); }
}

class D: C
{
    public override void F() { Console.WriteLine("D.F"); }
}

class Test
{
    static void Main() {
        D d = new D();
        A a = d;
        B b = d;
        C c = d;
        a.F();
        b.F();
        c.F();
        d.F();
    }
}

CD 類別包含兩個具有相同簽章的虛擬方法:所引進的 A 和所引進的方法 Cthe C and D classes contain two virtual methods with the same signature: The one introduced by A and the one introduced by C. 引進的方法會 C 隱藏繼承自的方法 AThe method introduced by C hides the method inherited from A. 因此,中的覆寫宣告 D 會覆寫引進的方法 C ,因此不可能覆 D 寫引進的方法 AThus, the override declaration in D overrides the method introduced by C, and it is not possible for D to override the method introduced by A. 此範例會產生輸出:The example produces the output:

B.F
B.F
D.F
D.F

請注意, D 透過不隱藏方法的較低衍生型別來存取的實例,就可以叫用隱藏的虛擬方法。Note that it is possible to invoke the hidden virtual method by accessing an instance of D through a less derived type in which the method is not hidden.

覆寫方法Override methods

當實例方法宣告包含修飾詞時 override ,方法會被視為覆 寫方法When an instance method declaration includes an override modifier, the method is said to be an override method. 覆寫方法會使用相同的簽章覆寫繼承的虛擬方法。An override method overrides an inherited virtual method with the same signature. 虛擬方法宣告會導入新的方法,而覆寫方法宣告則是會提供現有已繼承之虛擬方法的新實作,來將該方法特製化。Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.

宣告所覆寫的方法 override 稱為覆 寫基底方法The method overridden by an override declaration is known as the overridden base method. 對於在類別中宣告的覆寫方法 M C ,會藉由檢查的每個基類型別來判斷覆寫的基底方法 C ,其開頭為的直接基類型別, C 並繼續每個後續的直接基類型別,直到指定的基類型別為止,直到指定的基類型別都有一個可存取的方法,且其簽章與 M 替代類型引數For an override method M declared in a class C, the overridden base method is determined by examining each base class type of C, starting with the direct base class type of C and continuing with each successive direct base class type, until in a given base class type at least one accessible method is located which has the same signature as M after substitution of type arguments. 基於找出覆寫基底方法的目的,如果方法是,則會將其視為可存取,如果是,則為,如果是,則為,如果是,則會被宣告為與 public protected protected internal internal 相同程式中的宣告 CFor the purposes of locating the overridden base method, a method is considered accessible if it is public, if it is protected, if it is protected internal, or if it is internal and declared in the same program as C.

除非覆寫宣告的下列所有條件都成立,否則會發生編譯時期錯誤:A compile-time error occurs unless all of the following are true for an override declaration:

  • 如上面所述,可以找到覆寫的基底方法。An overridden base method can be located as described above.
  • 只有一個這種覆寫的基底方法。There is exactly one such overridden base method. 只有當基底類別型別是一種結構化型別,而型別引數的替代方式讓兩個方法的簽章都相同時,這項限制才會生效。This restriction has effect only if the base class type is a constructed type where the substitution of type arguments makes the signature of two methods the same.
  • 覆寫的基底方法為虛擬、抽象或覆寫方法。The overridden base method is a virtual, abstract, or override method. 換句話說,覆寫的基底方法不能是靜態或非虛擬。In other words, the overridden base method cannot be static or non-virtual.
  • 覆寫的基底方法不是密封的方法。The overridden base method is not a sealed method.
  • 覆寫方法和覆寫的基底方法具有相同的傳回類型。The override method and the overridden base method have the same return type.
  • 覆寫宣告和覆寫基底方法具有相同的宣告存取範圍。The override declaration and the overridden base method have the same declared accessibility. 換句話說,覆寫宣告無法變更虛擬方法的存取範圍。In other words, an override declaration cannot change the accessibility of the virtual method. 但是,如果覆寫的基底方法是內部受保護的,而且它是在與包含覆寫方法的元件不同的元件中宣告,則覆寫方法的宣告存取範圍必須受到保護。However, if the overridden base method is protected internal and it is declared in a different assembly than the assembly containing the override method then the override method's declared accessibility must be protected.
  • 覆寫宣告未指定類型參數條件約束子句。The override declaration does not specify type-parameter-constraints-clauses. 相反地,條件約束會繼承自覆寫的基底方法。Instead the constraints are inherited from the overridden base method. 請注意,在覆寫的方法中,屬於類型參數的條件約束可能會由繼承的條件約束中的型別引數取代。Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. 這可能會導致明確指定時不合法的條件約束,例如實數值型別或密封型別。This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

下列範例示範覆寫規則對泛型類別的運作方式:The following example demonstrates how the overriding rules work for generic classes:

abstract class C<T>
{
    public virtual T F() {...}
    public virtual C<T> G() {...}
    public virtual void H(C<T> x) {...}
}

class D: C<string>
{
    public override string F() {...}            // Ok
    public override C<string> G() {...}         // Ok
    public override void H(C<T> x) {...}        // Error, should be C<string>
}

class E<T,U>: C<U>
{
    public override U F() {...}                 // Ok
    public override C<U> G() {...}              // Ok
    public override void H(C<T> x) {...}        // Error, should be C<U>
}

覆寫宣告可以使用 base_access (基底存取) 來存取覆寫的基底方法。An override declaration can access the overridden base method using a base_access (Base access). 在範例中In the example

class A
{
    int x;

    public virtual void PrintFields() {
        Console.WriteLine("x = {0}", x);
    }
}

class B: A
{
    int y;

    public override void PrintFields() {
        base.PrintFields();
        Console.WriteLine("y = {0}", y);
    }
}

中的調用會叫 base.PrintFields() B PrintFields 用在中宣告的方法 Athe base.PrintFields() invocation in B invokes the PrintFields method declared in A. Base_access 會停用虛擬調用機制,而且只會將基底方法視為非虛擬方法。A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. B 寫入調用之後 ((A)this).PrintFields() ,它會以遞迴方式叫用在中宣告的方法,而不是在中宣告的 PrintFields 方法, B A 因為 PrintFields 是虛擬,而且的執行時間類型 ((A)this)BHad the invocation in B been written ((A)this).PrintFields(), it would recursively invoke the PrintFields method declared in B, not the one declared in A, since PrintFields is virtual and the run-time type of ((A)this) is B.

只有透過包含修飾詞, override 方法才能覆寫另一個方法。Only by including an override modifier can a method override another method. 在所有其他情況下,與繼承方法具有相同簽章的方法,只會隱藏繼承的方法。In all other cases, a method with the same signature as an inherited method simply hides the inherited method. 在範例中In the example

class A
{
    public virtual void F() {}
}

class B: A
{
    public virtual void F() {}        // Warning, hiding inherited F()
}

F中的方法不 B 包含修飾詞 override ,因此不會覆寫 F 中的方法 Athe F method in B does not include an override modifier and therefore does not override the F method in A. 相反地, F 中的方法會 B 隱藏中的方法 A ,並報告警告,因為宣告不包含修飾詞 newRather, the F method in B hides the method in A, and a warning is reported because the declaration does not include a new modifier.

在範例中In the example

class A
{
    public virtual void F() {}
}

class B: A
{
    new private void F() {}        // Hides A.F within body of B
}

class C: B
{
    public override void F() {}    // Ok, overrides A.F
}

F中的方法會 B 隱藏 F 繼承自的虛擬方法 Athe F method in B hides the virtual F method inherited from A. 由於中的新 FB 有私用存取,因此其範圍只包含的類別主體, B 而且不會延伸至 CSince the new F in B has private access, its scope only includes the class body of B and does not extend to C. 因此,中的宣告 F C 允許覆寫 F 繼承自的 ATherefore, the declaration of F in C is permitted to override the F inherited from A.

密封方法Sealed methods

當實例方法宣告包含修飾詞時 sealed ,該方法就稱為 密封方法When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. 如果實例方法宣告包含修飾詞 sealed ,則它也必須包含 override 修飾詞。If an instance method declaration includes the sealed modifier, it must also include the override modifier. 使用修飾詞 sealed 可防止衍生類別進一步覆寫方法。Use of the sealed modifier prevents a derived class from further overriding the method.

在範例中In the example

using System;

class A
{
    public virtual void F() {
        Console.WriteLine("A.F");
    }

    public virtual void G() {
        Console.WriteLine("A.G");
    }
}

class B: A
{
    sealed override public void F() {
        Console.WriteLine("B.F");
    } 

    override public void G() {
        Console.WriteLine("B.G");
    } 
}

class C: B
{
    override public void G() {
        Console.WriteLine("C.G");
    } 
}

類別 B 提供兩個覆寫方法: F 具有修飾詞的方法,以及不使用的 sealed G 方法。the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. B使用 sealed modifier 可防止進一步覆 CFB's use of the sealed modifier prevents C from further overriding F.

抽象方法Abstract methods

當實例方法宣告包含修飾詞時 abstract ,該方法會被視為 抽象方法When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. 雖然抽象方法也會隱含地成為虛擬方法,但它不能有修飾詞 virtualAlthough an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

抽象方法宣告會引進新的虛擬方法,但不會提供該方法的執行。An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. 相反地,非抽象衍生類別必須藉由覆寫該方法來提供自己的實作為。Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. 由於抽象方法不提供實際的實值,因此抽象方法的 method_body 只會包含分號。Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

抽象方法宣告只能在抽象類別 (抽象類別) 中使用。Abstract method declarations are only permitted in abstract classes (Abstract classes).

在範例中In the example

public abstract class Shape
{
    public abstract void Paint(Graphics g, Rectangle r);
}

public class Ellipse: Shape
{
    public override void Paint(Graphics g, Rectangle r) {
        g.DrawEllipse(r);
    }
}

public class Box: Shape
{
    public override void Paint(Graphics g, Rectangle r) {
        g.DrawRect(r);
    }
}

Shape類別會定義可自行繪製之幾何繪圖物件的抽象概念。the Shape class defines the abstract notion of a geometrical shape object that can paint itself. Paint 方法是抽象的,因為沒有有意義的預設實值。The Paint method is abstract because there is no meaningful default implementation. EllipseBox 類別是具體的實作為 ShapeThe Ellipse and Box classes are concrete Shape implementations. 因為這些類別不是抽象的,所以必須覆寫 Paint 方法並提供實際的實作為。Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

這是 base_access (基底存取) 參考抽象方法的編譯時期錯誤。It is a compile-time error for a base_access (Base access) to reference an abstract method. 在範例中In the example

abstract class A
{
    public abstract void F();
}

class B: A
{
    public override void F() {
        base.F();                        // Error, base.F is abstract
    }
}

針對調用報告編譯時期錯誤, base.F() 因為它參考抽象方法。a compile-time error is reported for the base.F() invocation because it references an abstract method.

允許抽象方法宣告以覆寫虛擬方法。An abstract method declaration is permitted to override a virtual method. 這可讓抽象類別強制在衍生類別中重新執行方法,並使方法的原始實作為無法使用。This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. 在範例中In the example

using System;

class A
{
    public virtual void F() {
        Console.WriteLine("A.F");
    }
}

abstract class B: A
{
    public abstract override void F();
}

class C: B
{
    public override void F() {
        Console.WriteLine("C.F");
    }
}

類別會宣告 A 虛擬方法,類別會 B 使用抽象方法覆寫這個方法,而類別會 C 覆寫抽象方法以提供它自己的實作為。class A declares a virtual method, class B overrides this method with an abstract method, and class C overrides the abstract method to provide its own implementation.

外部方法External methods

當方法宣告包含修飾詞時 extern ,該方法稱為 *external 方法 _。When a method declaration includes an extern modifier, that method is said to be an *external method _. 外部方法是在外部執行,通常是使用 c # 以外的語言。External methods are implemented externally, typically using a language other than C#. 因為外部方法宣告不提供實際的實值,所以外部方法的 _method_body * 只會包含分號。Because an external method declaration provides no actual implementation, the _method_body* of an external method simply consists of a semicolon. 外部方法可能不是泛型。An external method may not be generic.

extern修飾詞通常會搭配 DllImport 屬性使用 (與 COM 和 Win32 元件的交互操作性) ,可讓 Dll (動態連結程式庫) 來執行外部方法。The extern modifier is typically used in conjunction with a DllImport attribute (Interoperation with COM and Win32 components), allowing external methods to be implemented by DLLs (Dynamic Link Libraries). 執行環境可能會支援其他機制,以提供外部方法的執行。The execution environment may support other mechanisms whereby implementations of external methods can be provided.

當外部方法包含屬性時 DllImport ,方法宣告也必須包含 static 修飾詞。When an external method includes a DllImport attribute, the method declaration must also include a static modifier. 這個範例示範如何使用修飾詞 externDllImport 屬性:This example demonstrates the use of the extern modifier and the DllImport attribute:

using System.Text;
using System.Security.Permissions;
using System.Runtime.InteropServices;

class Path
{
    [DllImport("kernel32", SetLastError=true)]
    static extern bool CreateDirectory(string name, SecurityAttribute sa);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool RemoveDirectory(string name);

    [DllImport("kernel32", SetLastError=true)]
    static extern int GetCurrentDirectory(int bufSize, StringBuilder buf);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool SetCurrentDirectory(string name);
}

部分方法 (回顧) Partial methods (recap)

當方法宣告包含修飾詞時 partial ,該方法就稱為 部分方法When a method declaration includes a partial modifier, that method is said to be a partial method. 部分方法只能宣告為部分類型的成員, (部分類型) ,而且會受到一些限制。Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. 部分方法會進一步描述部分方法。Partial methods are further described in Partial methods.

擴充方法Extension methods

當方法的第一個參數包含修飾詞時 this ,該方法會被視為 擴充方法When the first parameter of a method includes the this modifier, that method is said to be an extension method. 擴充方法只能在非泛型、非嵌套的靜態類別中宣告。Extension methods can only be declared in non-generic, non-nested static classes. 擴充方法的第一個參數不能有以外的任何修飾詞 this ,而且參數類型不能是指標類型。The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

以下是會宣告兩個擴充方法的靜態類別範例:The following is an example of a static class that declares two extension methods:

public static class Extensions
{
    public static int ToInt32(this string s) {
        return Int32.Parse(s);
    }

    public static T[] Slice<T>(this T[] source, int index, int count) {
        if (index < 0 || count < 0 || source.Length - index < count)
            throw new ArgumentException();
        T[] result = new T[count];
        Array.Copy(source, index, result, 0, count);
        return result;
    }
}

擴充方法是一般的靜態方法。An extension method is a regular static method. 此外,它的封入靜態類別在範圍內,您可以使用實例方法調用語法來叫用擴充方法 (擴充方法 調用,) 使用接收者運算式做為第一個引數。In addition, where its enclosing static class is in scope, an extension method can be invoked using instance method invocation syntax (Extension method invocations), using the receiver expression as the first argument.

下列程式使用上述的擴充方法:The following program uses the extension methods declared above:

static class Program
{
    static void Main() {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in strings.Slice(1, 2)) {
            Console.WriteLine(s.ToInt32());
        }
    }
}

Slice可以在上使用方法, string[] 而且 ToInt32 可以在上使用方法 string ,因為它們已經宣告為擴充方法。The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. 使用一般的靜態方法呼叫,程式的意義與下列相同:The meaning of the program is the same as the following, using ordinary static method calls:

static class Program
{
    static void Main() {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in Extensions.Slice(strings, 1, 2)) {
            Console.WriteLine(Extensions.ToInt32(s));
        }
    }
}

方法主體Method body

方法宣告的 method_body 是由區塊主體、運算式主體或分號所組成。The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

方法的 結果型 別是 void ,如果傳回型別是 void ,或者方法是非同步,而且傳回型別為 System.Threading.Tasks.TaskThe result type of a method is void if the return type is void, or if the method is async and the return type is System.Threading.Tasks.Task. 否則,非非同步方法的結果型別是它的傳回型別,而傳回型別的非同步方法的結果型別 System.Threading.Tasks.Task<T>TOtherwise, the result type of a non-async method is its return type, and the result type of an async method with return type System.Threading.Tasks.Task<T> is T.

當方法具有 void 結果型別和區塊主體時, return 不允許在區塊中 (return 語句) 語句指定運算式。When a method has a void result type and a block body, return statements (The return statement) in the block are not permitted to specify an expression. 如果 void 方法的區塊執行正常完成 (也就是,控制從方法主體結尾的流程) ,該方法只會傳回目前的呼叫端。If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its current caller.

當方法有 void 結果和運算式主體時,運算式 E 必須是 statement_expression,且主體完全等同于表單的區塊主體 { E; }When a method has a void result and an expression body, the expression E must be a statement_expression, and the body is exactly equivalent to a block body of the form { E; }.

當方法具有非 void 結果型別和區塊主體時, return 區塊中的每個語句都必須指定可隱含轉換成結果型別的運算式。When a method has a non-void result type and a block body, each return statement in the block must specify an expression that is implicitly convertible to the result type. 傳回值之方法的區塊主體端點必須無法連線。The endpoint of a block body of a value-returning method must not be reachable. 換句話說,在具有區塊主體的傳回值的方法中,不允許控制項在方法主體結尾流動。In other words, in a value-returning method with a block body, control is not permitted to flow off the end of the method body.

當方法具有非 void 結果型別和運算式主體時,運算式必須可以隱含地轉換成結果型別,而且主體完全等同于表單的區塊主體 { return E; }When a method has a non-void result type and an expression body, the expression must be implicitly convertible to the result type, and the body is exactly equivalent to a block body of the form { return E; }.

在範例中In the example

class A
{
    public int F() {}            // Error, return value required

    public int G() {
        return 1;
    }

    public int H(bool b) {
        if (b) {
            return 1;
        }
        else {
            return 0;
        }
    }

    public int I(bool b) => b ? 1 : 0;
}

傳回值的 F 方法會導致編譯時期錯誤,因為控制項可以在方法主體的結尾流動。the value-returning F method results in a compile-time error because control can flow off the end of the method body. GH 方法都是正確的,因為所有可能的執行路徑都以指定傳回值的 return 語句結尾。The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. I方法是正確的,因為它的主體相當於語句區塊,其中只包含單一 return 語句。The I method is correct, because its body is equivalent to a statement block with just a single return statement in it.

方法多載Method overloading

方法多載解析規則會在 型別推斷中描述。The method overload resolution rules are described in Type inference.

屬性Properties

*屬性 _ 是可存取物件或類別特性的成員。A *property _ is a member that provides access to a characteristic of an object or a class. 屬性的範例包括字串的長度、字型的大小、視窗的標題、客戶的名稱,等等。Examples of properties include the length of a string, the size of a font, the caption of a window, the name of a customer, and so on. 屬性是欄位的自然延伸(兩者都是具有相關聯類型的命名成員),而存取欄位和屬性的語法相同。Properties are a natural extension of fields—both are named members with associated types, and the syntax for accessing fields and properties is the same. 不過,與欄位不同的是,屬性並不會指示儲存位置。However, unlike fields, properties do not denote storage locations. 相反地,屬性具有 _ 存取 子 *,可指定讀取或寫入其值時要執行的語句。Instead, properties have _ accessors* that specify the statements to be executed when their values are read or written. 因此,屬性會提供一種機制,讓動作與物件屬性的讀取和寫入產生關聯;此外,它們也允許計算這類屬性。Properties thus provide a mechanism for associating actions with the reading and writing of an object's attributes; furthermore, they permit such attributes to be computed.

屬性是使用 property_declaration s 來宣告:Properties are declared using property_declaration s:

property_declaration
    : attributes? property_modifier* type member_name property_body
    ;

property_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | property_modifier_unsafe
    ;

property_body
    : '{' accessor_declarations '}' property_initializer?
    | '=>' expression ';'
    ;

property_initializer
    : '=' variable_initializer ';'
    ;

Property_declaration 可能包含一組 屬性 (屬性) 和四個存取修飾詞的有效組合 (存取修飾詞) 、 (新的修飾詞) 、 (new static 靜態和實例方法) 、 (虛擬方法) 、 (覆寫方法) 、 (密封方法) 、 (抽象方法) , virtual override sealed abstract 以及 (extern 外部方法) 修飾詞。A property_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

屬性宣告受限於與方法宣告相同的規則, (方法 與有效的修飾片語合有關) 。Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

屬性宣告的 別會指定宣告所引進的屬性型別,而 member_name 則會指定屬性的名稱。The type of a property declaration specifies the type of the property introduced by the declaration, and the member_name specifies the name of the property. 除非屬性是明確的介面成員執行,否則 member_name 只是一個 識別碼Unless the property is an explicit interface member implementation, the member_name is simply an identifier. 針對明確的介面成員實 () 的 明確介面成員實作為, member_name 包含後面接著 " . " 和一個 識別碼 的 interface_type。For an explicit interface member implementation (Explicit interface member implementations), the member_name consists of an interface_type followed by a "." and an identifier.

屬性的 類型 至少必須可以像屬性本身一樣存取 (存取範圍條件約束) 。The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Property_body 可能是由 存取子主體 _ 或 _運算式主體*_ 所組成。A property_body may either consist of an accessor body _ or an _expression body*_. 在存取子主體中,_accessor_declarations * (必須以 " { " 和 " } " 標記括住),請 (屬性的 存取 子) 宣告存取子。In an accessor body, _accessor_declarations*, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. 存取子會指定與讀取和寫入屬性相關聯的可執行語句。The accessors specify the executable statements associated with reading and writing the property.

=>後面接著運算式和分號的運算式主體 E ,與語句主體完全相等 { get { return E; } } ,因此只能用來指定僅限 getter 的屬性,其中 getter 的結果是由單一運算式所指定。An expression body consisting of => followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify getter-only properties where the result of the getter is given by a single expression.

Property_initializer 可能只會提供自動執行的屬性 (自動執行的屬性) ,並使這類屬性的基礎欄位初始化為 運算式 所提供的值。A property_initializer may only be given for an automatically implemented property (Automatically implemented properties), and causes the initialization of the underlying field of such properties with the value given by the expression.

雖然存取屬性的語法與欄位的語法相同,但是屬性不會分類為變數。Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. 因此,不可能將屬性傳遞為 refout 引數。Thus, it is not possible to pass a property as a ref or out argument.

當屬性宣告包含修飾詞時 extern ,屬性會被視為 *external 屬性 _。When a property declaration includes an extern modifier, the property is said to be an *external property _. 因為外部屬性宣告不提供實際的實值,所以它的每個 _accessor_declarations * 都是由分號組成。Because an external property declaration provides no actual implementation, each of its _accessor_declarations* consists of a semicolon.

靜態和實例屬性Static and instance properties

當屬性宣告包含修飾詞時 static ,屬性會被視為 *靜態屬性 _。When a property declaration includes a static modifier, the property is said to be a *static property _. 當沒有任何修飾詞時 static ,屬性會被視為 _ 實例屬性 *。When no static modifier is present, the property is said to be an _*instance property**.

靜態屬性未與特定實例相關聯,而且是 this 在靜態屬性的存取子中參考的編譯時期錯誤。A static property is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static property.

實例屬性與類別的特定實例相關聯,而且可以 this 在該屬性的存取子中,以 (此存取) 的方式存取該實例。An instance property is associated with a given instance of a class, and that instance can be accessed as this (This access) in the accessors of that property.

當表單 member_access (成員存取) 中參考屬性時 E.M ,如果 M 是靜態屬性,則 E 必須代表包含的型別 M ,而且如果 M 是實例屬性,則 E 必須代表包含的型別的實例 MWhen a property is referenced in a member_access (Member access) of the form E.M, if M is a static property, E must denote a type containing M, and if M is an instance property, E must denote an instance of a type containing M.

靜態和實例成員之間的差異會在 靜態和實例成員中進一步討論。The differences between static and instance members are discussed further in Static and instance members.

存取子Accessors

屬性的 accessor_declarations 會指定與讀取和寫入該屬性相關聯的可執行語句。The accessor_declarations of a property specify the executable statements associated with reading and writing that property.

accessor_declarations
    : get_accessor_declaration set_accessor_declaration?
    | set_accessor_declaration get_accessor_declaration?
    ;

get_accessor_declaration
    : attributes? accessor_modifier? 'get' accessor_body
    ;

set_accessor_declaration
    : attributes? accessor_modifier? 'set' accessor_body
    ;

accessor_modifier
    : 'protected'
    | 'internal'
    | 'private'
    | 'protected' 'internal'
    | 'internal' 'protected'
    ;

accessor_body
    : block
    | ';'
    ;

存取子宣告包含 get_accessor_declarationset_accessor_declaration 或兩者。The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. 每個存取子宣告都是由標記組成, getset 後面接著選擇性的 accessor_modifieraccessor_bodyEach accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

使用 accessor_modifier s 受下列限制所規範:The use of accessor_modifier s is governed by the following restrictions:

  • Accessor_modifier 不能用在介面或明確介面成員執行中。An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • 針對沒有修飾詞的屬性或索引子 override ,只有當屬性或索引子同時具有和存取子,然後 getset 允許這些存取子的其中一個時,才允許 accessor_modifier。For a property or indexer that has no override modifier, an accessor_modifier is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors.
  • 若為包含修飾詞的屬性或索引子 override ,存取子必須符合正在覆寫之存取子的 accessor_modifier(如果有的話)。For a property or indexer that includes an override modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden.
  • Accessor_modifier 必須宣告與屬性或索引子本身的宣告存取範圍嚴格更嚴格的存取範圍。The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. 精確:To be precise:
    • 如果屬性或索引子具有的宣告存取範圍 publicaccessor_modifier 可以是 protected internalinternalprotectedprivateIf the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • 如果屬性或索引子具有的宣告存取範圍 protected internalaccessor_modifier 可以是 internalprotectedprivateIf the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • 如果屬性或索引子具有或的宣告存取 internal 範圍 protected ,則 accessor_modifier 必須是 privateIf the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • 如果屬性或索引子具有的宣告存取範圍 private ,則不會使用任何 accessor_modifierIf the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

針對 abstractextern 屬性,指定的每個存取子的 accessor_body 只是分號。For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. 非抽象的非 extern 屬性可能會讓每個 accessor_body 都是分號,在這種情況下,它會是 自動執行的屬性 _ (自動實作為) 的 屬性A non-abstract, non-extern property may have each accessor_body be a semicolon, in which case it is an *automatically implemented property _ (Automatically implemented properties). 自動執行的屬性至少必須有 get 存取子。An automatically implemented property must have at least a get accessor. 對於任何其他非抽象、非 extern 屬性的存取子,_accessor_body * 是 區塊 ,可指定叫用對應存取子時要執行的語句。For the accessors of any other non-abstract, non-extern property, the _accessor_body* is a block which specifies the statements to be executed when the corresponding accessor is invoked.

get存取子對應至具有屬性類型之傳回值的無參數方法。A get accessor corresponds to a parameterless method with a return value of the property type. 除了做為指派的目標之外,在運算式中參考屬性時,會叫用 get 屬性的存取子,以計算) 運算式 (值 的屬性值。Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property (Values of expressions). 存取子的主體 get 必須符合 方法主體中所述之傳回值方法的規則。The body of a get accessor must conform to the rules for value-returning methods described in Method body. 尤其是, return 存取子主體中的所有語句都 get 必須指定可隱含轉換成屬性類型的運算式。In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. 此外,存取子的端點 get 必須無法連線。Furthermore, the endpoint of a get accessor must not be reachable.

存取子會 set 對應至具有屬性類型的單一值參數和傳回類型的方法 voidA set accessor corresponds to a method with a single value parameter of the property type and a void return type. 存取子的隱含參數 set 一律會命名 valueThe implicit parameter of a set accessor is always named value. 當屬性被參考為指派的目標 (指派運算子) ,或做為 ++ 或 (後置 -- 遞增和遞減運算子前置遞增和遞減) 運算子 的運算元時, set 就會叫用此存取子,其值為 (++ -- 簡單指派) 中提供新值的引數 (或運算子的運算元。When a property is referenced as the target of an assignment (Assignment operators), or as the operand of ++ or -- (Postfix increment and decrement operators, Prefix increment and decrement operators), the set accessor is invoked with an argument (whose value is that of the right-hand side of the assignment or the operand of the ++ or -- operator) that provides the new value (Simple assignment). 存取子的主體 set 必須符合 void 方法主體中所述之方法的規則。The body of a set accessor must conform to the rules for void methods described in Method body. 尤其是, return 存取子主體中的語句 set 不允許指定運算式。In particular, return statements in the set accessor body are not permitted to specify an expression. 因為 set 存取子會隱含地擁有名為的參數 value ,所以存取子中的區域變數或常數宣告會有該名稱的編譯時期錯誤 setSince a set accessor implicitly has a parameter named value, it is a compile-time error for a local variable or constant declaration in a set accessor to have that name.

根據 get 和存取子的存在與否 set ,屬性的分類如下:Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • 包含存取子和存取子的屬性 get set 稱為 讀寫 屬性。A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • 只有具有存取子的屬性會視為 get 唯讀 屬性。A property that has only a get accessor is said to be a read-only property. 這是唯讀屬性的編譯時期錯誤,是指派的目標。It is a compile-time error for a read-only property to be the target of an assignment.
  • 只有具有存取子的屬性 set 稱為「 僅限寫入 」屬性(property)。A property that has only a set accessor is said to be a write-only property. 除了做為指派的目標之外,它也是在運算式中參考僅限寫入屬性的編譯時期錯誤。Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

在範例中In the example

public class Button: Control
{
    private string caption;

    public string Caption {
        get {
            return caption;
        }
        set {
            if (caption != value) {
                caption = value;
                Repaint();
            }
        }
    }

    public override void Paint(Graphics g, Rectangle r) {
        // Painting code goes here
    }
}

Button控制項宣告公用 Caption 屬性。the Button control declares a public Caption property. 屬性的存取子會傳回 get Caption 儲存在私用欄位中的字串 captionThe get accessor of the Caption property returns the string stored in the private caption field. set存取子會檢查新值是否與目前的值不同,如果是,則會儲存新的值,並重新繪製控制項。The set accessor checks if the new value is different from the current value, and if so, it stores the new value and repaints the control. 屬性通常會遵循如上所示的模式:存取子只會傳回 get 儲存在私用欄位中的值,而存取子則 set 會修改該私用欄位,然後執行完全更新物件狀態所需的任何其他動作。Properties often follow the pattern shown above: The get accessor simply returns a value stored in a private field, and the set accessor modifies that private field and then performs any additional actions required to fully update the state of the object.

由於 Button 上述類別,下列是使用屬性的範例 CaptionGiven the Button class above, the following is an example of use of the Caption property:

Button okButton = new Button();
okButton.Caption = "OK";            // Invokes set accessor
string s = okButton.Caption;        // Invokes get accessor

在這裡,藉 set 由指派值給屬性來叫用存取子,並藉 get 由參考運算式中的屬性來叫用存取子。Here, the set accessor is invoked by assigning a value to the property, and the get accessor is invoked by referencing the property in an expression.

get屬性的和 set 存取子不是相異的成員,也不能另外宣告屬性的存取子。The get and set accessors of a property are not distinct members, and it is not possible to declare the accessors of a property separately. 因此,讀寫屬性的兩個存取子不可能有不同的存取範圍。As such, it is not possible for the two accessors of a read-write property to have different accessibility. 範例The example

class A
{
    private string name;

    public string Name {                // Error, duplicate member name
        get { return name; }
    }

    public string Name {                // Error, duplicate member name
        set { name = value; }
    }
}

不會宣告單一讀寫屬性。does not declare a single read-write property. 相反地,它會宣告兩個具有相同名稱的屬性,一個是唯讀的,另一個只寫入。Rather, it declares two properties with the same name, one read-only and one write-only. 因為在相同類別中宣告的兩個成員不能有相同的名稱,所以此範例會導致編譯時期錯誤發生。Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

當衍生類別以與繼承屬性相同的名稱來宣告屬性時,衍生的屬性會隱藏與讀取和寫入相關的繼承屬性。When a derived class declares a property by the same name as an inherited property, the derived property hides the inherited property with respect to both reading and writing. 在範例中In the example

class A
{
    public int P {
        set {...}
    }
}

class B: A
{
    new public int P {
        get {...}
    }
}

P中的屬性會 B 隱藏 P 中的屬性,以及 A 讀取和寫入的相關內容。the P property in B hides the P property in A with respect to both reading and writing. 因此,在語句中Thus, in the statements

B b = new B();
b.P = 1;          // Error, B.P is read-only
((A)b).P = 1;     // Ok, reference to A.P

的指派 b.P 會導致報告編譯時期錯誤,因為中的唯讀 P 屬性會 B 隱藏中的僅限寫入 P 屬性 Athe assignment to b.P causes a compile-time error to be reported, since the read-only P property in B hides the write-only P property in A. 不過請注意,轉換可以用來存取隱藏的 P 屬性。Note, however, that a cast can be used to access the hidden P property.

不同于公用欄位,屬性會提供物件的內部狀態和其公用介面之間的分隔。Unlike public fields, properties provide a separation between an object's internal state and its public interface. 請設想下列範例:Consider the example:

class Label
{
    private int x, y;
    private string caption;

    public Label(int x, int y, string caption) {
        this.x = x;
        this.y = y;
        this.caption = caption;
    }

    public int X {
        get { return x; }
    }

    public int Y {
        get { return y; }
    }

    public Point Location {
        get { return new Point(x, y); }
    }

    public string Caption {
        get { return caption; }
    }
}

在此, Label 類別會使用兩個 int 欄位 xy 來儲存其位置。Here, the Label class uses two int fields, x and y, to store its location. 這個位置會以和屬性的形式公開,以及 X Y 做為 Location 型別的屬性 PointThe location is publicly exposed both as an X and a Y property and as a Location property of type Point. 如果在未來的版本中,將 Label 位置儲存為內部,就會變得更方便 Point ,而不會影響類別的公用介面:If, in a future version of Label, it becomes more convenient to store the location as a Point internally, the change can be made without affecting the public interface of the class:

class Label
{
    private Point location;
    private string caption;

    public Label(int x, int y, string caption) {
        this.location = new Point(x, y);
        this.caption = caption;
    }

    public int X {
        get { return location.x; }
    }

    public int Y {
        get { return location.y; }
    }

    public Point Location {
        get { return location; }
    }

    public string Caption {
        get { return caption; }
    }
}

不論 x ypublic readonly 欄位,都不可能對類別進行這 Label 類變更。Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

透過屬性公開狀態並不一定比直接公開欄位更有效率。Exposing state through properties is not necessarily any less efficient than exposing fields directly. 尤其是當屬性為非虛擬且只包含少量的程式碼時,執行環境可能會以存取子的實際程式碼取代對存取子的呼叫。In particular, when a property is non-virtual and contains only a small amount of code, the execution environment may replace calls to accessors with the actual code of the accessors. 此程式稱為「 內嵌」(property),它可讓屬性存取效率為現場存取,同時保有更大的屬性彈性。This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

因為叫用 get 存取子在概念上相當於讀取欄位的值,所以會將其視為不良的程式設計樣式,讓 get 存取子具有可觀察的副作用。Since invoking a get accessor is conceptually equivalent to reading the value of a field, it is considered bad programming style for get accessors to have observable side-effects. 在範例中In the example

class Counter
{
    private int next;

    public int Next {
        get { return next++; }
    }
}

屬性的值 Next 取決於先前存取屬性的次數。the value of the Next property depends on the number of times the property has previously been accessed. 因此,存取屬性會產生可觀察的副作用,而屬性則應實作為方法。Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

存取子的「沒有副作用」慣例 get 不表示 get 應該一律撰寫存取子,以只傳回儲存在欄位中的值。The "no side-effects" convention for get accessors doesn't mean that get accessors should always be written to simply return values stored in fields. 實際上, get 存取子通常會藉由存取多個欄位或叫用方法來計算屬性的值。Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. 不過,正確設計的 get 存取子不會執行任何動作,而不會在物件的狀態中造成可觀察的變更。However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

在第一次參考資源之前,可以使用屬性來延遲資源的初始化。Properties can be used to delay initialization of a resource until the moment it is first referenced. 例如:For example:

using System.IO;

public class Console
{
    private static TextReader reader;
    private static TextWriter writer;
    private static TextWriter error;

    public static TextReader In {
        get {
            if (reader == null) {
                reader = new StreamReader(Console.OpenStandardInput());
            }
            return reader;
        }
    }

    public static TextWriter Out {
        get {
            if (writer == null) {
                writer = new StreamWriter(Console.OpenStandardOutput());
            }
            return writer;
        }
    }

    public static TextWriter Error {
        get {
            if (error == null) {
                error = new StreamWriter(Console.OpenStandardError());
            }
            return error;
        }
    }
}

Console類別包含三個 In Out Error 分別代表標準輸入、輸出和錯誤裝置的屬性:、和。The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. 藉由將這些成員公開為屬性, Console 類別可以延遲其初始化,直到實際使用為止。By exposing these members as properties, the Console class can delay their initialization until they are actually used. 例如,在第一次參考 Out 屬性時,如下所示:For example, upon first referencing the Out property, as in

Console.Out.WriteLine("hello, world");

TextWriter建立輸出裝置的基礎。the underlying TextWriter for the output device is created. 但是,如果應用程式沒有 In 和屬性的參考 Error ,則不會為這些裝置建立任何物件。But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

自動執行的屬性Automatically implemented properties

自動執行的屬性 (或 short) 的 自動屬性 ,是具有僅限分號存取子主體的非抽象非外部屬性。An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Auto 屬性必須有 get 存取子,而且可以選擇性地有 set 存取子。Auto-properties must have a get accessor and can optionally have a set accessor.

當屬性指定為自動執行的屬性時,隱藏的支援欄位會自動提供給屬性使用,而存取子則會實作為讀取和寫入該支援欄位。When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. 如果 auto 屬性沒有 set 存取子,則會將支援欄位視為 readonly (Readonly 欄位) 。If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). 就像 readonly 欄位一樣,也可以在封入類別的函式主體中,將僅限 getter 的 auto 屬性指派給。Just like a readonly field, a getter-only auto-property can also be assigned to in the body of a constructor of the enclosing class. 這類指派會直接指派給屬性的 readonly 支援欄位。Such an assignment assigns directly to the readonly backing field of the property.

Auto 屬性可能會選擇性地有 property_initializer,這會直接套用至支援欄位,作為 (變數初始化運算式) 的 variable_initializerAn auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

下列範例將:The following example:

public class Point {
    public int X { get; set; } = 0;
    public int Y { get; set; } = 0;
}

相當於下列宣告:is equivalent to the following declaration:

public class Point {
    private int __x = 0;
    private int __y = 0;
    public int X { get { return __x; } set { __x = value; } }
    public int Y { get { return __y; } set { __y = value; } }
}

下列範例將:The following example:

public class ReadOnlyPoint
{
    public int X { get; }
    public int Y { get; }
    public ReadOnlyPoint(int x, int y) { X = x; Y = y; }
}

相當於下列宣告:is equivalent to the following declaration:

public class ReadOnlyPoint
{
    private readonly int __x;
    private readonly int __y;
    public int X { get { return __x; } }
    public int Y { get { return __y; } }
    public ReadOnlyPoint(int x, int y) { __x = x; __y = y; }
}

請注意,唯讀欄位的指派是合法的,因為它們出現在函式中。Notice that the assignments to the readonly field are legal, because they occur within the constructor.

協助工具選項Accessibility

如果存取子有 accessor_modifier,存取子的存取範圍 網域 (存取範圍網域) 存取子,是使用 accessor_modifier 的宣告存取範圍來決定。If an accessor has an accessor_modifier, the accessibility domain (Accessibility domains) of the accessor is determined using the declared accessibility of the accessor_modifier. 如果存取子沒有 accessor_modifier,就會從屬性或索引子的宣告存取範圍決定存取子的存取範圍網域。If an accessor does not have an accessor_modifier, the accessibility domain of the accessor is determined from the declared accessibility of the property or indexer.

Accessor_modifier 的存在,絕對不會影響成員查閱 (運算子) 或多載解析 (多載 解析) 。The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). 無論存取的內容為何,屬性或索引子上的修飾詞一律會決定要系結的屬性或索引子。The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

一旦選取了特定屬性或索引子,所牽涉特定存取子的存取範圍網域就會用來判斷該使用方式是否有效:Once a particular property or indexer has been selected, the accessibility domains of the specific accessors involved are used to determine if that usage is valid:

  • 如果使用方式是 () 運算式值 的值,則存取子 get 必須存在且可供存取。If the usage is as a value (Values of expressions), the get accessor must exist and be accessible.
  • 如果使用方式是簡單指派) (簡單 指派的目標,則 set 存取子必須存在且可供存取。If the usage is as the target of a simple assignment (Simple assignment), the set accessor must exist and be accessible.
  • 如果使用方式是做為複合指派的目標 (複合指派) ,或做為 ++ or 運算子的目標 -- (函數成員.9、 調用運算式) ,則存取子 get 和存取子 set 必須存在且可供存取。If the usage is as the target of compound assignment (Compound assignment), or as the target of the ++ or -- operators (Function members.9, Invocation expressions), both the get accessors and the set accessor must exist and be accessible.

在下列範例中,屬性 A.Text 會被屬性隱藏 B.Text ,即使在只呼叫存取子的內容中也是如此 setIn the following example, the property A.Text is hidden by the property B.Text, even in contexts where only the set accessor is called. 相反 B.Count 地,類別無法存取屬性 M ,因此會改用可存取的屬性 A.CountIn contrast, the property B.Count is not accessible to class M, so the accessible property A.Count is used instead.

class A
{
    public string Text {
        get { return "hello"; }
        set { }
    }

    public int Count {
        get { return 5; }
        set { }
    }
}

class B: A
{
    private string text = "goodbye"; 
    private int count = 0;

    new public string Text {
        get { return text; }
        protected set { text = value; }
    }

    new protected int Count { 
        get { return count; }
        set { count = value; }
    }
}

class M
{
    static void Main() {
        B b = new B();
        b.Count = 12;             // Calls A.Count set accessor
        int i = b.Count;          // Calls A.Count get accessor
        b.Text = "howdy";         // Error, B.Text set accessor not accessible
        string s = b.Text;        // Calls B.Text get accessor
    }
}

用來執行介面的存取子可能沒有 accessor_modifierAn accessor that is used to implement an interface may not have an accessor_modifier. 如果只有一個存取子用來執行介面,則可以使用 accessor_modifier 來宣告其他存取子:If only one accessor is used to implement an interface, the other accessor may be declared with an accessor_modifier:

public interface I
{
    string Prop { get; }
}

public class C: I
{
    public string Prop {
        get { return "April"; }       // Must not have a modifier here
        internal set {...}            // Ok, because I.Prop has no set accessor
    }
}

Virtual、sealed、override 和 abstract 屬性存取子Virtual, sealed, override, and abstract property accessors

virtual屬性宣告會指定屬性的存取子是虛擬的。A virtual property declaration specifies that the accessors of the property are virtual. 修飾詞會 virtual 套用至讀寫屬性的兩個存取子,而且讀寫屬性的一個存取子不可能是虛擬的。The virtual modifier applies to both accessors of a read-write property—it is not possible for only one accessor of a read-write property to be virtual.

abstract屬性宣告會指定屬性的存取子是虛擬的,但不會提供存取子的實際執行。An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. 相反地,非抽象衍生類別是必要的,藉由覆寫屬性來為存取子提供自己的實作為。Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. 因為抽象屬性宣告的存取子不提供實際的實值,所以其 accessor_body 只包含分號。Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

包含和修飾詞的屬性宣告 abstract override 會指定屬性為抽象,而且會覆寫基底屬性。A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. 這類屬性的存取子也是抽象的。The accessors of such a property are also abstract.

抽象屬性宣告只能用在抽象類別) (抽象類別 。您可以藉由包含指定指示詞的屬性宣告,在衍生類別中覆寫繼承的虛擬屬性的存取子 overrideAbstract property declarations are only permitted in abstract classes (Abstract classes).The accessors of an inherited virtual property can be overridden in a derived class by including a property declaration that specifies an override directive. 這就是所謂的覆 寫屬性 宣告。This is known as an overriding property declaration. 覆寫屬性宣告不會宣告新的屬性。An overriding property declaration does not declare a new property. 相反地,它只會特製化現有虛擬屬性的存取子。Instead, it simply specializes the implementations of the accessors of an existing virtual property.

覆寫屬性宣告必須指定與繼承屬性完全相同的存取範圍修飾詞、類型和名稱。An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. 如果繼承的屬性只有一個存取子 (也就是,如果繼承的屬性是唯讀的或僅限寫入的) ,則覆寫屬性必須只包含該存取子。If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property must include only that accessor. 如果繼承的屬性同時包含這兩個存取子 (亦即,如果繼承的屬性為讀寫) ,則覆寫屬性可以包含單一存取子或兩個存取子。If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors.

覆寫屬性宣告可能包含 sealed 修飾詞。An overriding property declaration may include the sealed modifier. 使用這個修飾詞可防止衍生類別進一步覆寫屬性。Use of this modifier prevents a derived class from further overriding the property. 密封屬性的存取子也是密封的。The accessors of a sealed property are also sealed.

除了宣告和調用語法的差異之外,virtual、sealed、override 和 abstract 存取子的行為與虛擬、密封、覆寫和抽象方法完全相同。Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. 具體而言, 虛擬方法、覆 寫方法密封方法抽象方法 中所述的規則,也適用于存取子是對應表單的方法:Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • get存取子對應到無參數方法,其具有屬性類型的傳回值,以及與包含屬性相同的修飾詞。A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • 存取子會 set 對應至具有屬性型別之單一值參數的方法、傳回 void 型別,以及與包含屬性相同的修飾詞。A set accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property.

在範例中In the example

abstract class A
{
    int y;

    public virtual int X {
        get { return 0; }
    }

    public virtual int Y {
        get { return y; }
        set { y = value; }
    }

    public abstract int Z { get; set; }
}

X 是虛擬唯讀屬性, Y 它是虛擬讀寫屬性,它 Z 是抽象的讀寫屬性。X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. 因為 Z 是抽象的,所以包含類別 A 也必須宣告為抽象。Because Z is abstract, the containing class A must also be declared abstract.

衍生自的類別 A 如下所示:A class that derives from A is show below:

class B: A
{
    int z;

    public override int X {
        get { return base.X + 1; }
    }

    public override int Y {
        set { base.Y = value < 0? 0: value; }
    }

    public override int Z {
        get { return z; }
        set { z = value; }
    }
}

在這裡, X 、和的宣告 Y 會覆 Z 寫屬性宣告。Here, the declarations of X, Y, and Z are overriding property declarations. 每個屬性宣告完全符合存取範圍修飾詞、類型和對應之繼承屬性的名稱。Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. 的存取子和的存取子, get X set Y 使用 base 關鍵字來存取繼承的存取子。The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. 的宣告 Z 會覆寫這兩個抽象存取子—因此,中沒有未完成的抽象函式成員 B ,且 B 允許為非抽象類別。The declaration of Z overrides both abstract accessors—thus, there are no outstanding abstract function members in B, and B is permitted to be a non-abstract class.

當屬性宣告為時,覆寫程式 override 代碼必須可以存取任何覆寫的存取子。When a property is declared as an override, any overridden accessors must be accessible to the overriding code. 此外,屬性或索引子本身以及存取子的宣告存取範圍,必須符合覆寫的成員和存取子。In addition, the declared accessibility of both the property or indexer itself, and of the accessors, must match that of the overridden member and accessors. 例如:For example:

public class B
{
    public virtual int P {
        protected set {...}
        get {...}
    }
}

public class D: B
{
    public override int P {
        protected set {...}            // Must specify protected here
        get {...}                      // Must not have a modifier here
    }
}

事件Events

*Event _ 是可讓物件或類別提供通知的成員。An *event _ is a member that enables an object or class to provide notifications. 用戶端可以藉由提供 _ 事件處理常式 * 來附加事件的可執行程式碼。Clients can attach executable code for events by supplying _*event handlers**.

事件是使用 event_declaration s 來宣告:Events are declared using event_declaration s:

event_declaration
    : attributes? event_modifier* 'event' type variable_declarators ';'
    | attributes? event_modifier* 'event' type member_name '{' event_accessor_declarations '}'
    ;

event_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | event_modifier_unsafe
    ;

event_accessor_declarations
    : add_accessor_declaration remove_accessor_declaration
    | remove_accessor_declaration add_accessor_declaration
    ;

add_accessor_declaration
    : attributes? 'add' block
    ;

remove_accessor_declaration
    : attributes? 'remove' block
    ;

Event_declaration 可能包含一組 屬性 (屬性) 和四個存取修飾詞的有效組合 (存取修飾詞) 、 (新的修飾詞) 、 (new static 靜態和實例方法) 、 (虛擬方法) 、 (覆寫方法) 、 (密封方法) 、 (抽象方法) , virtual override sealed abstract 以及 (extern 外部方法) 修飾詞。An event_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

事件宣告受限於與方法宣告相同的規則, (方法 與有效的修飾片語合有關) 。Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

事件宣告的 別必須是) 的 delegate_type (參考 型別,而且 delegate_type 必須至少可以像事件本身一樣存取, (存取範圍 條件約束) 。The type of an event declaration must be a delegate_type (Reference types), and that delegate_type must be at least as accessible as the event itself (Accessibility constraints).

事件宣告可能包含 event_accessor_declarationsAn event declaration may include event_accessor_declarations. 但是,如果不是外部的非抽象事件,編譯器會自動提供它們, (類似欄位的事件) ;外來事件的存取子會在外部提供。However, if it does not, for non-extern, non-abstract events, the compiler supplies them automatically (Field-like events); for extern events, the accessors are provided externally.

省略 event_accessor_declarations 定義一個或多個事件的事件宣告,每個 variable_declarator 都有一個事件。An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarator s. 屬性和修飾詞會套用到這類 event_declaration 所宣告的所有成員。The attributes and modifiers apply to all of the members declared by such an event_declaration.

這是一個編譯時期錯誤, event_declaration 同時包含修飾詞和以 abstract 括弧分隔的 event_accessor_declarationsIt is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

當事件宣告包含修飾詞時 extern ,事件稱為 *external event _。When an event declaration includes an extern modifier, the event is said to be an *external event _. 因為外來事件宣告不提供實際的實值,所以它會同時包含 extern 修飾詞和 _event_accessor_declarations * 這項錯誤。Because an external event declaration provides no actual implementation, it is an error for it to include both the extern modifier and _event_accessor_declarations*.

這是具有或修飾詞之事件宣告 variable_declarator 的編譯時期錯誤, abstract external 以包含 variable_initializerIt is a compile-time error for a variable_declarator of an event declaration with an abstract or external modifier to include a variable_initializer.

事件可以用來當做 and 運算子的左運算元 +=-= (事件指派) 。An event can be used as the left-hand operand of the += and -= operators (Event assignment). 分別使用這些運算子來將事件處理常式附加至或從事件中移除事件處理常式,而事件的存取修飾詞會控制允許這類作業的內容。These operators are used, respectively, to attach event handlers to or to remove event handlers from an event, and the access modifiers of the event control the contexts in which such operations are permitted.

由於 +=-= 是在宣告事件之型別的事件之外唯一允許的作業,外部程式碼可以加入和移除事件的處理常式,但無法以任何其他方式取得或修改事件處理常式的基礎清單。Since += and -= are the only operations that are permitted on an event outside the type that declares the event, external code can add and remove handlers for an event, but cannot in any other way obtain or modify the underlying list of event handlers.

在表單 x += y 或的作業中 x -= y ,當 x 是事件,且參考發生在包含宣告的型別之外時 x ,作業的結果會具有類型 (而不是具有的型別,而則是 void x x 指派) 之後的值。In an operation of the form x += y or x -= y, when x is an event and the reference takes place outside the type that contains the declaration of x, the result of the operation has type void (as opposed to having the type of x, with the value of x after the assignment). 此規則會禁止外部程式碼間接檢查事件的基礎委派。This rule prohibits external code from indirectly examining the underlying delegate of an event.

下列範例顯示如何將事件處理常式附加至類別的實例 ButtonThe following example shows how event handlers are attached to instances of the Button class:

public delegate void EventHandler(object sender, EventArgs e);

public class Button: Control
{
    public event EventHandler Click;
}

public class LoginDialog: Form
{
    Button OkButton;
    Button CancelButton;

    public LoginDialog() {
        OkButton = new Button(...);
        OkButton.Click += new EventHandler(OkButtonClick);
        CancelButton = new Button(...);
        CancelButton.Click += new EventHandler(CancelButtonClick);
    }

    void OkButtonClick(object sender, EventArgs e) {
        // Handle OkButton.Click event
    }

    void CancelButtonClick(object sender, EventArgs e) {
        // Handle CancelButton.Click event
    }
}

在此,實例的函式會 LoginDialog 建立兩個 Button 實例,並將事件處理常式附加至 Click 事件。Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

類似欄位的事件Field-like events

在包含事件宣告之類別或結構的程式文字內,某些事件可以像欄位一樣使用。Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. 若要以這種方式使用,事件不得為 abstractextern ,而且不得明確包含 event_accessor_declarationsTo be used in this way, an event must not be abstract or extern, and must not explicitly include event_accessor_declarations. 這類事件可用於允許欄位的任何內容。Such an event can be used in any context that permits a field. 此欄位包含 委派 (委派) 參考已新增至事件的事件處理常式清單。The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. 如果未加入任何事件處理常式,則欄位會包含 nullIf no event handlers have been added, the field contains null.

在範例中In the example

public delegate void EventHandler(object sender, EventArgs e);

public class Button: Control
{
    public event EventHandler Click;

    protected void OnClick(EventArgs e) {
        if (Click != null) Click(this, e);
    }

    public void Reset() {
        Click = null;
    }
}

Click 會當做類別內的欄位使用 ButtonClick is used as a field within the Button class. 如範例所示,您可以在委派調用運算式中檢查、修改和使用此欄位。As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. OnClick類別中的方法會「 Button 引發」 Click 事件。The OnClick method in the Button class "raises" the Click event. 引發事件的概念完全等同於叫用該事件所代表的委派項目,因此,就引發事件而言,並沒有任何特殊的語言建構。The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events. 請注意,委派調用前面會有檢查,以確保委派為非 null。Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

在類別的宣告之外 ButtonClick 成員只能在和運算子的左側使用 += -= ,如下所示:Outside the declaration of the Button class, the Click member can only be used on the left-hand side of the += and -= operators, as in

b.Click += new EventHandler(...);

這會將委派附加至事件的調用清單 Click ,以及which appends a delegate to the invocation list of the Click event, and

b.Click -= new EventHandler(...);

這會從事件的調用清單移除委派 Clickwhich removes a delegate from the invocation list of the Click event.

編譯類似欄位的事件時,編譯器會自動建立儲存區以保存委派,並為加入或移除委派欄位之事件處理常式的事件建立存取子。When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. 新增和移除作業都是安全線程,而且可能 (但不需要在針對實例事件的包含物件上保留鎖定 (鎖定) 語句 ,或是在靜態事件 (匿名物件建立運算式) 的型別物件時,) 完成。The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock (The lock statement) on the containing object for an instance event, or the type object (Anonymous object creation expressions) for a static event.

因此,下列形式的實例事件宣告:Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

會編譯成相當於下列專案的內容:will be compiled to something equivalent to:

class X
{
    private D __Ev;  // field to hold the delegate

    public event D Ev {
        add {
            /* add the delegate in a thread safe way */
        }

        remove {
            /* remove the delegate in a thread safe way */
        }
    }
}

在類別中 XEv 和運算子左邊的參考 += -= 會導致叫用加入和移除存取子。Within the class X, references to Ev on the left-hand side of the += and -= operators cause the add and remove accessors to be invoked. 所有其他參考 Ev 都會編譯為參考隱藏的欄位, __Ev 而不是 (成員存取) 。All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). 名稱 " __Ev " 是任意的; 隱藏欄位可以有任何名稱或完全沒有名稱。The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

事件存取子Event accessors

事件宣告通常會省略 event_accessor_declarations,如 Button 上述範例所示。Event declarations typically omit event_accessor_declarations, as in the Button example above. 這樣做的其中一種情況,就是您無法接受每個事件一個欄位的儲存體成本。One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. 在這種情況下,類別可以包含 event_accessor_declarations ,並使用私用機制來儲存事件處理常式的清單。In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

事件的 event_accessor_declarations 會指定與新增和移除事件處理常式相關聯的可執行語句。The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

存取子宣告包含 add_accessor_declarationremove_accessor_declarationThe accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. 每個存取子宣告都是由 token addremove 後面接著 區塊 所組成。Each accessor declaration consists of the token add or remove followed by a block. add_accessor_declaration 相關聯的 區塊 會指定加入事件處理常式時要執行的語句,而與 remove_accessor_declaration 相關聯的 區塊 則會指定移除事件處理常式時要執行的語句。The block associated with an add_accessor_declaration specifies the statements to execute when an event handler is added, and the block associated with a remove_accessor_declaration specifies the statements to execute when an event handler is removed.

每個 add_accessor_declarationremove_accessor_declaration 都會對應到一個具有事件種類和傳回型別之單一值參數的方法 voidEach add_accessor_declaration and remove_accessor_declaration corresponds to a method with a single value parameter of the event type and a void return type. 事件存取子的隱含參數命名為 valueThe implicit parameter of an event accessor is named value. 當事件在事件指派中使用時,會使用適當的事件存取子。When an event is used in an event assignment, the appropriate event accessor is used. 具體而言,如果指派運算子是 += ,則會使用 add 存取子,而且如果指派運算子是,則 -= 會使用 remove 存取子。Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is -= then the remove accessor is used. 無論是哪一種情況,指派運算子的右邊運算元都會當做事件存取子的引數使用。In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. Add_accessor_declarationremove_accessor_declaration 的區塊必須符合 void 方法主體中所述之方法的規則。The block of an add_accessor_declaration or a remove_accessor_declaration must conform to the rules for void methods described in Method body. 特別是, return 這類區塊中的語句不允許指定運算式。In particular, return statements in such a block are not permitted to specify an expression.

因為事件存取子會隱含地擁有名為的參數 value ,所以在事件存取子中宣告的區域變數或常數必須有該名稱的編譯時期錯誤。Since an event accessor implicitly has a parameter named value, it is a compile-time error for a local variable or constant declared in an event accessor to have that name.

在範例中In the example

class Control: Component
{
    // Unique keys for events
    static readonly object mouseDownEventKey = new object();
    static readonly object mouseUpEventKey = new object();

    // Return event handler associated with key
    protected Delegate GetEventHandler(object key) {...}

    // Add event handler associated with key
    protected void AddEventHandler(object key, Delegate handler) {...}

    // Remove event handler associated with key
    protected void RemoveEventHandler(object key, Delegate handler) {...}

    // MouseDown event
    public event MouseEventHandler MouseDown {
        add { AddEventHandler(mouseDownEventKey, value); }
        remove { RemoveEventHandler(mouseDownEventKey, value); }
    }

    // MouseUp event
    public event MouseEventHandler MouseUp {
        add { AddEventHandler(mouseUpEventKey, value); }
        remove { RemoveEventHandler(mouseUpEventKey, value); }
    }

    // Invoke the MouseUp event
    protected void OnMouseUp(MouseEventArgs args) {
        MouseEventHandler handler; 
        handler = (MouseEventHandler)GetEventHandler(mouseUpEventKey);
        if (handler != null)
            handler(this, args);
    }
}

類別會實作為 Control 事件的內部儲存機制。the Control class implements an internal storage mechanism for events. 方法會將 AddEventHandler 委派值與索引鍵產生關聯, GetEventHandler 方法會傳回目前與索引鍵相關聯的委派,而且方法會將 RemoveEventHandler 委派移除為指定之事件的事件處理常式。The AddEventHandler method associates a delegate value with a key, the GetEventHandler method returns the delegate currently associated with a key, and the RemoveEventHandler method removes a delegate as an event handler for the specified event. 有可能是基礎儲存機制的設計,就是將委派值與索引鍵建立關聯不會產生任何費用 null ,因此未處理的事件也不會耗用任何儲存體。Presumably, the underlying storage mechanism is designed such that there is no cost for associating a null delegate value with a key, and thus unhandled events consume no storage.

靜態和實例事件Static and instance events

當事件宣告包含修飾詞時 static ,事件稱為「靜態事件 _」。When an event declaration includes a static modifier, the event is said to be a *static event _. 如果沒有任何 static 修飾詞,則會將事件視為 _ 實例事件 *。When no static modifier is present, the event is said to be an _*instance event**.

靜態事件不會與特定實例相關聯,而且是 this 在靜態事件的存取子中參考的編譯時期錯誤。A static event is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static event.

實例事件與類別的特定實例相關聯,而且可以 this 在該事件的存取子中,以 (此存取) 的方式存取此實例。An instance event is associated with a given instance of a class, and this instance can be accessed as this (This access) in the accessors of that event.

在表單的 member_access (成員存取) 中參考事件時 E.M ,如果 M 是靜態事件,則 E 必須代表包含的型別 M ,而如果 M 是實例事件,則 E 必須代表包含的型別的實例 MWhen an event is referenced in a member_access (Member access) of the form E.M, if M is a static event, E must denote a type containing M, and if M is an instance event, E must denote an instance of a type containing M.

靜態和實例成員之間的差異會在 靜態和實例成員中進一步討論。The differences between static and instance members are discussed further in Static and instance members.

虛擬、密封、覆寫和抽象事件存取子Virtual, sealed, override, and abstract event accessors

virtual事件宣告會指定該事件的存取子是虛擬的。A virtual event declaration specifies that the accessors of that event are virtual. virtual修飾詞適用于事件的兩個存取子。The virtual modifier applies to both accessors of an event.

abstract事件宣告會指定事件的存取子是虛擬的,但不會提供存取子的實際執行。An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. 相反地,非抽象衍生類別是必要的,藉由覆寫事件來為存取子提供自己的實作為。Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. 由於抽象事件宣告不提供實際的實值,因此無法提供以大括弧分隔的 event_accessor_declarationsBecause an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

包含和修飾詞的事件宣告 abstract override 會指定事件為抽象,而且會覆寫基底事件。An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. 這類事件的存取子也是抽象的。The accessors of such an event are also abstract.

只有抽象類別) (抽象 類才能使用抽象事件宣告。Abstract event declarations are only permitted in abstract classes (Abstract classes).

您可以藉由包含指定修飾詞的事件宣告,在衍生類別中覆寫繼承的虛擬事件的存取子 overrideThe accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override modifier. 這就是所謂的覆 寫事件聲明This is known as an overriding event declaration. 覆寫事件宣告不會宣告新的事件。An overriding event declaration does not declare a new event. 相反地,它只會專門針對現有虛擬事件的存取子進行特製化。Instead, it simply specializes the implementations of the accessors of an existing virtual event.

覆寫事件宣告必須指定與覆寫事件完全相同的存取範圍修飾詞、類型和名稱。An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

覆寫事件宣告可能包含 sealed 修飾詞。An overriding event declaration may include the sealed modifier. 使用這個修飾詞可防止衍生類別進一步覆寫事件。Use of this modifier prevents a derived class from further overriding the event. 密封事件的存取子也是密封的。The accessors of a sealed event are also sealed.

這是覆寫事件宣告包含修飾詞的編譯時期錯誤 newIt is a compile-time error for an overriding event declaration to include a new modifier.

除了宣告和調用語法的差異之外,virtual、sealed、override 和 abstract 存取子的行為與虛擬、密封、覆寫和抽象方法完全相同。Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. 具體而言, 虛擬方法、覆 寫方法密封方法抽象方法 中所述的規則,也適用于存取子是對應表單的方法。Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form. 每個存取子都與包含事件種類的單一值參數、傳回型別 void ,以及與包含事件的修飾詞相同的方法對應。Each accessor corresponds to a method with a single value parameter of the event type, a void return type, and the same modifiers as the containing event.

索引子Indexers

*索引子 _ 是成員,可讓物件以與陣列相同的方式進行索引。An *indexer _ is a member that enables an object to be indexed in the same way as an array. 使用 _indexer_declaration * s 宣告索引子:Indexers are declared using _indexer_declaration*s:

indexer_declaration
    : attributes? indexer_modifier* indexer_declarator indexer_body
    ;

indexer_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | indexer_modifier_unsafe
    ;

indexer_declarator
    : type 'this' '[' formal_parameter_list ']'
    | type interface_type '.' 'this' '[' formal_parameter_list ']'
    ;

indexer_body
    : '{' accessor_declarations '}' 
    | '=>' expression ';'
    ;

Indexer_declaration 可能包含一組 屬性 (屬性) 和四個存取修飾詞的有效組合 (存取修飾詞) 、 (新的修飾詞) 、 (虛擬方法) 、 (覆寫方法) 、 (密封方法) 、 (new virtual override sealed abstract 抽象方法) ,以及 extern (的 外部方法) 修飾詞。An indexer_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

索引子宣告受限於與方法宣告相同的規則, (方法 與有效的修飾片語合有關) ,唯一的例外是在索引子宣告上不允許靜態修飾詞。Indexer declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers, with the one exception being that the static modifier is not permitted on an indexer declaration.

修飾詞 virtualoverrideabstract 都是互斥的,但在一個情況下除外。The modifiers virtual, override, and abstract are mutually exclusive except in one case. 和修飾詞可以 abstract override 一起使用,讓抽象的索引子可以覆寫虛擬的索引子。The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

索引子宣告的 別會指定宣告所引進之索引子的元素型別。The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. 除非索引子是明確的介面成員執行,否則 類型 後面會接著關鍵字 thisUnless the indexer is an explicit interface member implementation, the type is followed by the keyword this. 若為明確的介面成員執行, 類型 後面會接著 interface_type、" . " 和關鍵字 thisFor an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. 不同于其他成員,索引子沒有使用者定義的名稱。Unlike other members, indexers do not have user-defined names.

Formal_parameter_list 指定索引子的參數。The formal_parameter_list specifies the parameters of the indexer. 索引子的型式參數清單會對應至方法 (方法參數) 的型式參數清單,但是必須指定至少一個參數,而且 ref out 不允許和參數修飾詞。The formal parameter list of an indexer corresponds to that of a method (Method parameters), except that at least one parameter must be specified, and that the ref and out parameter modifiers are not permitted.

索引子的 類型formal_parameter_list 中參考的每個類型至少必須可以像索引子本身一樣存取 () 的 協助工具條件約束The type of an indexer and each of the types referenced in the formal_parameter_list must be at least as accessible as the indexer itself (Accessibility constraints).

Indexer_body 可能是由 存取子主體 _ 或 _運算式主體*_ 所組成。An indexer_body may either consist of an accessor body _ or an _expression body*_. 在存取子主體中,_accessor_declarations * (必須以 " { " 和 " } " 標記括住),請 (屬性的 存取 子) 宣告存取子。In an accessor body, _accessor_declarations*, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. 存取子會指定與讀取和寫入屬性相關聯的可執行語句。The accessors specify the executable statements associated with reading and writing the property.

由 " => " 後面接著運算式和分號所組成的運算式主體 E ,完全等同于語句主體 { get { return E; } } ,因此只能用來指定僅限 getter 的索引子,其中 getter 的結果是由單一運算式提供。An expression body consisting of "=>" followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify getter-only indexers where the result of the getter is given by a single expression.

雖然存取索引子專案的語法與陣列專案的語法相同,但是索引子元素不會分類為變數。Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. 因此,您無法將索引子元素傳遞為 refout 引數。Thus, it is not possible to pass an indexer element as a ref or out argument.

索引子的型式參數清單會定義索引子 (簽章 多載) 。The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. 具體而言,索引子的簽章是由其正式參數的數目和類型所組成。Specifically, the signature of an indexer consists of the number and types of its formal parameters. 專案類型和正式參數的名稱不是索引子簽章的一部分。The element type and names of the formal parameters are not part of an indexer's signature.

索引子的簽章必須與在相同類別中宣告之所有其他索引子的簽章不同。The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

索引子和屬性在概念上非常類似,但下列方式不同:Indexers and properties are very similar in concept, but differ in the following ways:

  • 屬性是由其名稱識別,而索引子則由其簽章識別。A property is identified by its name, whereas an indexer is identified by its signature.
  • 屬性是透過 simple_name (簡單名稱) 或 member_access (成員存取) 來存取,而索引子元素則是透過 element_access (索引子存取) 來存取。A property is accessed through a simple_name (Simple names) or a member_access (Member access), whereas an indexer element is accessed through an element_access (Indexer access).
  • 屬性可以是 static 成員,而索引子一律是實例成員。A property can be a static member, whereas an indexer is always an instance member.
  • get屬性的存取子對應至不含參數的方法,而索引子的存取子則 get 對應至具有與索引子相同型式參數清單的方法。A get accessor of a property corresponds to a method with no parameters, whereas a get accessor of an indexer corresponds to a method with the same formal parameter list as the indexer.
  • 屬性的存取子會 set 與名為的單一參數相對應 value ,而索引子的存取子則 set 對應至具有與索引子相同之正式參數清單的方法,再加上名為的其他參數 valueA set accessor of a property corresponds to a method with a single parameter named value, whereas a set accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named value.
  • 這是一個編譯時期錯誤,索引子存取子宣告的區域變數名稱與索引子參數相同。It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • 在覆寫屬性宣告中,會使用語法來存取繼承的屬性 base.P ,其中 P 是屬性名稱。In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. 在覆寫索引子宣告中,會使用語法來存取繼承的索引子 base[E] ,其中 E 是以逗號分隔的運算式清單。In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • 「自動執行的索引子」沒有任何概念。There is no concept of an "automatically implemented indexer". 具有具有分號存取子的非抽象非外部索引子是錯誤的。It is an error to have a non-abstract, non-external indexer with semicolon accessors.

除了這些差異之外, 存取 子和 自動執行的屬性 中定義的所有規則都會套用至索引子存取子以及屬性存取子。Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

當索引子宣告包含修飾詞時 extern ,索引子就稱為 *external 索引子 _。When an indexer declaration includes an extern modifier, the indexer is said to be an *external indexer _. 因為外部索引子宣告不提供實際的實值,所以它的每個 _accessor_declarations * 都是由分號組成。Because an external indexer declaration provides no actual implementation, each of its _accessor_declarations* consists of a semicolon.

下列範例會宣告一個 BitArray 類別,該類別會執行索引子來存取位陣列中的個別位。The example below declares a BitArray class that implements an indexer for accessing the individual bits in the bit array.

using System;

class BitArray
{
    int[] bits;
    int length;

    public BitArray(int length) {
        if (length < 0) throw new ArgumentException();
        bits = new int[((length - 1) >> 5) + 1];
        this.length = length;
    }

    public int Length {
        get { return length; }
    }

    public bool this[int index] {
        get {
            if (index < 0 || index >= length) {
                throw new IndexOutOfRangeException();
            }
            return (bits[index >> 5] & 1 << index) != 0;
        }
        set {
            if (index < 0 || index >= length) {
                throw new IndexOutOfRangeException();
            }
            if (value) {
                bits[index >> 5] |= 1 << index;
            }
            else {
                bits[index >> 5] &= ~(1 << index);
            }
        }
    }
}

類別的實例所 BitArray 耗用的記憶體比對應的 (明顯少 bool[] ,因為前者的每個值只會佔用一個位,而不是後者的一個位元組) ,但是它允許與相同的作業 bool[]An instance of the BitArray class consumes substantially less memory than a corresponding bool[] (since each value of the former occupies only one bit instead of the latter's one byte), but it permits the same operations as a bool[].

下列 CountPrimes 類別會使用 BitArray 和傳統的 "sieve" 演算法來計算介於1和指定最大值之間的質數數目:The following CountPrimes class uses a BitArray and the classical "sieve" algorithm to compute the number of primes between 1 and a given maximum:

class CountPrimes
{
    static int Count(int max) {
        BitArray flags = new BitArray(max + 1);
        int count = 1;
        for (int i = 2; i <= max; i++) {
            if (!flags[i]) {
                for (int j = i * 2; j <= max; j += i) flags[j] = true;
                count++;
            }
        }
        return count;
    }

    static void Main(string[] args) {
        int max = int.Parse(args[0]);
        int count = Count(max);
        Console.WriteLine("Found {0} primes between 1 and {1}", count, max);
    }
}

請注意,用來存取專案的語法與的專案 BitArray 完全相同 bool[]Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

下列範例顯示具有兩個參數的索引子的 26 * 10 方格類別。The following example shows a 26 * 10 grid class that has an indexer with two parameters. 第一個參數必須是範圍 a-z 內的大寫字母或小寫字母,而第二個參數必須是範圍0-9 中的整數。The first parameter is required to be an upper- or lowercase letter in the range A-Z, and the second is required to be an integer in the range 0-9.

using System;

class Grid
{
    const int NumRows = 26;
    const int NumCols = 10;

    int[,] cells = new int[NumRows, NumCols];

    public int this[char c, int col] {
        get {
            c = Char.ToUpper(c);
            if (c < 'A' || c > 'Z') {
                throw new ArgumentException();
            }
            if (col < 0 || col >= NumCols) {
                throw new IndexOutOfRangeException();
            }
            return cells[c - 'A', col];
        }

        set {
            c = Char.ToUpper(c);
            if (c < 'A' || c > 'Z') {
                throw new ArgumentException();
            }
            if (col < 0 || col >= NumCols) {
                throw new IndexOutOfRangeException();
            }
            cells[c - 'A', col] = value;
        }
    }
}

索引子多載Indexer overloading

型別推斷中描述索引子多載解析規則。The indexer overload resolution rules are described in Type inference.

運算子Operators

*運算子 _ 是一個成員,它會定義運算式運算子的意義,這些運算子可以套用至類別的實例。An *operator _ is a member that defines the meaning of an expression operator that can be applied to instances of the class. 使用 _operator_declaration * s 宣告運算子:Operators are declared using _operator_declaration*s:

operator_declaration
    : attributes? operator_modifier+ operator_declarator operator_body
    ;

operator_modifier
    : 'public'
    | 'static'
    | 'extern'
    | operator_modifier_unsafe
    ;

operator_declarator
    : unary_operator_declarator
    | binary_operator_declarator
    | conversion_operator_declarator
    ;

unary_operator_declarator
    : type 'operator' overloadable_unary_operator '(' type identifier ')'
    ;

overloadable_unary_operator
    : '+' | '-' | '!' | '~' | '++' | '--' | 'true' | 'false'
    ;

binary_operator_declarator
    : type 'operator' overloadable_binary_operator '(' type identifier ',' type identifier ')'
    ;

overloadable_binary_operator
    : '+'   | '-'   | '*'   | '/'   | '%'   | '&'   | '|'   | '^'   | '<<'
    | right_shift | '=='  | '!='  | '>'   | '<'   | '>='  | '<='
    ;

conversion_operator_declarator
    : 'implicit' 'operator' type '(' type identifier ')'
    | 'explicit' 'operator' type '(' type identifier ')'
    ;

operator_body
    : block
    | '=>' expression ';'
    | ';'
    ;

有三種類型的可多載運算子:一元運算子 (一元 運算子) 、二元運算子 (二元 運算子) 和轉換運算子 (轉換運算子) 。There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

Operator_body 可以是分號、語句主體 _ 或 _運算式主體*The operator_body is either a semicolon, a statement body _ or an _expression body*. 語句主體是由 _block * 所組成,這會指定叫用運算子時要執行的語句。A statement body consists of a _block*, which specifies the statements to execute when the operator is invoked. 區塊 必須符合 方法主體中所述之傳回值方法的規則。The block must conform to the rules for value-returning methods described in Method body. 運算式主體是由 => 後面接著運算式和分號所組成,而且表示叫用運算子時要執行的單一運算式。An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked.

針對 extern 運算子, operator_body 只包含分號。For extern operators, the operator_body consists simply of a semicolon. 對於所有其他運算子, operator_body 是區塊主體或運算式主體。For all other operators, the operator_body is either a block body or an expression body.

下列規則適用于所有運算子宣告:The following rules apply to all operator declarations:

  • 運算子宣告必須同時包含 publicstatic 修飾詞。An operator declaration must include both a public and a static modifier.
  • 運算子) (s 參數必須是值參數, (值參數) 。The parameter(s) of an operator must be value parameters (Value parameters). 這是運算子宣告用來指定或參數的編譯時期錯誤 ref outIt is a compile-time error for an operator declaration to specify ref or out parameters.
  • 運算子的簽章 (一元運算子二元運算子轉換運算子) 必須與在相同類別中宣告的所有其他運算子的簽章不同。The signature of an operator (Unary operators, Binary operators, Conversion operators) must differ from the signatures of all other operators declared in the same class.
  • 在運算子宣告中參考的所有型別至少都必須可以如同運算子本身一樣存取 (存取範圍條件約束) 。All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • 在運算子宣告中多次出現相同的修飾詞時,會發生錯誤。It is an error for the same modifier to appear multiple times in an operator declaration.

每個運算子類別都會強加額外的限制,如下列各節所述。Each operator category imposes additional restrictions, as described in the following sections.

和其他成員一樣,衍生類別會繼承基類中宣告的運算子。Like other members, operators declared in a base class are inherited by derived classes. 因為運算子宣告一律需要類別或結構,其中運算子宣告為參與運算子的簽章,所以在衍生類別中宣告的運算子不可能隱藏在基類中宣告的運算子。Because operator declarations always require the class or struct in which the operator is declared to participate in the signature of the operator, it is not possible for an operator declared in a derived class to hide an operator declared in a base class. 因此, new 在運算子宣告中絕對不需要使用修飾詞,因此絕對不會允許。Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

一元和二元運算子的其他資訊可以在 運算子中找到。Additional information on unary and binary operators can be found in Operators.

您可以在 使用者定義的轉換中找到轉換運算子的其他資訊。Additional information on conversion operators can be found in User-defined conversions.

一元運算子Unary operators

下列規則適用于一元運算子聲明,其中 T 代表包含運算子宣告之類別或結構的實例類型:The following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • 一元 +-!~ 運算子必須採用型別或的單一參數 T T? ,而且可以傳回任何型別。A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • 一元 ++ or -- 運算子必須採用型別的單一參數 T T? ,且必須傳回相同類型或衍生自它的型別。A unary ++ or -- operator must take a single parameter of type T or T? and must return that same type or a type derived from it.
  • 一元 true or false 運算子必須採用類型或的單一參數 T T? ,且必須傳回型別 boolA unary true or false operator must take a single parameter of type T or T? and must return type bool.

一元運算子的簽章包含運算子 token (+ 、、 - 、、、、 ! ~ ++ -- true 或) , false 以及單一型式參數的類型。The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. 傳回類型不是一元運算子簽章的一部分,也不是正式參數的名稱。The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

truefalse 一元運算子需要成對的宣告。The true and false unary operators require pair-wise declaration. 如果類別宣告其中一個運算子,但未宣告另一個運算子,就會發生編譯時期錯誤。A compile-time error occurs if a class declares one of these operators without also declaring the other. truefalse 運算子會在使用者定義的條件式邏輯運算子布林運算式中進一步說明。The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

下列範例顯示整數向量類別的實和後續使用方式 operator ++The following example shows an implementation and subsequent usage of operator ++ for an integer vector class:

public class IntVector
{
    public IntVector(int length) {...}

    public int Length {...}                 // read-only property

    public int this[int index] {...}        // read-write indexer

    public static IntVector operator ++(IntVector iv) {
        IntVector temp = new IntVector(iv.Length);
        for (int i = 0; i < iv.Length; i++)
            temp[i] = iv[i] + 1;
        return temp;
    }
}

class Test
{
    static void Main() {
        IntVector iv1 = new IntVector(4);    // vector of 4 x 0
        IntVector iv2;

        iv2 = iv1++;    // iv2 contains 4 x 0, iv1 contains 4 x 1
        iv2 = ++iv1;    // iv2 contains 4 x 2, iv1 contains 4 x 2
    }
}

請注意,運算子方法如何傳回將1加入運算元所產生的值,就像後置遞增和遞減運算子 (後置 遞增和 遞減運算子) ,以及前置遞增和遞減運算子 (前置遞增 和遞減運算子) 。Note how the operator method returns the value produced by adding 1 to the operand, just like the postfix increment and decrement operators (Postfix increment and decrement operators), and the prefix increment and decrement operators (Prefix increment and decrement operators). 與 c + + 不同的是,此方法不需要直接修改其運算元的值。Unlike in C++, this method need not modify the value of its operand directly. 事實上,修改運算元值會違反後置遞增運算子的標準語義。In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

二元運算子Binary operators

下列規則適用于二元運算子宣告,其中 T 代表包含運算子宣告之類別或結構的實例類型:The following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • 二元非移位運算子必須採用兩個參數,至少其中一個必須有型別 TT? ,而且可以傳回任何型別。A binary non-shift operator must take two parameters, at least one of which must have type T or T?, and can return any type.
  • 二元 << or >> 運算子必須採用兩個參數,第一個必須有型別, T 而第二個必須有型別 T? intint? ,而且可以傳回任何型別。A binary << or >> operator must take two parameters, the first of which must have type T or T? and the second of which must have type int or int?, and can return any type.

二元運算子的簽章包含運算子 token (、、、、、、、、、、、、、、 + - * / % & | ^ << >> == != > < >=<=) ,以及兩個型式參數的類型。The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. 傳回型別和型式參數的名稱不是二元運算子之簽章的一部分。The return type and the names of the formal parameters are not part of a binary operator's signature.

某些二元運算子需要成對的宣告。Certain binary operators require pair-wise declaration. 針對成對任一個運算子的每個宣告,必須有配對的另一個運算子相符的宣告。For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. 當兩個運算子宣告具有相同的傳回類型,而且每個參數的類型相同時,就會符合兩個運算子。Two operator declarations match when they have the same return type and the same type for each parameter. 下列運算子需要成對的宣告:The following operators require pair-wise declaration:

  • operator ==operator !=operator == and operator !=
  • operator >operator <operator > and operator <
  • operator >=operator <=operator >= and operator <=

轉換運算子Conversion operators

轉換運算子宣告 (使用者定義的轉換引入 使用者定義的轉換,) 可擴大預先定義的隱含和明確轉換。A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

包含關鍵字的轉換運算子宣告導 implicit 入了使用者定義的隱含轉換。A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. 隱含轉換可能會在各種情況下進行,包括函數成員調用、轉換運算式和指派。Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. 這會在 隱含轉換中進一步說明。This is described further in Implicit conversions.

包含關鍵字的轉換運算子宣告導 explicit 入了使用者定義的明確轉換。A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. 明確轉換可以在 cast 運算式中進行,並在明確的 轉換中進一步說明。Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

轉換運算子會將來源類型(以轉換運算子的參數類型表示)轉換為目標型別,並以轉換運算子的傳回類型來表示。A conversion operator converts from a source type, indicated by the parameter type of the conversion operator, to a target type, indicated by the return type of the conversion operator.

針對指定的來源類型 S 和目標型別 T ,如果 ST 是可為 null 的型別,請讓 S0T0 參考其基礎類型,否則, S0 T0S 分別等於和 TFor a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. S T 只有在下列所有條件都成立時,才允許類別或結構宣告從來源類型轉換成目標型別:A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S0T0 是不同的類型。S0 and T0 are different types.
  • S0T0 為運算子宣告發生所在的類別或結構類型。Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • 和都不 S0 T0interface_typeNeither S0 nor T0 is an interface_type.
  • 排除使用者定義的轉換,轉換不會存在於 S T 或之間 T SExcluding user-defined conversions, a conversion does not exist from S to T or from T to S.

基於這些規則的目的,與或相關聯的任何類型參數 S T 都會被視為與其他類型沒有繼承關聯性的唯一類型,而且會忽略這些型別參數上的任何條件約束。For the purposes of these rules, any type parameters associated with S or T are considered to be unique types that have no inheritance relationship with other types, and any constraints on those type parameters are ignored.

在範例中In the example

class C<T> {...}

class D<T>: C<T>
{
    public static implicit operator C<int>(D<T> value) {...}      // Ok
    public static implicit operator C<string>(D<T> value) {...}   // Ok
    public static implicit operator C<T>(D<T> value) {...}        // Error
}

前兩個運算子宣告是允許的,因為 索引子.3 Tintstring 分別會被視為不具關聯性的唯一類型。the first two operator declarations are permitted because, for the purposes of Indexers.3, T and int and string respectively are considered unique types with no relationship. 不過,第三個運算子是錯誤,因為 C<T> 是的基類 D<T>However, the third operator is an error because C<T> is the base class of D<T>.

從第二個規則開始,轉換運算子必須在宣告運算子的類別或結構類型之間轉換。From the second rule it follows that a conversion operator must convert either to or from the class or struct type in which the operator is declared. 例如,類別或結構類型可以 C 定義從到的轉換 C int int C ,而不 int bool 是從轉換成。For example, it is possible for a class or struct type C to define a conversion from C to int and from int to C, but not from int to bool.

您無法直接重新定義預先定義的轉換。It is not possible to directly redefine a pre-defined conversion. 因此,不允許轉換運算子從或轉換為, object 因為 object 和所有其他類型之間已經存在隱含和明確轉換。Thus, conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types. 同樣地,轉換的來源和目標型別都不能是另一種的基底類型,因為轉換已經存在。Likewise, neither the source nor the target types of a conversion can be a base type of the other, since a conversion would then already exist.

不過,您可以針對特定類型引數,在泛型型別上宣告運算子,以指定已經以預先定義的轉換形式存在的轉換。However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. 在範例中In the example

struct Convertible<T>
{
    public static implicit operator Convertible<T>(T value) {...}
    public static explicit operator T(Convertible<T> value) {...}
}

當類型 object 指定為的型別引數時 T ,第二個運算子會宣告已經存在 (隱含的轉換,因此也會從任何類型轉換為類型 object) 。when type object is specified as a type argument for T, the second operator declares a conversion that already exists (an implicit, and therefore also an explicit, conversion exists from any type to type object).

如果兩個類型之間存在預先定義的轉換,則會忽略這些類型之間的任何使用者定義轉換。In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. 具體來說:Specifically:

  • 如果預先定義的隱含轉換 (從類型到類型的 隱含轉換) 存在 S TS T 則會忽略 (的隱含或明確) 的所有使用者定義轉換。If a pre-defined implicit conversion (Implicit conversions) exists from type S to type T, all user-defined conversions (implicit or explicit) from S to T are ignored.
  • 如果預先定義的明確轉換 (從類型到類型的 明確轉換) 存在 S TS 則會忽略從到的任何使用者定義明確轉換 TIf a pre-defined explicit conversion (Explicit conversions) exists from type S to type T, any user-defined explicit conversions from S to T are ignored. 此外,Furthermore:

如果 T 是介面型別,則會忽略從到的使用者自訂隱含轉換 S TIf T is an interface type, user-defined implicit conversions from S to T are ignored.

否則, S 仍會考慮從到的使用者定義隱含轉換 TOtherwise, user-defined implicit conversions from S to T are still considered.

針對所有類型 object ,但 Convertible<T> 上述型別的運算子不會與預先定義的轉換產生衝突。For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. 例如:For example:

void F(int i, Convertible<int> n) {
    i = n;                          // Error
    i = (int)n;                     // User-defined explicit conversion
    n = i;                          // User-defined implicit conversion
    n = (Convertible<int>)i;        // User-defined implicit conversion
}

不過,針對型別 object ,預先定義的轉換會隱藏所有案例中的使用者定義轉換,但會隱藏一個:However, for type object, pre-defined conversions hide the user-defined conversions in all cases but one:

void F(object o, Convertible<object> n) {
    o = n;                         // Pre-defined boxing conversion
    o = (object)n;                 // Pre-defined boxing conversion
    n = o;                         // User-defined implicit conversion
    n = (Convertible<object>)o;    // Pre-defined unboxing conversion
}

不允許使用者定義的轉換將或轉換成 interface_type s。User-defined conversions are not allowed to convert from or to interface_type s. 尤其是,這項限制可確保在轉換為 interface_type 時,不會發生任何使用者定義的轉換,而且只有在轉換的物件實際上會實作為指定的 interface_type 時,才會將轉換成 interface_type 成功。In particular, this restriction ensures that no user-defined transformations occur when converting to an interface_type, and that a conversion to an interface_type succeeds only if the object being converted actually implements the specified interface_type.

轉換運算子的簽章包含來源類型和目標型別。The signature of a conversion operator consists of the source type and the target type. (請注意,這是傳回型別參與簽章的唯一成員形式。 ) implicit explicit 轉換運算子的或分類不是運算子簽章的一部分。(Note that this is the only form of member for which the return type participates in the signature.) The implicit or explicit classification of a conversion operator is not part of the operator's signature. 因此,類別或結構無法宣告 implicit explicit 具有相同來源和目標型別的和轉換運算子。Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

一般而言,使用者定義的隱含轉換應該設計為永遠不會擲回例外狀況,而且永遠不會遺失資訊。In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. 如果使用者定義的轉換可能會引發例外狀況 (例如,因為來源引數超出範圍) 或遺失資訊 (例如捨棄高序位位) ,則應該將該轉換定義為明確轉換。If a user-defined conversion can give rise to exceptions (for example, because the source argument is out of range) or loss of information (such as discarding high-order bits), then that conversion should be defined as an explicit conversion.

在範例中In the example

using System;

public struct Digit
{
    byte value;

    public Digit(byte value) {
        if (value < 0 || value > 9) throw new ArgumentException();
        this.value = value;
    }

    public static implicit operator byte(Digit d) {
        return d.value;
    }

    public static explicit operator Digit(byte b) {
        return new Digit(b);
    }
}

從轉換 Digitbyte 是隱含的,因為它永遠不會擲回例外狀況或遺失資訊,但是從到的轉換 byte 是明確的,因為只能表示的 Digit Digit 可能值子集 bytethe conversion from Digit to byte is implicit because it never throws exceptions or loses information, but the conversion from byte to Digit is explicit since Digit can only represent a subset of the possible values of a byte.

實例的函式Instance constructors

*實例 的函式 _ 是一個成員,它會執行初始化類別實例所需的動作。An *instance constructor _ is a member that implements the actions required to initialize an instance of a class. 使用 _constructor_declaration * s 宣告實例的函式:Instance constructors are declared using _constructor_declaration*s:

constructor_declaration
    : attributes? constructor_modifier* constructor_declarator constructor_body
    ;

constructor_modifier
    : 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'extern'
    | constructor_modifier_unsafe
    ;

constructor_declarator
    : identifier '(' formal_parameter_list? ')' constructor_initializer?
    ;

constructor_initializer
    : ':' 'base' '(' argument_list? ')'
    | ':' 'this' '(' argument_list? ')'
    ;

constructor_body
    : block
    | ';'
    ;

Constructor_declaration 可能包含一組 屬性 (屬性) 、四個存取修飾詞的有效組合 (存取修飾詞) 和 extern (的 外部方法) 修飾詞。A constructor_declaration may include a set of attributes (Attributes), a valid combination of the four access modifiers (Access modifiers), and an extern (External methods) modifier. 不允許使用多個修飾詞來多次包含相同的修飾詞。A constructor declaration is not permitted to include the same modifier multiple times.

Constructor_declarator識別碼 必須命名宣告實例的函式所在的類別。The identifier of a constructor_declarator must name the class in which the instance constructor is declared. 如果指定了任何其他名稱,則會發生編譯時期錯誤。If any other name is specified, a compile-time error occurs.

實例函式的選擇性 formal_parameter_list 受限於與 (方法) 的方法 formal_parameter_list 相同的規則。The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (Methods). 型式參數清單定義了 (簽章的簽章 多載) 的實例,並控管多載解析 (型別推斷) 在調用中選取特定實例的函式。The formal parameter list defines the signature (Signatures and overloading) of an instance constructor and governs the process whereby overload resolution (Type inference) selects a particular instance constructor in an invocation.

Formal_parameter_list 實例的函式所參考的每個類型,都必須至少可以像是函式本身一樣存取 () 的 存取範圍條件約束Each of the types referenced in the formal_parameter_list of an instance constructor must be at least as accessible as the constructor itself (Accessibility constraints).

選擇性的 constructor_initializer 會在執行這個實例的函式 constructor_body 中指定的語句之前,指定要叫用的另一個實例的函式。The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. 這會在函式 初始化運算式中進一步說明。This is described further in Constructor initializers.

當函式宣告包含修飾詞時 extern ,此函式會被視為 *外部 的函式 _。When a constructor declaration includes an extern modifier, the constructor is said to be an *external constructor _. 因為外部的函式宣告不提供實際的實值,所以其 _constructor_body * 是由分號組成。Because an external constructor declaration provides no actual implementation, its _constructor_body* consists of a semicolon. 若為所有其他的函式, constructor_body 包含指定語句的 區塊 ,以初始化類別的新實例。For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. 這會完全對應至具有傳回 void 類型 (方法主體) 的實例方法區塊。This corresponds exactly to the block of an instance method with a void return type (Method body).

不會繼承實例的函式。Instance constructors are not inherited. 因此,類別沒有任何實例的函式,而不是實際在類別中宣告的。Thus, a class has no instance constructors other than those actually declared in the class. 如果類別不包含任何實例的函式宣告,則預設實例的函式會自動提供 (預設 的函式) 。If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

實例的函式是由 object_creation_expression 的 (物件建立運算式) 和 constructor_initializer 所叫用。Instance constructors are invoked by object_creation_expression s (Object creation expressions) and through constructor_initializer s.

建構函式初始設定式Constructor initializers

除了) 類別以外的所有實例的函式 (之外,也會 object 隱含地在 constructor_body 之前,將另一個實例的函式叫用。All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. 隱含調用的函式取決於 constructor_initializerThe constructor to implicitly invoke is determined by the constructor_initializer:

  • 表單的實例函式初始化運算式, base(argument_list)base() 從直接基類叫用實例的函式。An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. 使用 argument_list (如果有的話)以及多載解析規則的多 載解析規則,便會選取該函式。That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. 這組候選實例的函式是由直接基類中包含的所有可存取的實例的函式所組成,如果沒有任何實例的函式是在直接基類中宣告,則 (預設 的函式) 。The set of candidate instance constructors consists of all accessible instance constructors contained in the direct base class, or the default constructor (Default constructors), if no instance constructors are declared in the direct base class. 如果這個集合是空的,或無法識別單一最佳實例的函式,就會發生編譯時期錯誤。If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • 表單的實例函式初始化運算式, this(argument-list)this() 導致叫用類別本身的實例的函式。An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. 使用 argument_list (如果有的話)以及多載解析規則的多 載解析規則,便會選取此函式。The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. 候選實例的一組函式是由類別本身中宣告的所有可存取的實例函式所組成。The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. 如果這個集合是空的,或無法識別單一最佳實例的函式,就會發生編譯時期錯誤。If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. 如果實例的函式宣告包含叫用函式本身的函式初始化運算式,就會發生編譯時期錯誤。If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

如果實例的函式沒有任何函式初始化運算式,則會隱含提供表單的函式初始化運算式 base()If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. 因此,表單的實例函式宣告Thus, an instance constructor declaration of the form

C(...) {...}

完全等同于is exactly equivalent to

C(...): base() {...}

由實例的函式宣告 formal_parameter_list 指定的參數範圍包含該宣告的函式初始化運算式。The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. 因此,可以使用函式初始化運算式來存取此函式的參數。Thus, a constructor initializer is permitted to access the parameters of the constructor. 例如:For example:

class A
{
    public A(int x, int y) {}
}

class B: A
{
    public B(int x, int y): base(x + y, x - y) {}
}

實例的函式初始化運算式無法存取所建立的實例。An instance constructor initializer cannot access the instance being created. 因此,它是在函式 this 初始化運算式的引數運算式中參考的編譯時期錯誤,如同引數運算式透過 simple_name 參考任何實例成員的編譯時期錯誤一樣。Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple_name.

執行個體變數初始化運算式Instance variable initializers

當實例的函式沒有任何函式初始化運算式,或它具有表單的函式初始化運算式時 base(...) ,該函式會隱含地執行其類別中所宣告之實例欄位的 variable_initializer 所指定的初始化。When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable_initializer s of the instance fields declared in its class. 這會對應到在進入函式時,以及直接基類的隱含調用之前,立即執行的一系列指派。This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. 變數初始化運算式會以它們出現在類別宣告中的文字順序來執行。The variable initializers are executed in the textual order in which they appear in the class declaration.

執行的函式Constructor execution

變數初始化運算式會轉換成指派語句,而這些指派語句會在基類實例的調用之前執行。Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. 這種排序可確保在執行具有該實例存取權的任何語句之前,所有實例欄位都是由其變數初始化運算式初始化。This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

假設範例Given the example

using System;

class A
{
    public A() {
        PrintFields();
    }

    public virtual void PrintFields() {}
}

class B: A
{
    int x = 1;
    int y;

    public B() {
        y = -1;
    }

    public override void PrintFields() {
        Console.WriteLine("x = {0}, y = {1}", x, y);
    }
}

new B() 用來建立的實例時 B ,會產生下列輸出:when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

的值 x 是1,因為在叫用基類實例的函式之前,會先執行變數初始化運算式。The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. 不過,的值 y 為 0 (預設值 int) ,因為在基類的函式傳回 y 之前,不會執行的指派。However, the value of y is 0 (the default value of an int) because the assignment to y is not executed until after the base class constructor returns.

將執行個體變數初始化運算式和函式初始化運算式視為在 constructor_body 之前自動插入的語句,會很有用。It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. 範例The example

using System;
using System.Collections;

class A
{
    int x = 1, y = -1, count;

    public A() {
        count = 0;
    }

    public A(int n) {
        count = n;
    }
}

class B: A
{
    double sqrt2 = Math.Sqrt(2.0);
    ArrayList items = new ArrayList(100);
    int max;

    public B(): this(100) {
        items.Add("default");
    }

    public B(int n): base(n - 1) {
        max = n;
    }
}

包含數個變數初始化運算式;它也包含 forms (和) 的函式初始化運算式 base thiscontains several variable initializers; it also contains constructor initializers of both forms (base and this). 此範例對應到如下所示的程式碼,其中每個批註指出自動插入的語句 (自動插入的函式調用所用的語法無效,但只是為了說明) 的機制。The example corresponds to the code shown below, where each comment indicates an automatically inserted statement (the syntax used for the automatically inserted constructor invocations isn't valid, but merely serves to illustrate the mechanism).

using System.Collections;

class A
{
    int x, y, count;

    public A() {
        x = 1;                       // Variable initializer
        y = -1;                      // Variable initializer
        object();                    // Invoke object() constructor
        count = 0;
    }

    public A(int n) {
        x = 1;                       // Variable initializer
        y = -1;                      // Variable initializer
        object();                    // Invoke object() constructor
        count = n;
    }
}

class B: A
{
    double sqrt2;
    ArrayList items;
    int max;

    public B(): this(100) {
        B(100);                      // Invoke B(int) constructor
        items.Add("default");
    }

    public B(int n): base(n - 1) {
        sqrt2 = Math.Sqrt(2.0);      // Variable initializer
        items = new ArrayList(100);  // Variable initializer
        A(n - 1);                    // Invoke A(int) constructor
        max = n;
    }
}

預設建構函式Default constructors

如果類別不包含任何實例的函式宣告,則會自動提供預設實例的函式。If a class contains no instance constructor declarations, a default instance constructor is automatically provided. 該預設的函式只會叫用直接基類的無參數函式。That default constructor simply invokes the parameterless constructor of the direct base class. 如果類別為抽象,則預設的函式的宣告存取範圍會受到保護。If the class is abstract then the declared accessibility for the default constructor is protected. 否則,預設函式的宣告存取範圍是公用的。Otherwise, the declared accessibility for the default constructor is public. 因此,預設的函式一律為表單Thus, the default constructor is always of the form

protected C(): base() {}

or

public C(): base() {}

其中 C 是類別的名稱。where C is the name of the class. 如果多載解析無法判斷基類的函式初始化運算式的唯一最佳候選項,就會發生編譯時期錯誤。If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

在範例中In the example

class Message
{
    object sender;
    string text;
}

因為類別不包含任何實例的函式宣告,所以會提供預設的函式。a default constructor is provided because the class contains no instance constructor declarations. 因此,此範例完全等同于Thus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

私用函式Private constructors

當類別 T 只宣告私用實例的函式時,的程式文字以外的類別就不可能 T T 直接建立的實例 TWhen a class T declares only private instance constructors, it is not possible for classes outside the program text of T to derive from T or to directly create instances of T. 因此,如果類別只包含靜態成員,而不想要具現化,則加入空的私用實例的函式會防止具現化。Thus, if a class contains only static members and isn't intended to be instantiated, adding an empty private instance constructor will prevent instantiation. 例如:For example:

public class Trig
{
    private Trig() {}        // Prevent instantiation

    public const double PI = 3.14159265358979323846;

    public static double Sin(double x) {...}
    public static double Cos(double x) {...}
    public static double Tan(double x) {...}
}

類別會將 Trig 相關的方法和常數群組在一起,但不應該具現化。The Trig class groups related methods and constants, but is not intended to be instantiated. 因此,它會宣告一個空的私用實例的函式。Therefore it declares a single empty private instance constructor. 至少必須宣告一個實例的函式,以隱藏自動產生預設的函式。At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

選擇性實例的函式參數Optional instance constructor parameters

「函式 this(...) 初始化運算式」的形式通常會與多載搭配使用,以實作為選擇性的實例處理常式參數。The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. 在範例中In the example

class Text
{
    public Text(): this(0, 0, null) {}

    public Text(int x, int y): this(x, y, null) {}

    public Text(int x, int y, string s) {
        // Actual constructor implementation
    }
}

前兩個實例的函式只會提供遺漏引數的預設值。the first two instance constructors merely provide the default values for the missing arguments. 這兩者都會使用函式 this(...) 初始化運算式來叫用第三個實例的函式,這實際上會執行初始化新實例的工作。Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. 這是選擇性的函式參數的效果:The effect is that of optional constructor parameters:

Text t1 = new Text();                    // Same as Text(0, 0, null)
Text t2 = new Text(5, 10);               // Same as Text(5, 10, null)
Text t3 = new Text(5, 20, "Hello");

靜態建構函式Static constructors

*靜態 的函式 _ 是一個成員,它會執行初始化封閉類別型別所需的動作。A *static constructor _ is a member that implements the actions required to initialize a closed class type. 靜態的函式是使用 _static_constructor_declaration * s 來宣告:Static constructors are declared using _static_constructor_declaration*s:

static_constructor_declaration
    : attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body
    ;

static_constructor_modifiers
    : 'extern'? 'static'
    | 'static' 'extern'?
    | static_constructor_modifiers_unsafe
    ;

static_constructor_body
    : block
    | ';'
    ;

Static_constructor_declaration 可能包含一組 屬性 (屬性) 和 extern (外部方法) 的修飾詞。A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

Static_constructor_declaration識別碼 必須為宣告靜態函式的類別命名。The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. 如果指定了任何其他名稱,則會發生編譯時期錯誤。If any other name is specified, a compile-time error occurs.

當靜態的函式宣告包含 extern 修飾詞時,靜態的函式稱為「外部靜態 函式」。When a static constructor declaration includes an extern modifier, the static constructor is said to be an *external static constructor _. 因為外部靜態的函式宣告未提供實際的實值,所以其 _static_constructor_body * 是由分號組成。Because an external static constructor declaration provides no actual implementation, its _static_constructor_body* consists of a semicolon. 對於所有其他靜態的函式宣告, static_constructor_body 是由 區塊 所組成,這些區塊會指定要執行的語句,以便初始化類別。For all other static constructor declarations, the static_constructor_body consists of a block which specifies the statements to execute in order to initialize the class. 這會完全對應至靜態方法的 method_body ,其傳回 void 型別 (方法主體) 。This corresponds exactly to the method_body of a static method with a void return type (Method body).

靜態的函式不會被繼承,也不能直接呼叫。Static constructors are not inherited, and cannot be called directly.

封閉式類別型別的靜態函式最多隻會在指定的應用程式域中執行一次。The static constructor for a closed class type executes at most once in a given application domain. 靜態函式的執行會由下列事件中的第一個事件所觸發,以在應用程式域內發生:The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • 建立類別型別的實例。An instance of the class type is created.
  • 系統會參考類別型別的任何靜態成員。Any of the static members of the class type are referenced.

如果類別包含 Main (應用程式啟動) 的方法,在此情況下會開始執行,該類別的靜態函式 Main 會在呼叫方法之前執行。If a class contains the Main method (Application Startup) in which execution begins, the static constructor for that class executes before the Main method is called.

若要初始化新的封閉類別型別,請先建立一組新的靜態欄位, (靜態和實例欄位 ,) 為該特定的封閉型別建立。To initialize a new closed class type, first a new set of static fields (Static and instance fields) for that particular closed type is created. 每個靜態欄位都會初始化為預設值 (預設值) 。Each of the static fields is initialized to its default value (Default values). 接下來,會針對這些靜態欄位執行靜態欄位初始化運算式 (靜態欄位初始化) 。Next, the static field initializers (Static field initialization) are executed for those static fields. 最後,會執行靜態的函式。Finally, the static constructor is executed.

範例The example

using System;

class Test
{
    static void Main() {
        A.F();
        B.F();
    }
}

class A
{
    static A() {
        Console.WriteLine("Init A");
    }
    public static void F() {
        Console.WriteLine("A.F");
    }
}

class B
{
    static B() {
        Console.WriteLine("Init B");
    }
    public static void F() {
        Console.WriteLine("B.F");
    }
}

必須產生輸出:must produce the output:

Init A
A.F
Init B
B.F

由於的 A 呼叫會觸發的靜態函式執行,因此的 A.F B 呼叫會觸發的執行靜態函式 B.Fbecause the execution of A's static constructor is triggered by the call to A.F, and the execution of B's static constructor is triggered by the call to B.F.

您可以建立迴圈相依性,以允許在其預設值狀態中觀察到變數初始化運算式的靜態欄位。It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

範例The example

using System;

class A
{
    public static int X;

    static A() {
        X = B.Y + 1;
    }
}

class B
{
    public static int Y = A.X + 1;

    static B() {}

    static void Main() {
        Console.WriteLine("X = {0}, Y = {1}", A.X, B.Y);
    }
}

產生下列輸出produces the output

X = 1, Y = 2

若要執行 Main 方法,系統會先在類別的靜態函式之前,先執行的初始化運算式 B.Y BTo execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Y的初始化運算式會導致 A 執行的靜態函式,因為參考的值 A.XY's initializer causes A's static constructor to be run because the value of A.X is referenced. 的靜態函式接著會  A 繼續計算的值  X ,而在執行此動作時,會提取的預設值  Y ,也就是零。The static constructor of A in turn proceeds to compute the value of X, and in doing so fetches the default value of Y, which is zero. A.X 因此會初始化為1。A.X is thus initialized to 1. 執行的 A 靜態欄位初始化運算式和靜態函式的程式接著會完成,並傳回的初始值計算結果為  Y 2。The process of running A's static field initializers and static constructor then completes, returning to the calculation of the initial value of Y, the result of which becomes 2.

由於靜態的函式只會針對每個封閉的結構化類別類型執行一次,因此,在無法透過條件約束 (類型參數條件約束) 的條件約束的類型參數上強制執行執行時間檢查是很方便的位置。Because the static constructor is executed exactly once for each closed constructed class type, it is a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile-time via constraints (Type parameter constraints). 例如,下列型別會使用靜態的函式來強制型別引數為列舉:For example, the following type uses a static constructor to enforce that the type argument is an enum:

class Gen<T> where T: struct
{
    static Gen() {
        if (!typeof(T).IsEnum) {
            throw new ArgumentException("T must be an enum");
        }
    }
}

解構函式Destructors

* 「 式」是一個成員,它會執行銷毀類別實例所需的動作。A *destructor _ is a member that implements the actions required to destruct an instance of a class. 使用 _destructor_declaration * 來宣告函式:A destructor is declared using a _destructor_declaration*:

destructor_declaration
    : attributes? 'extern'? '~' identifier '(' ')' destructor_body
    | destructor_declaration_unsafe
    ;

destructor_body
    : block
    | ';'
    ;

Destructor_declaration 可能包含一組 屬性 (屬性) 。A destructor_declaration may include a set of attributes (Attributes).

Destructor_declaration識別碼 必須為宣告了此函式的類別命名。The identifier of a destructor_declaration must name the class in which the destructor is declared. 如果指定了任何其他名稱,則會發生編譯時期錯誤。If any other name is specified, a compile-time error occurs.

當「函式」宣告包含修飾詞時 extern ,此函式稱為「外部的函 式」。When a destructor declaration includes an extern modifier, the destructor is said to be an *external destructor _. 因為外部的函式宣告不提供實際的實值,所以其 _destructor_body * 包含分號。Because an external destructor declaration provides no actual implementation, its _destructor_body* consists of a semicolon. 對於所有其他的析構函數, destructor_body 是由 區塊 所組成,這些區塊會指定要執行的語句,以便銷毀類別的實例。For all other destructors, the destructor_body consists of a block which specifies the statements to execute in order to destruct an instance of the class. Destructor_body 完全對應至實例方法的 method_body ,其傳回型別 void (方法主體) 。A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

不會繼承析構函數。Destructors are not inherited. 因此,類別沒有任何不同的析構函數可以在該類別中宣告。Thus, a class has no destructors other than the one which may be declared in that class.

由於必須有一個不含參數的自函式,所以不能多載,所以一個類別最多隻能有一個函式。Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

系統會自動叫用析構函數,而且無法明確地叫用。Destructors are invoked automatically, and cannot be invoked explicitly. 當任何程式碼無法再使用該實例時,實例就會變成可進行銷毀的條件。An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. 實例的函式的執行時間可能會在實例變成可供回收之後的任何時間執行。Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. 解構實例時,該實例繼承鏈中的析構函數會依序從大部分衍生到最不衍生的順序呼叫。When an instance is destructed, the destructors in that instance's inheritance chain are called, in order, from most derived to least derived. 可以在任何執行緒上執行一個可執行檔函式。A destructor may be executed on any thread. 如需管理如何執行和如何執行函式之規則的進一步討論,請參閱 自動記憶體管理For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

範例的輸出The output of the example

using System;

class A
{
    ~A() {
        Console.WriteLine("A's destructor");
    }
}

class B: A
{
    ~B() {
        Console.WriteLine("B's destructor");
    }
}

class Test
{
   static void Main() {
        B b = new B();
        b = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
   }
}

isis

B's destructor
A's destructor

由於繼承鏈中的析構函數會依序呼叫,從大部分衍生到最不衍生。since destructors in an inheritance chain are called in order, from most derived to least derived.

藉由覆寫上的虛擬方法來實作為析構函數 Finalize System.ObjectDestructors are implemented by overriding the virtual method Finalize on System.Object. C # 程式不允許覆寫這個方法,或將它 (或直接覆寫) 。C# programs are not permitted to override this method or call it (or overrides of it) directly. 例如,程式For instance, the program

class A 
{
    override protected void Finalize() {}    // error

    public void F() {
        this.Finalize();                     // error
    }
}

包含兩個錯誤。contains two errors.

編譯器的行為就像這個方法和覆寫完全不存在。The compiler behaves as if this method, and overrides of it, do not exist at all. 因此,這個程式:Thus, this program:

class A 
{
    void Finalize() {}                            // permitted
}

有效,且顯示的方法會隱藏 System.ObjectFinalize 方法。is valid, and the method shown hides System.Object's Finalize method.

如需從函式擲回例外狀況時的行為討論,請參閱 例外狀況的處理方式For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

迭代器Iterators

函數成員 (函式 成員) 使用反覆運算器 (區塊來執行 ,) 稱為反覆運算器A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

只要對應函式成員的傳回型別是 (列舉值介面 的其中一個列舉值介面,) 或 (可列舉 介面) 的其中一個可列舉介面,iterator 區塊就可以當做函數成員的主體。An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces (Enumerator interfaces) or one of the enumerable interfaces (Enumerable interfaces). 它可能會做為 method_bodyoperator_bodyaccessor_body,而事件、實例的函式、靜態的函式和析構函數則無法實作為反覆運算器。It can occur as a method_body, operator_body or accessor_body, whereas events, instance constructors, static constructors and destructors cannot be implemented as iterators.

當函數成員是使用 iterator 區塊來執行時,函式成員的型式參數清單會有編譯時期錯誤,以指定任何 refout 參數。When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any ref or out parameters.

列舉值介面Enumerator interfaces

列舉值介面 為非泛型介面 System.Collections.IEnumerator ,以及泛型介面的所有具現化 System.Collections.Generic.IEnumerator<T>The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. 為了簡潔起見,在本章中,這些介面會分別參考為 IEnumeratorIEnumerator<T>For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

可列舉介面Enumerable interfaces

列舉的介面為 非泛型介面 System.Collections.IEnumerable ,以及泛型介面的所有具現化 System.Collections.Generic.IEnumerable<T>The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. 為了簡潔起見,在本章中,這些介面會分別參考為 IEnumerableIEnumerable<T>For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Yield 類型Yield type

反覆運算器會產生一系列的值,全都是相同的型別。An iterator produces a sequence of values, all of the same type. 這個型別稱為反覆運算器的 yield 型 別。This type is called the yield type of the iterator.

  • 傳回或的反覆運算器的 yield 型 IEnumeratorIEnumerable objectThe yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • 傳回或的反覆運算器的 yield 型 IEnumerator<T>IEnumerable<T> TThe yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

列舉值物件Enumerator objects

當函式成員傳回列舉值介面型別時,若函式成員是使用 iterator 區塊來執行,則叫用函數成員不會立即在 iterator 區塊中執行程式碼。When a function member returning an enumerator interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. 相反地,會建立並傳回 列舉值物件Instead, an enumerator object is created and returned. 這個物件會封裝 iterator 區塊中指定的程式碼,並在叫用列舉值物件的方法時,執行 iterator 區塊中的程式碼 MoveNextThis object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object's MoveNext method is invoked. 列舉值物件具有下列特性:An enumerator object has the following characteristics:

  • 它會實 IEnumeratorIEnumerator<T> ,其中 T 是反覆運算器的 yield 型別。It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • 它會實作 System.IDisposableIt implements System.IDisposable.
  • 如果傳遞給函式成員的任何) 和實例值 (,則會使用引數值的複本進行初始化。It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • 它有四個可能的狀態(*在 _ 之前*執行中、**暫停**之後*),且一開始是在 _ 之前 的 * 狀態。It has four potential states, before _, _running_, _suspended_, and _after_, and is initially in the _ before state.

列舉值物件通常是編譯器產生的列舉值類別實例,此類別會封裝 iterator 區塊中的程式碼並實作為列舉值介面,但可以執行其他方法。An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible. 如果列舉值類別是由編譯器產生,該類別將會在包含函式成員的類別中直接或間接地嵌套或間接地進行嵌套,而且會有一個為編譯器使用 (識別碼) 的名稱。If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (Identifiers).

列舉值物件可以執行比上述指定更多的介面。An enumerator object may implement more interfaces than those specified above.

下列各節描述 MoveNext Current 列舉值物件所提供的、和 Dispose 介面之成員 IEnumerable 的確切行為 IEnumerable<T>The following sections describe the exact behavior of the MoveNext, Current, and Dispose members of the IEnumerable and IEnumerable<T> interface implementations provided by an enumerator object.

請注意,列舉值物件不支援 IEnumerator.Reset 方法。Note that enumerator objects do not support the IEnumerator.Reset method. 叫用這個方法會導致擲回 System.NotSupportedExceptionInvoking this method causes a System.NotSupportedException to be thrown.

MoveNext 方法The MoveNext method

MoveNext列舉值物件的方法會封裝 iterator 區塊的程式碼。The MoveNext method of an enumerator object encapsulates the code of an iterator block. MoveNext叫用方法會在 iterator 區塊中執行程式碼,並適當地設定 Current 列舉值物件的屬性。Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. 執行的精確動作 MoveNext 取決於叫用時列舉值物件的狀態 MoveNextThe precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • 如果列舉值物件的狀態 于,則叫用 MoveNextIf the state of the enumerator object is before, invoking MoveNext:
    • 將狀態變更為 [ 正在 執行]。Changes the state to running.
    • 初始化參數 (包括 this 反覆運算器區塊) 的引數值,以及初始化列舉值物件時所儲存的實例值。Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • 從開始執行反覆運算器區塊,直到執行中斷 (,如下) 所述。Executes the iterator block from the beginning until execution is interrupted (as described below).
  • 如果列舉值物件的狀態為執行 中, 則不會指定叫用的結果 MoveNextIf the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • 如果列舉值物件的狀態已 暫停,則叫用 MoveNextIf the state of the enumerator object is suspended, invoking MoveNext:
    • 將狀態變更為 [ 正在 執行]。Changes the state to running.
    • 還原所有區域變數和參數的值 (包括此) 到上次暫停執行 iterator 區塊時所儲存的值。Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. 請注意,這些變數所參考之任何物件的內容在上一次呼叫 MoveNext 之後可能已經變更。Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • 在導致執行暫止的語句之後繼續執行 iterator 區塊 yield return ,然後繼續執行,直到執行中斷為止 (如以下) 所述。Resumes execution of the iterator block immediately following the yield return statement that caused the suspension of execution and continues until execution is interrupted (as described below).
  • 如果列舉值物件的狀態為 after,則叫用會傳回 MoveNext falseIf the state of the enumerator object is after, invoking MoveNext returns false.

MoveNext 執行 iterator 區塊時,執行可能會以四種方式中斷: yield return 語句、語句、 yield break 遇到 iterator 區塊的結尾,以及擲回和傳播至 iterator 區塊的例外狀況。When MoveNext executes the iterator block, execution can be interrupted in four ways: By a yield return statement, by a yield break statement, by encountering the end of the iterator block, and by an exception being thrown and propagated out of the iterator block.

  • yield return (yield 語句 時遇到語句) :When a yield return statement is encountered (The yield statement):
    • 語句中指定的運算式會進行評估、隱含轉換成 yield 型別,然後指派給 Current 列舉值物件的屬性。The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • 反覆運算器主體的執行已暫止。Execution of the iterator body is suspended. 所有本機變數和參數的值(包括 this) )都會儲存 (,就像這個語句的位置一樣 yield returnThe values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. 如果 yield return 語句在一或多個 try 區塊中, finally 就不會在此時間執行相關聯的區塊。If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • 列舉值物件的狀態會變更為 [已 暫停]。The state of the enumerator object is changed to suspended.
    • MoveNext方法 true 會傳回給它的呼叫者,表示反復專案已成功前進到下一個值。The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • yield break (yield 語句 時遇到語句) :When a yield break statement is encountered (The yield statement):
    • 如果 yield break 語句在一或多個 try 區塊中,則會執行相關聯的 finally 區塊。If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • 列舉值物件的狀態會變更為 afterThe state of the enumerator object is changed to after.
    • MoveNext方法 false 會傳回其呼叫端,表示反復專案已完成。The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • 遇到反覆運算器主體的結尾時:When the end of the iterator body is encountered:
    • 列舉值物件的狀態會變更為 afterThe state of the enumerator object is changed to after.
    • MoveNext方法 false 會傳回其呼叫端,表示反復專案已完成。The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • 當擲回例外狀況並傳播至 iterator 區塊時:When an exception is thrown and propagated out of the iterator block:
    • finally例外狀況傳播將會執行反覆運算器主體中的適當區塊。Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • 列舉值物件的狀態會變更為 afterThe state of the enumerator object is changed to after.
    • 例外狀況傳播會繼續至方法的呼叫端 MoveNextThe exception propagation continues to the caller of the MoveNext method.

目前的屬性The Current property

列舉值物件的 Current 屬性會受到 yield return iterator 區塊中的語句影響。An enumerator object's Current property is affected by yield return statements in the iterator block.

當列舉值物件處於 *擱置 _ 狀態時,的值 Current 就是上一次呼叫所設定的值 MoveNextWhen an enumerator object is in the *suspended _ state, the value of Current is the value set by the previous call to MoveNext. 當列舉值物件是在 [狀態] 之後的 [ *之前*]、[ *執行中]* 或 [_]*之後*,未指定存取結果 CurrentWhen an enumerator object is in the before, running, or _ after* states, the result of accessing Current is unspecified.

如果反覆運算器的 yield 型別不是 object ,則透過列舉值物件的實作為存取的結果,會 Current IEnumerable 對應至 Current 透過列舉值物件的實作為存取, IEnumerator<T> 並將結果轉換為 objectFor an iterator with a yield type other than object, the result of accessing Current through the enumerator object's IEnumerable implementation corresponds to accessing Current through the enumerator object's IEnumerator<T> implementation and casting the result to object.

Dispose 方法The Dispose method

Dispose方法是用來清除反復專案,方法是將列舉值物件帶到 after 狀態。The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • 如果列舉值物件的狀態為 *之前 的 *,則叫用會將 Dispose 狀態變更為 _ after *。If the state of the enumerator object is before _, invoking Dispose changes the state to _after**.
  • 如果列舉值物件的狀態為執行 中, 則不會指定叫用的結果 DisposeIf the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • 如果列舉值物件的狀態已 暫停,則叫用 DisposeIf the state of the enumerator object is suspended, invoking Dispose:
    • 將狀態變更為 [ 正在 執行]。Changes the state to running.
    • 執行任何 finally 區塊,如同最後執行的 yield return 語句是 yield break 語句。Executes any finally blocks as if the last executed yield return statement were a yield break statement. 如果這會導致擲回例外狀況,並將其傳播至反覆運算器主體,則列舉值物件的狀態會設定為 after ,而例外狀況會傳播至方法的呼叫端 DisposeIf this causes an exception to be thrown and propagated out of the iterator body, the state of the enumerator object is set to after and the exception is propagated to the caller of the Dispose method.
    • 將狀態變更為 afterChanges the state to after.
  • 如果列舉值物件的狀態為 after,則叫用 Dispose 不會有任何影響。If the state of the enumerator object is after, invoking Dispose has no affect.

可列舉物件Enumerable objects

當傳回可列舉介面類別型的函式成員使用 iterator 區塊來執行時,叫用函數成員不會立即在 iterator 區塊中執行程式碼。When a function member returning an enumerable interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. 相反地,會建立並傳回可列舉 物件Instead, an enumerable object is created and returned. 可列舉物件的 GetEnumerator 方法會傳回列舉值物件,此物件會封裝 iterator 區塊中指定的程式碼,並在叫用列舉值物件的方法時,執行 iterator 區塊中的程式碼 MoveNextThe enumerable object's GetEnumerator method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object's MoveNext method is invoked. 可列舉物件具有下列特性:An enumerable object has the following characteristics:

  • 它會實 IEnumerableIEnumerable<T> ,其中 T 是反覆運算器的 yield 型別。It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • 如果傳遞給函式成員的任何) 和實例值 (,則會使用引數值的複本進行初始化。It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

可列舉物件通常是編譯器產生之可列舉類別的實例,此類別會封裝 iterator 區塊中的程式碼並執行可列舉的介面,但可以執行其他方法。An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the code in the iterator block and implements the enumerable interfaces, but other methods of implementation are possible. 如果可列舉類別是由編譯器所產生,該類別將會在包含函式成員的類別中直接或間接地嵌套或間接地進行嵌套,而且它將會有可供編譯器使用 (識別碼) 的名稱。If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (Identifiers).

可列舉物件所執行的介面,可能會比上述指定的介面更多。An enumerable object may implement more interfaces than those specified above. 尤其是可列舉的物件也可以 IEnumerator IEnumerator<T> 實作為和,讓它同時作為可列舉的和列舉值。In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. 在該類型的實中,第一次叫用可列舉物件的 GetEnumerator 方法時,會傳回可列舉的物件本身。In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. 如果有可列舉物件的後續調用,則會傳回可列舉 GetEnumerator 物件的複本(如果有的話)。Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. 因此,每個傳回的列舉值都有自己的狀態,而一個列舉值的變更不會影響另一個列舉值。Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

GetEnumerator 方法The GetEnumerator method

可列舉物件提供 GetEnumerator 和介面之方法的執行 IEnumerable IEnumerable<T>An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. 這兩個 GetEnumerator 方法共用一個可取得並傳回可用列舉值物件的常見實值。The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. 列舉值物件是使用已初始化可列舉物件時儲存的引數值和實例值來初始化,否則列舉值物件會如 列舉值物件中所述。The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in Enumerator objects.

實作範例Implementation example

本節將描述標準 c # 結構的反覆運算器可能執行。This section describes a possible implementation of iterators in terms of standard C# constructs. 此處所述的執行是以 Microsoft c # 編譯器所使用的相同原則為基礎,但它並不是指強制執行或唯一可行的原則。The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation or the only one possible.

下列 Stack<T> 類別會 GetEnumerator 使用反覆運算器來執行其方法。The following Stack<T> class implements its GetEnumerator method using an iterator. 反覆運算器會以上到下的順序列舉堆疊的元素。The iterator enumerates the elements of the stack in top to bottom order.

using System;
using System.Collections;
using System.Collections.Generic;

class Stack<T>: IEnumerable<T>
{
    T[] items;
    int count;

    public void Push(T item) {
        if (items == null) {
            items = new T[4];
        }
        else if (items.Length == count) {
            T[] newItems = new T[count * 2];
            Array.Copy(items, 0, newItems, 0, count);
            items = newItems;
        }
        items[count++] = item;
    }

    public T Pop() {
        T result = items[--count];
        items[count] = default(T);
        return result;
    }

    public IEnumerator<T> GetEnumerator() {
        for (int i = count - 1; i >= 0; --i) yield return items[i];
    }
}

GetEnumerator方法可以轉譯成編譯器產生的列舉值類別的具現化,該類別會封裝 iterator 區塊中的程式碼,如下所示。The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following.

class Stack<T>: IEnumerable<T>
{
    ...

    public IEnumerator<T> GetEnumerator() {
        return new __Enumerator1(this);
    }

    class __Enumerator1: IEnumerator<T>, IEnumerator
    {
        int __state;
        T __current;
        Stack<T> __this;
        int i;

        public __Enumerator1(Stack<T> __this) {
            this.__this = __this;
        }

        public T Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            switch (__state) {
                case 1: goto __state1;
                case 2: goto __state2;
            }
            i = __this.count - 1;
        __loop:
            if (i < 0) goto __state2;
            __current = __this.items[i];
            __state = 1;
            return true;
        __state1:
            --i;
            goto __loop;
        __state2:
            __state = 2;
            return false;
        }

        public void Dispose() {
            __state = 2;
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

在上述轉譯中,iterator 區塊中的程式碼會轉換成狀態機器,並放入 MoveNext 列舉值類別的方法中。In the preceding translation, the code in the iterator block is turned into a state machine and placed in the MoveNext method of the enumerator class. 此外,區域變數會轉換 i 成列舉值物件中的欄位,讓它可以在的調用之間繼續存在 MoveNextFurthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext.

下列範例會列印整數1到10的簡單乘法資料表。The following example prints a simple multiplication table of the integers 1 through 10. FromTo範例中的方法會傳回可列舉的物件,並使用反覆運算器來執行。The FromTo method in the example returns an enumerable object and is implemented using an iterator.

using System;
using System.Collections.Generic;

class Test
{
    static IEnumerable<int> FromTo(int from, int to) {
        while (from <= to) yield return from++;
    }

    static void Main() {
        IEnumerable<int> e = FromTo(1, 10);
        foreach (int x in e) {
            foreach (int y in e) {
                Console.Write("{0,3} ", x * y);
            }
            Console.WriteLine();
        }
    }
}

FromTo方法可以轉譯成編譯器產生的可列舉類別的具現化,該類別會封裝 iterator 區塊中的程式碼,如下所示。The FromTo method can be translated into an instantiation of a compiler-generated enumerable class that encapsulates the code in the iterator block, as shown in the following.

using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;

class Test
{
    ...

    static IEnumerable<int> FromTo(int from, int to) {
        return new __Enumerable1(from, to);
    }

    class __Enumerable1:
        IEnumerable<int>, IEnumerable,
        IEnumerator<int>, IEnumerator
    {
        int __state;
        int __current;
        int __from;
        int from;
        int to;
        int i;

        public __Enumerable1(int __from, int to) {
            this.__from = __from;
            this.to = to;
        }

        public IEnumerator<int> GetEnumerator() {
            __Enumerable1 result = this;
            if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) {
                result = new __Enumerable1(__from, to);
                result.__state = 1;
            }
            result.from = result.__from;
            return result;
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return (IEnumerator)GetEnumerator();
        }

        public int Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            switch (__state) {
            case 1:
                if (from > to) goto case 2;
                __current = from++;
                __state = 1;
                return true;
            case 2:
                __state = 2;
                return false;
            default:
                throw new InvalidOperationException();
            }
        }

        public void Dispose() {
            __state = 2;
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

可列舉的類別會同時執行可列舉的介面和列舉值介面,讓它同時作為可列舉的和列舉值。The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. 第一次叫用 GetEnumerator 方法時,會傳回可列舉的物件本身。The first time the GetEnumerator method is invoked, the enumerable object itself is returned. 如果有可列舉物件的後續調用,則會傳回可列舉 GetEnumerator 物件的複本(如果有的話)。Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. 因此,每個傳回的列舉值都有自己的狀態,而一個列舉值的變更不會影響另一個列舉值。Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. Interlocked.CompareExchange方法是用來確保安全線程作業。The Interlocked.CompareExchange method is used to ensure thread-safe operation.

fromto 參數會轉換成可列舉類別中的欄位。The from and to parameters are turned into fields in the enumerable class. 由於 from 已在 iterator 區塊中進行修改,因此引進了一個額外的 __from 欄位來保存 from 每個列舉值中提供的初始值。Because from is modified in the iterator block, an additional __from field is introduced to hold the initial value given to from in each enumerator.

MoveNext InvalidOperationException 當為時,方法會擲回 __state 0The MoveNext method throws an InvalidOperationException if it is called when __state is 0. 這可防止在未先呼叫的情況下,使用可列舉物件作為列舉值物件 GetEnumeratorThis protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

下列範例顯示簡單的樹狀目錄類別。The following example shows a simple tree class. Tree<T>類別會 GetEnumerator 使用反覆運算器來執行其方法。The Tree<T> class implements its GetEnumerator method using an iterator. 反覆運算器會以中置順序列舉樹狀結構的元素。The iterator enumerates the elements of the tree in infix order.

using System;
using System.Collections.Generic;

class Tree<T>: IEnumerable<T>
{
    T value;
    Tree<T> left;
    Tree<T> right;

    public Tree(T value, Tree<T> left, Tree<T> right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }

    public IEnumerator<T> GetEnumerator() {
        if (left != null) foreach (T x in left) yield x;
        yield value;
        if (right != null) foreach (T x in right) yield x;
    }
}

class Program
{
    static Tree<T> MakeTree<T>(T[] items, int left, int right) {
        if (left > right) return null;
        int i = (left + right) / 2;
        return new Tree<T>(items[i], 
            MakeTree(items, left, i - 1),
            MakeTree(items, i + 1, right));
    }

    static Tree<T> MakeTree<T>(params T[] items) {
        return MakeTree(items, 0, items.Length - 1);
    }

    // The output of the program is:
    // 1 2 3 4 5 6 7 8 9
    // Mon Tue Wed Thu Fri Sat Sun

    static void Main() {
        Tree<int> ints = MakeTree(1, 2, 3, 4, 5, 6, 7, 8, 9);
        foreach (int i in ints) Console.Write("{0} ", i);
        Console.WriteLine();

        Tree<string> strings = MakeTree(
            "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun");
        foreach (string s in strings) Console.Write("{0} ", s);
        Console.WriteLine();
    }
}

GetEnumerator方法可以轉譯成編譯器產生的列舉值類別的具現化,該類別會封裝 iterator 區塊中的程式碼,如下所示。The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following.

class Tree<T>: IEnumerable<T>
{
    ...

    public IEnumerator<T> GetEnumerator() {
        return new __Enumerator1(this);
    }

    class __Enumerator1 : IEnumerator<T>, IEnumerator
    {
        Node<T> __this;
        IEnumerator<T> __left, __right;
        int __state;
        T __current;

        public __Enumerator1(Node<T> __this) {
            this.__this = __this;
        }

        public T Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            try {
                switch (__state) {

                case 0:
                    __state = -1;
                    if (__this.left == null) goto __yield_value;
                    __left = __this.left.GetEnumerator();
                    goto case 1;

                case 1:
                    __state = -2;
                    if (!__left.MoveNext()) goto __left_dispose;
                    __current = __left.Current;
                    __state = 1;
                    return true;

                __left_dispose:
                    __state = -1;
                    __left.Dispose();

                __yield_value:
                    __current = __this.value;
                    __state = 2;
                    return true;

                case 2:
                    __state = -1;
                    if (__this.right == null) goto __end;
                    __right = __this.right.GetEnumerator();
                    goto case 3;

                case 3:
                    __state = -3;
                    if (!__right.MoveNext()) goto __right_dispose;
                    __current = __right.Current;
                    __state = 3;
                    return true;

                __right_dispose:
                    __state = -1;
                    __right.Dispose();

                __end:
                    __state = 4;
                    break;

                }
            }
            finally {
                if (__state < 0) Dispose();
            }
            return false;
        }

        public void Dispose() {
            try {
                switch (__state) {

                case 1:
                case -2:
                    __left.Dispose();
                    break;

                case 3:
                case -3:
                    __right.Dispose();
                    break;

                }
            }
            finally {
                __state = 4;
            }
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

在語句中使用的編譯器產生的而暫存物件 foreach 會提升至 __left 列舉值物件的和 __right 欄位中。The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. __state列舉值物件的欄位會經過仔細更新,如果擲回例外狀況,就會 Dispose() 正確地呼叫正確的方法。The __state field of the enumerator object is carefully updated so that the correct Dispose() method will be called correctly if an exception is thrown. 請注意,不可能使用簡單的語句來撰寫翻譯的程式碼 foreachNote that it is not possible to write the translated code with simple foreach statements.

Async 函數Async functions

方法 (方法) 或匿名函式 (匿名 函式運算式) 使用 async 修飾詞,稱為 *async function _。A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an *async function _. 一般情況下,會使用 _ async* 一詞來描述任何具有修飾詞的函式 asyncIn general, the term _ async* is used to describe any kind of function that has the async modifier.

非同步函式的型式參數清單會產生編譯時期錯誤,以指定任何 refout 參數。It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

Async 方法的 return_type 必須是 void 或工作 類型The return_type of an async method must be either void or a task type. 工作類型是和所建立的 System.Threading.Tasks.Task 類型 System.Threading.Tasks.Task<T>The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. 為了簡潔起見,在本章中,這些類型 Task 分別稱為和 Task<T>For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. 傳回工作類型的非同步方法稱為「工作傳回」。An async method returning a task type is said to be task-returning.

工作類型的確切定義是已定義的實作,但是從語言的觀點來看,工作類型的狀態為 [不完整]、[成功] 或 [錯誤]。The exact definition of the task types is implementation defined, but from the language's point of view a task type is in one of the states incomplete, succeeded or faulted. 錯誤的工作會記錄相關的例外狀況。A faulted task records a pertinent exception. 成功 Task<T> 記錄型別的結果 TA succeeded Task<T> records a result of type T. 工作類型為可等候,因此可以是 await 運算式 (await 運算式) 的運算元。Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

非同步函式調用可透過 await 運算式 (await 運算式) 在其主體中,以暫停評估。An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. 稍後可能會在暫停 await 運算式的點繼續進行評估,方法是使用 *恢復委派 _。Evaluation may later be resumed at the point of the suspending await expression by means of a *resumption delegate _. 繼續委派的型別為 System.Action ,而且當叫用時,非同步函式調用的評估將會從其離開的 await 運算式繼續進行。The resumption delegate is of type System.Action, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. 如果從未暫止函式呼叫,或繼續委派的最新呼叫端,則非同步函式叫用的 _ 目前呼叫者* 就是原始呼叫端。The _ current caller* of an async function invocation is the original caller if the function invocation has never been suspended, or the most recent caller of the resumption delegate otherwise.

評估工作傳回的非同步函式Evaluation of a task-returning async function

傳回工作傳回的非同步函式會導致產生傳回之工作類型的實例。Invocation of a task-returning async function causes an instance of the returned task type to be generated. 這稱為非同步函式的 return 工作。This is called the return task of the async function. 工作最初處於不完整的狀態。The task is initially in an incomplete state.

非同步函式主體接著會進行評估,直到它 (藉由) 或結束的 await 運算式暫停為止,此時會將 point 控制項傳回給呼叫者,以及傳回的工作。The async function body is then evaluated until it is either suspended (by reaching an await expression) or terminates, at which point control is returned to the caller, along with the return task.

當非同步函式的主體終止時,傳回的工作會移出未完成的狀態:When the body of the async function terminates, the return task is moved out of the incomplete state:

  • 如果函式主體因為到達 return 語句或主體結尾而終止,則會將任何結果值記錄在傳回工作中,該工作會進入成功狀態。If the function body terminates as the result of reaching a return statement or the end of the body, any result value is recorded in the return task, which is put into a succeeded state.
  • 如果函式主體因為未攔截的例外狀況 (擲回 語句 而終止,) 例外狀況會記錄在會進入錯誤狀態的傳回工作中。If the function body terminates as the result of an uncaught exception (The throw statement) the exception is recorded in the return task which is put into a faulted state.

Void 傳回非同步函式的評估Evaluation of a void-returning async function

如果非同步函式的傳回型別為 void ,則評估會以下列方式與上述不同:因為不會傳回任何工作,所以函式會改為將完成和例外狀況傳達給目前線程的 同步處理內容If the return type of the async function is void, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current thread's synchronization context. 同步處理內容的確切定義與執行相依,但表示目前線程正在執行的「where」。The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. 當評估傳回的非同步函式開始時發生、成功完成,或導致擲回未攔截的例外狀況時,就會通知同步處理內容。The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

這可讓內容追蹤在其下執行的 void 傳回非同步函式數目,以及決定如何傳播來自它們的例外狀況。This allows the context to keep track of how many void-returning async functions are running under it, and to decide how to propagate exceptions coming out of them.