類別Classes

「類別」(class)是一種資料結構,其中可能包含資料成員(常數和欄位)、函式成員(方法、屬性、事件、索引子、運算子、實例的函式、析構函式和靜態的函式)和巢狀型別。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_modifiers (類別修飾詞),後面接著選擇性的 partial 修飾詞,後面接著關鍵字class 和命名類別的識別碼,後面接著選擇性的type_parameter_list類型參數),後面接著選擇性的class_base規格(類別基底規格),後面接著一組選擇性的type_parameter_constraints_clauses (類型參數條件約束),後面接著class_body類別主體),並選擇性地後面加上分號。A class_declaration consists of an optional set of attributes (Attributes), followed by an optional set of class_modifiers (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_clauses (Type parameter constraints), followed by a class_body (Class body), optionally followed by a semicolon.

類別宣告無法提供type_parameter_constraints_clause,除非它也提供type_parameter_listA class declaration cannot supply type_parameter_constraints_clauses 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.

在嵌套類別上允許修飾詞。newThe 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.

publicinternal、和修飾private詞會控制類別的存取範圍。 protectedThe 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).

下列各sealedstatic將討論、和修飾詞。abstractThe 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:

  • 抽象類別無法直接具現化,而且在抽象類別上使用new運算子時,會發生編譯時期錯誤。An 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
    }
}

abstract 類別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. 類別CF覆寫並提供實際的實作為。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

修飾詞是用來標示要宣告為靜態類別的類別。 staticThe 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:

  • 靜態類別不能包含sealedabstract修飾詞。A static class may not include a sealed or abstract modifier. 不過,請注意,因為靜態類別無法具現化或衍生自,所以其行為就像是密封和抽象。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 internal宣告存取範圍的成員。A 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. 您不能在靜態類別中宣告實例的函式,也不會提供靜態類別的預設實例(default)。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). 當類別在靜態外部類別中嵌套時,除非明確包含static修飾詞,否則嵌套類別不是靜態類別。When 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.I 格式的namespace_or_type_name中的 T,或The namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • Namespace_or_type_name是表單T 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 、運算式sizeofas運算式、運算式或預設值運算式。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

@No__t-0 修飾詞是用來表示這個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_base中包含class_type時,它會指定所要宣告之類別的直接基底類別。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並未明確指定直接基類,因此它的直接基類是隱含object的。Since 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自或internal類別的類別,這是編譯時期錯誤。For 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的直接基底類別會暫時假設object為。While 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>發生錯誤object,因為在基類規格中, C會將的直接基類視為,因此(由命名空間和型別名稱的規則) C不會被視為具有成員B.is 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. 參考上述範例,的基本類別BAobjectReferring 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編譯時期B錯誤,使其相依于。When 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于(其立即A封閉的類別),這會迴圈的相依。results 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 B B取決於A (因為既是它的直接基類,也是其立即封入類別),但不依賴(因為不是基類,也不是的封入類別A A).B 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從類別A衍生。class 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_clauses。Generic type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clauses.

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 的 token,後面接著型別參數的名稱,後面接著冒號和該型別參數的條件約束清單。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. 就像get屬性set 存取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_parameterinterface_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.Array:、 System.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.
  • 在指定where的子句中,類型不得指定一次以上。A 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.
  • 在指定where的子句中,類型不得指定一次以上。A type must not be specified more than once in a given where clause.

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

  • 如果使用型別T參數做為型別參數S的條件約束S ,則會相依于 TIf a type parameter T is used as a constraint for type parameter S then S depends on T.
  • 如果型別參數S相依于型別參數TT而且U S U相依于型別參數,則會相依于If 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. 如果類型參數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.
  • 如果 S 具有class_type條件約束 A,而 T 具有 Bclass_type條件約束,則必須有從 AB 的識別轉換或隱含參考轉換,或從BAIf 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條件約束 @no__t 為-4,而 Tclass_type條件約束 B,則必須從 A 進行識別轉換或隱含參考轉換。若要 B,或從 0 到 1 的隱含參考轉換。If 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.ValueType、和任何介面類別型有效地進行這種限制。System.EnumEffectively 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
{
    ...
}

型別參數T有效基類定義如下:The 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.ValueType為。If 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_ 的有效基類集中最包含的類型(提升轉換運算子參數條件約束。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@no__t 的最特定基底類型。For 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.
  • 如果 T 具有interface_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.ValueType,則已知為參考型別A 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因為會限制為一律IPrintable執行。the 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包含 partial 修飾詞,則代表部分類型宣告。A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partial不是關鍵字,而且只會做為修飾詞(如果它緊接在class其中一個關鍵字之前) structinterface在類型宣告中,或在方法宣告中的類型void之前。partial 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.

您可以使用partial修飾詞,在多個元件中宣告嵌套的類型。Nested 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規格(、、 internalprotected 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修飾詞,則會將類別視為密封(密封類別)。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. 特定基底介面只能在每個元件上命名一次,但允許多個部分命名為相同的基底介面。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 {...}

類別C的基底介面集為IAIBICthe 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. 在類型的多個部分中宣告相同的成員是編譯時期錯誤,除非該成員是具有partial修飾詞的類型。It 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.

部分方法不能定義存取修飾詞,而private是隱含的。Partial 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. 如果主體是以區塊的形式提供,則宣告會被視為實作為部分方法宣告。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個元件可能會出現不同的指示詞(Using指示詞)。Thus, different using directives (Using directives) may be present for each part. 在某個部分中解讀簡單名稱(類型推斷)時,只using會考慮包含該部分之命名空間宣告的指示詞。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_declarations 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_declaration立即包含的class_member_declaration會將新的成員引進此宣告空間。A class_declaration creates a new declaration space (Declarations), and the class_member_declarations immediately contained by the class_declaration introduce new members into this declaration space. 下列規則適用于class_member_declarations:The following rules apply to class_member_declarations:

  • 實例的函式、析構函式和靜態的函式必須與直接封入類別的名稱相同。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和的簽章。out.In 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.
  • 實例函式的簽章必須與相同類別中宣告之所有其他實例的簽章的簽章不同,而且在相同類別中宣告的兩個函式可能不會refout與完全不同的簽章。The 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 T類別宣告a中的成員類型為「二維陣列」,因此上述結構型別中的成員類型是「一維陣列的二維陣列Gen int"、或int[,][]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所宣告的成員,以及中A所宣告的成員。If 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_parameterclass_base規格的對應type_argument )來進行轉換。These 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取代型別參數T所取得的非繼承成員。In 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>也具有類別B宣告中的繼承成員。D<int> also has an inherited member from the class declaration B. 這個繼承成員的決定方式是先以基類B<int[]>規格B<T[]>中的D<int> T取代int來判斷的基類類型。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[]>. 然後,做為的型別Bint[]變數,會Upublic 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).

New 修飾詞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. 移除new修飾詞會隱藏這個警告。This 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不包含任何存取修飾詞時,會假設為 privateWhen 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:

  • E.M 格式的member_access成員存取)中參考到靜態成員 M 時,E 必須代表包含 M 的類型。When 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 在表單 E.Mmember_access成員存取)中被參考時,E 必須代表包含 M 之類型的實例。When 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. 這是用E來表示類型的系結時錯誤。It 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
    }
}

@No__t-0 方法顯示,在實例函式成員中,可以使用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. @No__t-0 方法顯示,在靜態函式成員中,這是編譯時期錯誤,可透過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. @No__t-0 方法顯示在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

在類別或結構宣告內宣告的型別稱為「嵌套型別」。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是宣告類型N的類型完整名稱。The 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

非巢狀型別可以有publicinternal宣告存取範圍,且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 internalprotectedinternalprivate),和其他類別成員一樣,預設為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.
  • 在結構中宣告的巢狀型別可以有三種形式的宣告存取範圍public(、 internalprivate),而且就像其他結構成員一樣,預設為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 {...} }
}

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

起來Hiding

巢狀型別可能會隱藏(名稱隱藏)基底成員。A nested type may hide (Name hiding) a base member. 在嵌套的類型宣告上允許修飾詞,因此可以明確表示隱藏。newThe 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 MBase定義之方法的嵌套類別。shows 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. 當巢狀型別需要存取其包含類型的實例成員時,可以this提供包含類型實例的,做為巢狀型別的函式引數。In 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實例會建立的this Nested實例,並將它自己的Nested設為的函式,以便提供後續C的實例成員存取權。An 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 protected已宣告存取範圍之包含類型的成員。A 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類別Nested的類別。shows 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();
    }
}

nested Derived.Nested類別會透過的實例Base F 呼叫,Derived來存取的基類中所定義的受保護方法。 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:

  • 允許基礎執行程式使用一般識別碼作為方法名稱,以取得或設定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.
  • 若要允許其他語言互通,請使用一般識別碼做為 get 或 set 存取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,因此會保留和set_P方法的get_P簽章。a 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

若為具有參數清單L之類型T的索引子(索引器),則會保留下列簽章: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可以包含一組屬性attribute)、一個 @no__t 3 修飾詞(新的修飾詞),以及四個存取修飾詞(存取修飾詞)的有效組合。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也不需要也不允許 static 修飾詞。Even 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的清單,其中每個都會引進新的成員。The type is followed by a list of constant_declarators, 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.

常數宣告中指定的類型必須是 sbytebyteshortushortintuintlong、1、char、0、1、2、3、4、 enum_typereference_輸入The 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. 除了 @no__t- 1 以外, 建立非 null 值的唯一方法是套用 new 運算子,而且因為constant_expression中不允許使用 new 運算子,所以常數的唯一可能值為string 以外的 reference_type 是 nullSince 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_types other than string is null.

當需要常數值的符號名稱時,但當常數宣告中不允許該值的類型,或在編譯時期無法由constant_expression計算值時,@no__t 1 欄位(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.YB.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.XB.Z可能會相依于,但B.Z之後可能不會同時依賴A.YReferring 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可以包含一組屬性attribute)、一個 @no__t 3 修飾詞(新的修飾詞)、四個存取修飾詞(存取修飾詞)的有效組合,以及 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包括修飾詞( volatile Readonly 欄位)或修飾詞(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的清單,其中每個都會引進新的成員。The type is followed by a list of variable_declarators, each of which introduces a new member. Variable_declarator包含命名該成員的識別碼,選擇性地後面接著 "@no__t 2" 權杖和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.

E.M 格式的member_access成員存取)中參考欄位時,如果 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包含 @no__t 1 修飾詞時,宣告引進的欄位就是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做為或ref參數的內容。These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

嘗試指派給readonly欄位,或在任何其他內容中outref或參數的形式傳遞,會產生編譯時期錯誤。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

當需要常數值的符號名稱,但在宣告const中不允許值的類型,或在編譯時期無法計算值時,欄位就很有用。static readonlyA 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;
    }
}

、、Red White、和Green成員無法const宣告為成員,因為它們的值無法在編譯時期計算。 Blue Blackthe 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

常數和 readonly 欄位具有不同的二進位版本控制語義。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即使Program2未重新編譯,語句仍會輸出新的值。Thus, 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取得的值,而且在重新編譯之前Program2 ,中Program1的變更不會受到影響。However, 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 fields

field_declaration包含 @no__t 1 修飾詞時,該宣告所引進的欄位就是變動性欄位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. 對於變動性欄位,這類重新排列優化會受到限制:For volatile fields, such reordering optimizations are restricted:

  • 讀取 volatile 欄位稱為「變動性讀取」。A read of a volatile field is called a volatile read. 「變動性讀取」具有「取得語義」;也就是說,在指令序列之後發生的記憶體參照之前,一定會發生這種情況。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類型、System.IntPtr、、 、shortbool、、、、或System.UIntPtrushort int sbyte uint char floatThe type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • 具有 bytesbyteshortushortintuint 之列舉基底類型的enum_typeAn 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為的非 volatile 欄位中,然後將儲存truefinishedvolatile 欄位中。This 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. 因為finished143宣告,所以主執行緒必須讀取欄位result中的值。 volatileSince finished has been declared volatile, the main thread must read the value 143 from the field result. finished如果尚未result finished 0宣告欄位,則會允許存放區在存放區之後顯示在主執行緒上,並因此讓主執行緒讀取來自的值volatile欄位resultIf 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. finished 宣告volatile為欄位可防止任何這類不一致。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_initializers。Field declarations may include variable_initializers. 對於靜態欄位,變數初始化運算式會對應到在類別初始化期間執行的指派語句。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

在執行其初始化a運算式b之前,會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

因為的初始化運算式和的初始化Y運算式可能會以任一順序發生,所以只會限制在這些欄位的參考之前。 Xbecause 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_declarations 來宣告:Methods are declared using method_declarations:

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虛擬方法)、0 (覆寫方法)、2 (密封的方法)、4 (抽象方法),以及 6 (外部方法)修飾詞。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.
  • 宣告最多包含下列其中一個修飾詞: new和。 overrideThe declaration includes at most one of the following modifiers: new and override.
  • abstract如果宣告包含修飾詞,則宣告不會包含下列任何修飾符: virtual staticsealedexternIf 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修飾詞,則宣告也會override包含修飾詞。If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • 如果宣告partial包含修飾詞,則不會包含下列任何修飾符: newpublicprotectedinternalprivatevirtual、、 override sealedabstract或。externIf 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_typevoidThe 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_listIf 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會指定個別型別參數的條件約束(型別參數條件約束),而且只有在同時提供type_parameter_list時才會指定,而且方法不會有override 修飾詞。The optional type_parameter_constraints_clauses 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.

Return_type和方法的formal_parameter_list中所參考的每個類型,必須至少與方法本身(存取範圍條件約束)相同。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. 語句主體是由區塊所組成,它會指定叫用方法時要執行的語句。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.

若為 abstract 和 @no__t 1 方法, 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 out不同的簽章。In 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_clauses 的整個範圍內形成類型,但不能用來組成在屬性中。The method's type_parameters 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_clauses 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是由一組選擇性的屬性屬性)、選擇性的 refoutthis 修飾詞、類型識別碼和選擇性default_argument所組成。A 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_argumentfixed_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.

@No__t-0 或 @no__t 1 參數不能有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是由一組選擇性的屬性屬性)、一個 @no__t 3 修飾詞、一個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
) { }

Mformal_parameter_list中,i 是必要的 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.
  • 參考參數,使用ref修飾詞來宣告。Reference parameters, which are declared with the ref modifier.
  • 使用out修飾詞宣告的輸出參數。Output parameters, which are declared with the out modifier.
  • 使用params修飾詞宣告的參數陣列。Parameter arrays, which are declared with the params modifier.

如簽章多載中所refout ,和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.

允許方法將新值指派給值參數。A method is permitted to assign new values to a value parameter. 這類指派只會影響值參數所代表的本機儲存位置,而不會影響在方法調用中指定的實際引數。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.

宣告為 iterator (反覆運算器)的方法不能有參考參數。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在中Main的調用, x表示iy表示。jFor the invocation of Swap in Main, x represents i and y represents j. 因此,叫用具有交換ij值的效果。Thus, 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);
    }
}

Fs b的調用會將的a參考傳遞給和。 Gthe invocation of F in G passes a reference to s for both a and b. 因此,對於該調用, s名稱、 ab全都參考相同的儲存位置,而三個指派全都會修改實例欄位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修飾詞宣告的參數是 output 參數。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.

宣告為部分方法(部分方法)或 Iterator (反覆運算器)的方法不能有輸出參數。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

請注意, name SplitPath和變數可以在傳遞至之前解除指派,並在呼叫之後將其視為明確指派。 dirNote 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修飾詞與ref修飾詞和out結合。It 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[]在方法的一般形式和單一object參數所花費的形式之間,可能會有不明確的情況。When 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).

E.M 格式的member_access成員存取)中參考方法時,如果 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. 在確切的詞彙中,使用具有N編譯時間型C別和C運行A時間型R別之實例的引數清單叫用名為的方法時( R其中是或衍生的類別)。從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:

  • 首先,多載解析會套用CN、和A,以從中宣告並M繼承C的方法集合中選取特定的方法。First, 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是非虛擬方法, M則會叫用。Then, if M is a non-virtual method, M is invoked.
  • 否則, M會是虛擬方法,而且會叫用與相關M R的最高衍生的執行。Otherwise, 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 R相關之虛擬方法的最衍生實作為判斷方式如下:The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • 如果R包含的引進virtual M宣告,則這是最常衍生的M執行。If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • 否則,如果R override包含的M,則這是最常衍生的M執行。Otherwise, 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的,而且也會覆寫繼承G的方法。The 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

請注意B.G,語句a.G()會叫用A.G,而不是。Notice 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類別包含兩個具有相同簽章的虛擬方法:所導入C的和所引進的一個。Athe C and D classes contain two virtual methods with the same signature: The one introduced by A and the one introduced by C. 引進C的方法會隱藏繼承自A的方法。The method introduced by C hides the method inherited from A. 因此,中的 override 宣告D會覆寫所導C入的方法,而不可能D覆寫所引進A的方法。Thus, 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. Override 方法會以相同的簽章覆寫繼承的虛擬方法。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. 針對在類別CM宣告的覆寫方法,會藉由檢查的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視為可存取internal protected protected internal,如果是,則為,如果是,則為,如果是,則C會在與相同的程式中宣告。For 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() PrintFields A的調用會叫用在中宣告的方法。 Bthe 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在寫入B PrintFields PrintFields A了調用之後,它會以遞迴方式叫用在中宣告的方法,而不是在中宣告的方法,因為是虛擬和的執行時間類型。 ((A)this).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 override F的方法不包含修飾詞,因此不會覆寫中A的方法。 Bthe F method in B does not include an override modifier and therefore does not override the F method in A. 相反地, FB的方法會隱藏中A的方法,而且會new回報警告,因為宣告不包含修飾詞。Rather, 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 F A的方法會隱藏繼承自的虛擬方法。 Bthe F method in B hides the virtual F method inherited from A. 由於中的FB具有私用存取,其範圍僅B包含的類別主體,而且不會延伸C至。Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. 因此, FC的宣告允許覆寫繼承自AFTherefore, 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會防止C進一步覆寫FB'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. 雖然抽象方法也是隱含的虛擬方法,但它不能有修飾virtual詞。Although 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. Ellipse Shape類別是具體的實作為。 BoxThe 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修飾詞時,該方法會被視為外部方法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.

修飾詞通常會DllImport與屬性搭配使用(與 COM 和 Win32 元件互通),讓外部方法可以由 dll (動態連結程式庫)來執行。 externThe 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. 這個範例示範extern修飾詞DllImport和屬性的用法: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());
        }
    }
}

方法可ToInt32 string在上使用,而且方法可在上使用,因為它們已宣告為擴充方法。 string[] SliceThe 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.Task,則為方法的結果型別。The 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.

當方法具有 @no__t 0 的結果和運算式主體時,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. 和方法H是正確的,因為所有可能的執行路徑都在指定傳回值的 return 語句中結束。 GThe 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_declarations 來宣告:Properties are declared using property_declarations:

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虛擬方法)、0 (覆寫方法)、2 (密封的方法)、4 (抽象方法),以及 6 (外部方法)修飾詞。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. 因此,您無法將屬性ref當做或out引數傳遞。Thus, it is not possible to pass a property as a ref or out argument.

當屬性宣告包含extern修飾詞時,屬性會被視為外部屬性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.

當屬性在 E.M 格式的member_access成員存取)中參考時,如果 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. 每個存取子宣告都包含 get 的 token,或 set,後面接著選擇性的accessor_modifieraccessor_bodyEach accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

使用accessor_modifier會受到下列限制:The use of accessor_modifiers 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 修飾詞的屬性或索引子,只有在屬性或索引子同時具備 @no__t 2 和 @no__t 3 存取子,而且只允許在其中一個存取子時,才允許accessor_modifierFor 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:
    • 如果屬性或索引子具有 public 的宣告存取範圍,則accessor_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 internal 的宣告存取範圍,則accessor_modifier可能是 internalprotectedprivateIf the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • 如果屬性或索引子具有 internalprotected 的已宣告存取範圍,則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.

對於 abstract 和 @no__t 1 屬性而言,指定的每個存取子的accessor_body只是一個分號。For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. 非抽象的非外部屬性可能會有每個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. 對於任何其他非抽象的非外部屬性的存取子, 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. 特別是,存取returnget主體中的所有語句都必須指定可隱含轉換成屬性類型的運算式。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.

存取子會對應至方法,其中包含屬性類型的單一值參數void和傳回型別。 setA 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. 當屬性被參考為指派(指派運算子)的目標時,或做++為或--的運算元(後置遞增和遞減運算子前置遞增和遞減運算子)時,存取子是使用引數(其值是指派的右手邊或++ or --運算子的運算元)來叫用,以提供新的值(簡單指派)。 setWhen 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. 由於存取子會隱含地具有名value為的參數,因此, set存取子中的區域變數或常數宣告會有一個編譯時期錯誤,以擁有該名稱。 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.

根據getset存取子的存在與否,屬性的分類方式如下: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存取子的屬性稱為「僅限寫入」屬性。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
    }
}

控制項會宣告公用Caption屬性。 Buttonthe Button control declares a public Caption property. Caption屬性get的存取子會傳回儲存在私caption用欄位中的字串。The 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.

針對上述Caption類別,下列是使用屬性的範例: ButtonGiven 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.

屬性getset和存取子不是不同的成員,而且不可能分別宣告屬性的存取子。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 P A的屬性會隱藏中的屬性,與讀取和寫入有關。 Bthe 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指派會導致報告編譯時期錯誤,因為中B的唯讀P屬性會在中A隱藏寫入P屬性。the 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. 不過,請注意,cast 可以用來存取 hidden 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為類型Point的屬性。The 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; }
    }
}

有,而是public readonly欄位,可能Label無法對類別進行這類變更。 y xHad 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. 此程式稱為「內嵌」,它可讓屬性存取的效率與欄位存取一樣有效率,但仍會保留更多的屬性彈性。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;
        }
    }
}

類別包含三個Out屬性: In、和Error,分別代表標準的輸入、輸出和錯誤裝置。 ConsoleThe 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. 但是,如果應用程式不會參考InError屬性,則不會為這些裝置建立任何物件。But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

自動執行的屬性Automatically implemented properties

自動執行的屬性(簡稱為「自動」屬性)是具有僅限分號存取子主體的非抽象非外部屬性。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唯讀欄位)。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_initializer變數初始化運算式)。An 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.
  • 如果使用方式是複合指派(複合指派)的目標,或做為++ --或運算子的目標(函式成員.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屬性,即使在只呼叫存取子的set內容中也是如此。In 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, 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.

包含abstractoverride修飾詞的屬性宣告,會指定屬性為抽象,並覆寫基底屬性。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.

抽象屬性宣告只允許用於抽象類別(抽象類別)。藉由包含指定override指示詞的屬性宣告,可以在衍生類別中覆寫繼承的虛擬屬性的存取子。Abstract 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 存取子的行為與虛擬、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.
  • 存取子會對應至方法void ,其中包含屬性類型的單一值參數、傳回類型,以及與包含屬性相同的修飾詞。 setA 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 ZY宣告會覆寫屬性宣告。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. baseget存取子set和的存取Y子會使用關鍵字來存取繼承的存取子。 XThe get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. 的宣告會B B覆寫抽象存取子(因此,中沒有任何未處理的抽象函數成員),且允許為非Z抽象類別。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
    }
}

EventsEvents

事件」(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_declarations 宣告的:Events are declared using event_declarations:

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虛擬方法)、0 (覆寫方法)、2 (密封的方法)、4 (抽象方法),以及 6 (外部方法)修飾詞。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_declarators. 屬性和修飾詞適用于這類event_declaration所宣告的所有成員。The attributes and modifiers apply to all of the members declared by such an event_declaration.

發生編譯時期錯誤,讓event_declaration同時包含 @no__t 1 修飾詞和以大括弧分隔的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修飾詞時,事件稱為「外來事件」。When an event declaration includes an extern modifier, the event is said to be an external event. 因為外來事件宣告不會提供實際的實值,所以它會錯誤地包含 extern 修飾詞和event_accessor_declarationsBecause 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是包含variable_initializerabstract 或 @no__t 2 修飾詞,就會發生編譯時期錯誤。It is a compile-time error for a variable_declarator of an event declaration with an abstract or external modifier to include a variable_initializer.

事件可用來做為+=-=運算子(事件指派)的左運算元。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表單voidx -= y的作業中,當x是事件,而參考發生在包含宣告的x類型之外時,作業的結果會有類型(而不是使用的類型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.

下列範例顯示如何將事件處理常式附加至Button類別的實例:The 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. 如果尚未加入任何事件處理常式,則欄位會null包含。If 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會當做Button類別中的欄位使用。Click 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方法會「引發」事件。Click ButtonThe 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.

Button類別的宣告外Click ,成員只能在+=-=運算子的左邊使用,如下所示: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(...);

這會從Click事件的調用清單中移除委派。which 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. 新增和移除作業是安全線程,而且可能(但不需要)在針對實例事件的包含物件或型別物件(匿名物件建立運算式)保留鎖定(lock 語句)時完成適用于靜態事件。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 */
        }
    }
}

在類別X內, Ev +=-=運算子左側的參考會導致叫用 add 和 remove 存取子。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. 每個存取子宣告都是addremove token 或後面接著區塊所組成。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都會對應至一個方法,其中包含事件種類的單一值參數和一個 @no__t 2 傳回類型。Each 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的區塊必須符合方法主體中所述 @no__t 2 方法的規則。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. 方法會將委派值與索引鍵產生關聯GetEventHandler ,方法會傳回目前與索引鍵相關聯的委派, RemoveEventHandler而方法則會移除委派,做為指定事件的事件處理常式。 AddEventHandlerThe 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.

E.M 格式的member_access成員存取)中參考事件時,如果 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).

繼承之虛擬事件的存取子可以在衍生類別中覆寫,方法是包含指定override修飾詞的事件宣告。The 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.

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

除了宣告和調用語法的差異之外,virtual、sealed、override 和 abstract 存取子的行為與虛擬、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_declarations 宣告索引子:Indexers are declared using indexer_declarations:

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 (覆寫方法)、0 (密封方法)、2 (抽象方法),以及 4 (外部方法)修飾詞。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.

索引子宣告受限於與方法宣告(方法)相同的規則,與修飾詞的有效組合有關,唯一的例外是在索引子宣告上不允許使用 static 修飾詞。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. abstractoverride修飾詞可以一起使用,讓抽象的索引子可以覆寫虛擬一個。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而索引子的存取子會對應至具有與索引子相同的型式參數清單的方法,再加上一個額外的參數。名value為。A 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修飾詞時,索引子稱為外部索引子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使用和傳統 "eratosthenes" 演算法來計算介於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

運算子」(operator)是一個成員,其定義可以套用至類別實例之運算式運算子的意義。An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. 使用operator_declaration來宣告運算子:Operators are declared using operator_declarations:

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. 語句主體是由區塊所組成,它會指定叫用運算子時要執行的語句。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:

  • 運算子宣告必須同時public包含static和修飾詞。An operator declaration must include both a public and a static modifier.
  • 運算子的參數必須是值參數(值參數)。The parameter(s) of an operator must be value parameters (Value parameters). 這是一個編譯時期錯誤,可讓運算子宣告指定refout參數。It 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.
  • 一元++--運算子必須採用類型TT?的單一參數,而且必須傳回該相同類型或從中衍生的類型。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 T?false運算子必須接受類型T為或的單一參數,而且必須傳回類型boolA unary true or false operator must take a single parameter of type T or T? and must return type bool.

一元運算子的簽章是由運算子 token (+- ! ~ -- ++ false、、、、、或)和單一型式參數的類型所組成。 trueThe 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. 和運算子會在使用者定義的條件式邏輯運算子布林運算式中進一步說明。 false trueThe 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 int? int運算子必須接受兩個參數,其中第一個必須具有類型或,而第二個必須具有類型或,而且可以傳回任何類型。 >>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 的S0類型T0 ,請讓和參考其基礎S0類型T0 ,否則和為分別等於T和。 SFor 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.
  • @No__t-0 或 T0 都不是interface_typeNeither S0 nor T0 is an interface_type.
  • 排除使用者定義的轉換,轉換不會S從到T或從TSExcluding user-defined conversions, a conversion does not exist from S to T or from T to S.

基於這些規則的目的,與ST相關聯的任何類型參數都會被視為與其他類型沒有繼承關係的唯一類型,而且會忽略這些類型參數上的任何條件約束。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結構類型可以定義從Cintint C到的轉換,而不是從intboolFor 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) {...}
}

當 type 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到類型T的預先定義隱含轉換(隱含轉換)存在,則會忽略從ST的所有使用者定義轉換(隱含或明確)。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到類型T的預先定義明確轉換(明確轉換)存在,則會忽略從ST的任何使用者定義明確轉換。If 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是介面類別型,則會忽略從ST的使用者定義隱含轉換。If T is an interface type, user-defined implicit conversions from S to T are ignored.

否則,使用者定義從ST的隱含轉換仍會被視為。Otherwise, 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_typeUser-defined conversions are not allowed to convert from or to interface_types. 特別是,這項限制可確保在轉換成interface_type時,不會發生任何使用者定義的轉換,而且只有在轉換的物件實際實作為目標時,才會成功轉換為interface_type指定的interface_typeIn 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);
    }
}

Digit轉換成byte是隱含的,因為它永遠不會擲回例外狀況或遺失資訊byteDigit但從轉換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來宣告:Instance constructors are declared using constructor_declarations:

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可以包含一組屬性屬性)、四個存取修飾詞(存取修飾詞)的有效組合,以及 @no__t 4 (外部方法)修飾詞。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_expressions (物件建立運算式)和透過constructor_initializer叫用。Instance constructors are invoked by object_creation_expressions (Object creation expressions) and through constructor_initializers.

建構函式初始設定式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_initializer所決定:The constructor to implicitly invoke is determined by the constructor_initializer:

  • 表單base(argument_list)的實例函式初始化運算式base() ,或會導致叫用直接基類的實例(class)。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() {...}

由實例的「函式宣告」(instance) 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_initializers 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(的預設值y ),因為int在基類的函式傳回之前,不會執行的指派。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;
    }
}

包含數個變數初始化運算式;它也包含兩種形式的函式base初始化this運算式(和)。contains 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或直接建立的T實例。When 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來宣告:Static constructors are declared using static_constructor_declarations:

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可以包含一組屬性屬性)和 @no__t 3 修飾詞(外部方法)。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宣告」包含修飾詞時,靜態的「函式」稱為「外部靜態」(external)函數。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. 這會完全對應至具有 @no__t 1 傳回型別(方法主體)之靜態方法的method_bodyThis 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 B呼叫會觸發的靜態函式執行B.F ,而的呼叫會觸發的執行靜態函式。A.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初始化運算式(在類別B的靜態的函式之前)。To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Y的初始化運算式A導致會執行的靜態函式,因為參考A.X的值。Y'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 ,並具有 @no__t 2 傳回型別(方法主體)。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();
   }
}

is

B's destructor
A's destructor

因為繼承鏈中的析構函數是依順序呼叫,從最常衍生到最不衍生的。since destructors in an inheritance chain are called in order, from most derived to least derived.

藉由覆寫上Finalize System.Object的虛擬方法,來實作為析構函數。Destructors 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.Object隱藏Finalize的方法。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

使用 iterator 區塊(區塊)所實作用的函式成員(函數成員)稱為iteratorA 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

Iterator 會產生一系列的值,這些都是相同的型別。An iterator produces a sequence of values, all of the same type. 這個型別稱為 iterator 的yield 型別。This type is called the yield type of the iterator.

  • 傳回IEnumerator objectIEnumerable的反覆運算器產生類型為。The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • 傳回IEnumerator<T> TIEnumerable<T>的反覆運算器產生類型為。The 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 區塊中指定的程式碼,而當叫用枚舉器物件的MoveNext方法時,就會在 iterator 區塊中執行程式碼。This 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:

  • 它會執行IEnumerator<T> T和,其中是反覆運算器的產生類型。 IEnumeratorIt 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提供CurrentIEnumerableDispose 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.NotSupportedException這個方法會導致擲回。Invoking this method causes a System.NotSupportedException to be thrown.

MoveNext 方法The MoveNext method

列舉值物件的方法會封裝iterator區塊的程式碼。MoveNextThe MoveNext method of an enumerator object encapsulates the code of an iterator block. 叫用Current方法會在 iterator 區塊中執行程式碼,並適當地設定列舉值物件的屬性。 MoveNextInvoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. 所執行的精確動作MoveNext取決於叫用時MoveNext ,列舉值物件的狀態:The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • 如果列舉值物件的狀態在之前,請MoveNext叫用:If 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).
  • 如果列舉值物件的狀態為MoveNext執行中,則會未指定叫用的結果。If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • 如果列舉值物件的狀態為已止, MoveNext則叫用:If the state of the enumerator object is suspended, invoking MoveNext:
    • 將狀態變更為 [正在執行]。Changes the state to running.
    • 將所有區域變數和參數的值(包括 this)還原為上次暫停執行反覆運算器區塊時所儲存的值。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.
    • 在導致執行暫止的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).
  • 如果列舉值物件的狀態在之後MoveNext false則叫用傳回。If the state of the enumerator object is after, invoking MoveNext returns false.

MoveNext執行 iterator 區塊時,可以透過四種方式來中斷執行:yield return透過語句yield break 、語句、遇到反覆運算器區塊的結尾,以及擲回的例外狀況,並從反覆運算器區塊傳播。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 語句): yield returnWhen 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 return語句的位置一樣。The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. 如果語句位於一個或多個try區塊內,此時不finally會執行相關聯的區塊。 yield returnIf 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.
    • 方法會回到true其呼叫端,表示反復專案已成功前進到下一個值。 MoveNextThe MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • 遇到語句時(yield 語句): yield breakWhen a yield break statement is encountered (The yield statement):
    • 如果語句位於一個或多個try區塊中,則會finally執行相關聯的區塊。 yield breakIf the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • 列舉值物件的狀態會變更為 [之後]。The state of the enumerator object is changed to after.
    • 方法會回到false其呼叫端,表示反復專案已完成。 MoveNextThe MoveNext method returns false to its caller, indicating that the iteration is complete.
  • 遇到反覆運算器主體的結尾時:When the end of the iterator body is encountered:
    • 列舉值物件的狀態會變更為 [之後]。The state of the enumerator object is changed to after.
    • 方法會回到false其呼叫端,表示反復專案已完成。 MoveNextThe MoveNext method returns false to its caller, indicating that the iteration is complete.
  • 當擲回例外狀況並從反覆運算器區塊傳播時: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.
    • 列舉值物件的狀態會變更為 [之後]。The state of the enumerator object is changed to after.
    • 例外狀況傳播會繼續到MoveNext方法的呼叫端。The 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就是先前呼叫MoveNext所設定的值。When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. 當列舉值物件處於 [之前]、[執行中] 或 [之後] 狀態時Current ,不會指定存取的結果。When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified.

針對具有以外之 yield object類型的反覆運算器,透過列舉值物件Current CurrentIEnumerable實值存取的結果會對應到透過列舉值物件的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

方法是藉由將列舉值物件帶到 after 狀態來清除反復專案。 DisposeThe Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • 如果列舉值物件的狀態為 [之前], Dispose則叫用會將狀態變更為 [之後]。If the state of the enumerator object is before, invoking Dispose changes the state to after.
  • 如果列舉值物件的狀態為Dispose執行中,則會未指定叫用的結果。If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • 如果列舉值物件的狀態為已止, Dispose則叫用:If 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 ,而例外狀況會傳播至Dispose方法的呼叫端。If 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.
    • 將狀態變更為 [之後]。Changes the state to after.
  • 如果列舉值物件的狀態是afterDispose叫用就不會有任何影響。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 區塊中所指定之程式碼的列舉值物件,而當叫用枚舉器物件的MoveNext方法時,就會在 iterator 區塊中執行程式碼。The 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:

  • 它會執行IEnumerable<T> T和,其中是反覆運算器的產生類型。 IEnumerableIt 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 IEnumerableIEnumerable<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>類別會使用 iterator 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會轉換成列舉值物件中的欄位,因此它可以在的MoveNext調用之間繼續存在。Furthermore, 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中的方法會傳回可列舉的物件,並使用 iterator 來執行。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.

InvalidOperationException如果為,0則方法會擲回。 MoveNext __stateThe MoveNext method throws an InvalidOperationException if it is called when __state is 0. 這可防止在沒有第一次呼叫GetEnumerator的情況下,使用可列舉物件做為列舉值物件。This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

下列範例顯示簡單的樹狀目錄類別。The following example shows a simple tree class. 類別會使用 iterator GetEnumerator來執行其方法。 Tree<T>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. 列舉值物件的Dispose() 欄位會經過仔細更新,如此一來,如果擲回例外狀況,就會正確地呼叫正確的方法。__stateThe __state field of the enumerator object is carefully updated so that the correct Dispose() method will be called correctly if an exception is thrown. 請注意,您無法使用簡單foreach的語句來撰寫已轉譯的程式碼。Note that it is not possible to write the translated code with simple foreach statements.

Async 函數Async functions

使用async修飾詞的方法(方法)或匿名函式(匿名函式運算式)稱為非同步函式。A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an async function. 一般來說,「非同步」一詞是用來描述具有async修飾詞的任何一種函數。In 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.

非同步方法的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>. 為了簡潔起見,在本章中,這些類型分別會當做TaskTask<T>來參考。For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. 傳回工作類型的非同步方法稱為「工作傳回」(task)。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>記錄類型T的結果。A 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.

評估工作傳回的 async 函數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. 這稱為 async 函數的傳回工作。This is called the return task of the async function. 工作最初處於不完整的狀態。The task is initially in an incomplete state.

接著會評估 async 函式主體,直到它暫停(藉由到達 await 運算式)或終止為止,此時會將控制項傳回給呼叫端,以及傳回的工作。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.

當 async 函式的主體終止時,傳回的工作會移出不完整的狀態: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.
  • 如果函式主體因未攔截的例外狀況(throw 語句)而終止,則會在傳回錯誤狀態的傳回工作中記錄例外狀況。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 的 async 函數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. 同步處理內容的確切定義與實作為相依,但表示目前線程正在執行的「位置」。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.

這可讓內容追蹤在其底下執行的空傳回非同步函式數目,以及決定如何傳播來自它們的例外狀況。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.