KlassenClasses

Eine-Klasse ist eine Datenstruktur, die Datenmember (Konstanten und Felder), Funktionsmember (Methoden, Eigenschaften, Ereignisse, Indexer, Operatoren, Instanzkonstruktoren, Dekonstruktoren und statische Konstruktoren) und die in der Struktur enthaltenen Typen enthalten kann.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. Klassentypen unterstützen Vererbung, einen Mechanismus, mit dem eine abgeleitete Klasse eine Basisklasse erweitern und spezialisieren kann.Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

Klassen DeklarationenClass declarations

Ein class_declaration ist ein type_declaration (Typdeklarationen), der eine neue Klasse deklariert.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 ';'?
    ;

Ein class_declaration besteht aus einem optionalen Satz von Attributen (Attributen), gefolgt von einem optionalen Satz von class_modifiers (Klassenmodifizierer), gefolgt von einem optionalen partial-Modifizierer, gefolgt vom-Schlüsselwort. class und ein Bezeichner , der die-Klasse benennt, gefolgt von einem optionalen type_parameter_list (Typparameter), gefolgt von einer optionalen class_base -Spezifikation (Klassenbasis Spezifikation), gefolgt von ein optionaler Satz von type_parameter_constraints_clauses (Typparameter Einschränkungen), gefolgt von einem class_body (Klassen Text), optional gefolgt von einem Semikolon.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.

Eine Klassen Deklaration kann keine type_parameter_constraints_clauses bereitstellen, es sei denn, Sie stellt auch ein type_parameter_listA class declaration cannot supply type_parameter_constraints_clauses unless it also supplies a type_parameter_list.

Eine Klassen Deklaration, die ein type_parameter_list bereitstellt, ist eine generische Klassen Deklaration.A class declaration that supplies a type_parameter_list is a generic class declaration. Außerdem ist jede Klasse, die in einer generischen Klassen Deklaration oder generischen Struktur Deklaration geschachtelt ist, selbst eine generische Klassen Deklaration, da Typparameter für den enthaltenden Typ angegeben werden müssen, um einen konstruierten Typ zu erstellen.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.

KlassenmodifiziererClass modifiers

Ein class_declaration kann optional eine Sequenz von Klassenmodifizierer einschließen:A class_declaration may optionally include a sequence of class modifiers:

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

Es ist ein Kompilierzeitfehler, damit derselbe Modifizierer mehrmals in einer Klassen Deklaration angezeigt wird.It is a compile-time error for the same modifier to appear multiple times in a class declaration.

Der new -Modifizierer ist für-Klassen zulässig.The new modifier is permitted on nested classes. Er gibt an, dass die Klasse einen geerbten Member mit demselben Namen verbirgt, wie im neuen Modifiziererbeschrieben.It specifies that the class hides an inherited member by the same name, as described in The new modifier. Es ist ein Kompilierzeitfehler, damit new der-Modifizierer in einer Klassen Deklaration angezeigt wird, die keine Klassen Deklaration ist.It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

Die publicModifizierer protected, private , internalund Steuern den Zugriff auf die-Klasse.The public, protected, internal, and private modifiers control the accessibility of the class. Abhängig vom Kontext, in dem die Klassen Deklaration auftritt, sind einige dieser Modifizierer möglicherweise nicht zulässig (alsBarrierefreiheit deklariert).Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

Die abstractmodifiziererer, sealed und static werden in den folgenden Abschnitten erläutert.The abstract, sealed and static modifiers are discussed in the following sections.

Abstrakte KlassenAbstract classes

Der abstract -Modifizierer wird verwendet, um anzugeben, dass eine Klasse unvollständig ist und nur als Basisklasse verwendet werden soll.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. Eine abstrakte Klasse unterscheidet sich wie folgt von einer nicht abstrakten Klasse:An abstract class differs from a non-abstract class in the following ways:

  • Eine abstrakte Klasse kann nicht direkt instanziiert werden, und es handelt sich um einen Kompilierzeitfehler new , wenn der Operator für eine abstrakte Klasse verwendet werden soll.An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. Obwohl es möglich ist, Variablen und Werte zu haben, deren Kompilier Zeittypen abstrakt sind, sind diese Variablen und Werte null notwendigerweise entweder ein oder enthalten Verweise auf Instanzen von nicht abstrakten Klassen, die von den abstrakten Typen abgeleitet sind.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.
  • Eine abstrakte Klasse ist zulässig (jedoch nicht erforderlich), um abstrakte Member zu enthalten.An abstract class is permitted (but not required) to contain abstract members.
  • Eine abstrakte Klasse kann nicht versiegelt werden.An abstract class cannot be sealed.

Wenn eine nicht abstrakte Klasse von einer abstrakten Klasse abgeleitet wird, muss die nicht abstrakte Klasse tatsächliche Implementierungen aller geerbten abstrakten Member enthalten, wodurch diese abstrakten Member überschrieben werden.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. Im BeispielIn 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
    }
}

die abstrakte- A Klasse führt eine abstrakte FMethode ein.the abstract class A introduces an abstract method F. Die B -Klasse führt eine Gzusätzliche-Methode ein, aber da Sie keine FImplementierung B von bereitstellt, muss auch als abstrakt deklariert werden.Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. Die C Klasse über F schreibt und stellt eine tatsächliche Implementierung bereit.Class C overrides F and provides an actual implementation. Da keine abstrakten Member in Cvorhanden sind, C ist zulässig (jedoch nicht erforderlich), um nicht abstrakt zu sein.Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

Versiegelte KlassenSealed classes

Der sealed -Modifizierer wird verwendet, um die Ableitung von einer Klasse zu verhindern.The sealed modifier is used to prevent derivation from a class. Ein Kompilierzeitfehler tritt auf, wenn eine versiegelte Klasse als Basisklasse einer anderen Klasse angegeben wird.A compile-time error occurs if a sealed class is specified as the base class of another class.

Eine versiegelte Klasse kann nicht auch eine abstrakte Klasse sein.A sealed class cannot also be an abstract class.

Der sealed -Modifizierer wird hauptsächlich verwendet, um eine unbeabsichtigte Ableitung zu verhindern, er ermöglicht aber auch bestimmte Lauf Zeit Optimierungen.The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. Insbesondere weil eine versiegelte Klasse bekanntermaßen keine abgeleiteten Klassen hat, ist es möglich, die Aufrufe virtueller Funktionsmember für versiegelte Klassen Instanzen in nicht virtuelle Aufrufe umzuwandeln.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.

Statische KlassenStatic classes

Der static -Modifizierer wird verwendet, um die Klasse zu markieren, die als statische Klassedeklariert wird.The static modifier is used to mark the class being declared as a static class. Eine statische Klasse kann nicht instanziiert werden, kann nicht als Typ verwendet werden und darf nur statische Member enthalten.A static class cannot be instantiated, cannot be used as a type and can contain only static members. Nur eine statische Klasse kann Deklarationen von Erweiterungs Methoden (Erweiterungs Methoden) enthalten.Only a static class can contain declarations of extension methods (Extension methods).

Eine statische Klassen Deklaration unterliegt den folgenden Einschränkungen:A static class declaration is subject to the following restrictions:

  • Eine statische Klasse darf keinen-Modifizierer sealed oder abstract -Modifizierer enthalten.A static class may not include a sealed or abstract modifier. Beachten Sie jedoch, dass eine statische Klasse, die nicht von instanziiert oder abgeleitet werden kann, so verhält, als ob Sie sowohl versiegelt als auch abstrakt wäre.Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • Eine statische Klasse darf keine class_base Specification (Klassenbasis Spezifikation) enthalten und kann weder eine Basisklasse noch eine Liste implementierter Schnittstellen explizit angeben.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. Eine statische Klasse erbt implizit vom Typ object.A static class implicitly inherits from type object.
  • Eine statische Klasse kann nur statische Member (statische Member und Instanzmember) enthalten.A static class can only contain static members (Static and instance members). Beachten Sie, dass Konstanten und Untertypen als statische Member klassifiziert werden.Note that constants and nested types are classified as static members.
  • Eine statische Klasse kann keine Member mit protected oder protected internal deklarierter Barrierefreiheit haben.A static class cannot have members with protected or protected internal declared accessibility.

Es handelt sich um einen Kompilierzeitfehler, der gegen diese Einschränkungen verstößt.It is a compile-time error to violate any of these restrictions.

Eine statische Klasse hat keine Instanzkonstruktoren.A static class has no instance constructors. Es ist nicht möglich, einen Instanzkonstruktor in einer statischen Klasse zu deklarieren, und für eine statische Klasse wird kein Standardinstanzkonstruktor (Standardkonstruktoren) bereitgestellt.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.

Die Member einer statischen Klasse sind nicht automatisch statisch, und die Element Deklarationen müssen explizit einen static Modifizierer einschließen (mit Ausnahme von Konstanten und Typen).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). Wenn eine Klasse in einer statischen äußeren Klasse geschachtelt ist, ist die geschachtelte Klasse keine statische Klasse, es sei denn static , Sie enthält explizit einen Modifizierer.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.

Verweisen auf statische KlassentypenReferencing static class types

Ein namespace_or_type_name (Namespace und Typnamen) darf auf eine statische Klasse verweisen, wennA namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • Namespace_or_type_name ist der T in einem namespace_or_type_name -Format T.I oderThe namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • Namespace_or_type_name ist der T in einer typeof_expression (Argument Liste1) der Form typeof(T).The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

Ein primary_expression (Funktionsmember) darf auf eine statische Klasse verweisen, wennA primary_expression (Function members) is permitted to reference a static class if

In jedem anderen Kontext ist es ein Kompilierzeitfehler, um auf eine statische Klasse zu verweisen.In any other context it is a compile-time error to reference a static class. Es ist z. b. ein Fehler für eine statische Klasse, die als Basisklasse, als konstituierender Typ (in Form vonTypen) eines Members, als generisches Typargument oder als Typparameter Einschränkung verwendet werden soll.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. Ebenso kann eine statische Klasse nicht in einem Arraytyp, einem Zeigertyp, einem new Ausdruck, einem Umwandlungs Ausdruck, is einem Ausdruck, as einem Ausdruck, sizeof einem Ausdruck oder einem Standardwert Ausdruck verwendet werden.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.

Partieller ModifiziererPartial modifier

Der partial-Modifizierer wird verwendet, um anzugeben, dass dieses class_declaration eine partielle Typdeklaration ist.The partial modifier is used to indicate that this class_declaration is a partial type declaration. Mehrere partielle Typdeklarationen mit demselben Namen innerhalb eines einschließenden Namespace oder einer Typdeklaration kombinieren eine Typdeklaration, die den in partiellen Typenangegebenen Regeln folgt.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.

Die Deklaration einer Klasse, die über separate Segmente von Programmtext verteilt ist, kann nützlich sein, wenn diese Segmente in verschiedenen Kontexten erstellt oder verwaltet werden.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. Beispielsweise kann ein Teil einer Klassen Deklaration maschinell generiert werden, während der andere manuell erstellt wird.For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. Die Text Trennung der beiden verhindert, dass Updates durch eine in Konflikt mit Updates durch die andere verursacht werden.Textual separation of the two prevents updates by one from conflicting with updates by the other.

TypparameterType parameters

Ein Typparameter ist ein einfacher Bezeichner, der einen Platzhalter für ein Typargument angibt, das zum Erstellen eines konstruierten Typs bereitgestellt wird.A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. Ein Typparameter ist ein formaler Platzhalter für einen Typ, der später bereitgestellt wird.A type parameter is a formal placeholder for a type that will be supplied later. Im Gegensatz dazu ist einTypargument (Typargumente) der tatsächliche Typ, der beim Erstellen eines konstruierten Typs den Typparameter ersetzt.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
    ;

Jeder Typparameter in einer Klassen Deklaration definiert einen Namen im Deklarations Raum (Deklarationen) dieser Klasse.Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. Daher kann er nicht denselben Namen wie ein anderer Typparameter oder ein Member haben, der in dieser Klasse deklariert ist.Thus, it cannot have the same name as another type parameter or a member declared in that class. Ein Typparameter kann nicht den gleichen Namen haben wie der Typ selbst.A type parameter cannot have the same name as the type itself.

Klassenbasis SpezifikationClass base specification

Eine Klassen Deklaration kann eine class_base -Spezifikation enthalten, die die direkte Basisklasse der Klasse und die Schnittstellen (Schnittstellen) definiert, die von der-Klasse direkt implementiert werden.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)*
    ;

Die in einer Klassen Deklaration angegebene Basisklasse kann ein konstruierter Klassentyp (konstruierte Typen) sein.The base class specified in a class declaration can be a constructed class type (Constructed types). Eine Basisklasse kann nicht eigenständig ein Typparameter sein, Sie kann jedoch die Typparameter enthalten, die sich im Gültigkeitsbereich befinden.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

BasisklassenBase classes

Wenn ein class_type in der class_baseenthalten ist, gibt es die direkte Basisklasse der Klasse an, die deklariert wird.When a class_type is included in the class_base, it specifies the direct base class of the class being declared. Wenn eine Klassen Deklaration keine class_basehat oder wenn die class_base nur Schnittstellentypen auflistet, wird davon ausgegangen, dass die direkte Basisklasse object ist.If 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. Eine Klasse erbt Member von ihrer direkten Basisklasse, wie in Vererbungbeschrieben.A class inherits members from its direct base class, as described in Inheritance.

Im BeispielIn the example

class A {}

class B: A {}

die A Klasse ist die direkte Basisklasse von B, und B wird als abgeleitet Abezeichnet.class A is said to be the direct base class of B, and B is said to be derived from A. Da A nicht explizit eine direkte Basisklasse angibt, ist die direkte Basisklasse implizit object.Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

Wenn eine Basisklasse für einen konstruierten Klassentyp in der Deklaration der generischen Klasse angegeben wird, wird die Basisklasse des konstruierten Typs abgerufen, indem für jede type_parameter in der Basisklassen Deklaration der entsprechende type_argument des konstruierten Typs.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. Bei Angabe der generischen Klassen DeklarationenGiven the generic class declarations

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

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

die Basisklasse des konstruierten Typs G<int> B<string,int[]>wäre.the base class of the constructed type G<int> would be B<string,int[]>.

Die direkte Basisklasse eines Klassen Typs muss mindestens so zugänglich sein wie der Klassentyp selbst (Barrierefreiheits Domänen).The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). Beispielsweise ist es ein Kompilierzeitfehler für eine public Klasse, die von einer private -Klasse internal oder-Klasse abgeleitet werden soll.For example, it is a compile-time error for a public class to derive from a private or internal class.

Die direkte Basisklasse eines Klassen Typs darf keinem der folgenden Typen sein: System.Array, System.Delegate, System.MulticastDelegate, System.Enumoder System.ValueType.The 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. Darüber hinaus kann eine generische Klassen Deklaration System.Attribute nicht als direkte oder indirekte Basisklasse verwenden.Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

Bei der Bestimmung der Bedeutung der direkten Basisklassen Spezifikation A einer Klasse Bwird die direkte Basisklasse von B vorübergehend objectals festgelegt.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. Intuitiv wird dadurch sichergestellt, dass die Bedeutung einer Basisklassen Spezifikation nicht rekursiv von sich selbst abhängig ist.Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. Das Beispiel:The example:

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

class C : A<C.B> {}

A<C.B> ist fehlerhaft object, da in der Basisklassen Spezifikation die direkte Basisklasse C von als betrachtet wird und daher (durch die Regeln von Namespace-und Typnamen) C nicht als Member Bangesehenwird.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.

Die Basisklassen eines Klassen Typs sind die direkte Basisklasse und deren Basisklassen.The base classes of a class type are the direct base class and its base classes. Mit anderen Worten: der Satz von Basisklassen ist der transitiv Abschluss der direkten Basisklassen Beziehung.In other words, the set of base classes is the transitive closure of the direct base class relationship. Im obigen Beispiel sind B A die Basisklassen von und object.Referring to the example above, the base classes of B are A and object. Im BeispielIn the example

class A {...}

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

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

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

die Basisklassen von D<int> sind C<int[]>, B<IComparable<int[]>>, Aund. objectthe base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

Mit Ausnahme von objectClass hat jeder Klassentyp genau eine direkte Basisklasse.Except for class object, every class type has exactly one direct base class. Die object Klasse verfügt über keine direkte Basisklasse und ist die ultimative Basisklasse aller anderen Klassen.The object class has no direct base class and is the ultimate base class of all other classes.

Wenn eine Klasse B von einer Klasse Aabgeleitet ist, ist dies ein A Kompilier BZeitfehler, von dem abhängig ist.When a class B derives from a class A, it is a compile-time error for A to depend on B. Eine Klasse hängt direkt von ihrer direkten Basisklasse (sofern vorhanden) ab und hängt direkt von der Klasse ab, in der Sie sofort geschachtelt ist (sofern vorhanden).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). Bei dieser Definition ist der gesamte Satz von Klassen, von dem eine Klasse abhängt, die reflexive und transitiv Schließung der direkt von Beziehung abhängig.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.

Das BeispielThe example

class A: A {}

ist fehlerhaft, da die-Klasse von sich selbst abhängt.is erroneous because the class depends on itself. Ebenso ist das BeispielLikewise, the example

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

ist fehlerhaft, da die Klassen zirkulär von sich selbst abhängen.is in error because the classes circularly depend on themselves. Abschließend wird das BeispielFinally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

A führt zu B.C einem Kompilierzeitfehler, da von (seiner direkten Basisklasse B ) abhängt, das von (seiner unmittelbar einschließenden AKlasse) abhängt, von dem zirkulär abhängig ist.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.

Beachten Sie, dass eine Klasse nicht von den Klassen abhängig ist, die darin geschachtelt sind.Note that a class does not depend on the classes that are nested within it. Im BeispielIn the example

class A
{
    class B: A {}
}

B``B B A hängt von ab A(da sowohl die direkte Basisklasse als auch die unmittelbar einschließende Klasse ist), aber nicht von abhängt (da weder eine Basisklasse noch eine einschließende Klasse von ist). 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). Daher ist das Beispiel gültig.Thus, the example is valid.

Es ist nicht möglich, von einer sealed Klasse abzuleiten.It is not possible to derive from a sealed class. Im BeispielIn the example

sealed class A {}

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

die B Klasse ist fehlerhaft, da Sie versucht, von sealed der A-Klasse abzuleiten.class B is in error because it attempts to derive from the sealed class A.

SchnittstellenimplementierungenInterface implementations

Eine class_base -Spezifikation kann eine Liste von Schnittstellentypen enthalten. in diesem Fall wird die Klasse so genannte, dass die angegebenen Schnittstellentypen direkt implementiert werden.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. Schnittstellen Implementierungen werden in Schnittstellen Implementierungenausführlicher erläutert.Interface implementations are discussed further in Interface implementations.

Typparameter EinschränkungenType parameter constraints

Generische Typ-und Methoden Deklarationen können optional Typparameter Einschränkungen durch Einschließen von type_parameter_constraints_clauses angeben.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' '(' ')'
    ;

Jede type_parameter_constraints_clause besteht aus dem Token where, gefolgt vom Namen eines Typparameters, gefolgt von einem Doppelpunkt und der Liste der Einschränkungen für diesen Typparameter.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. Für jeden Typparameter kann höchstens where eine Klausel vorhanden sein, und die where Klauseln können in beliebiger Reihenfolge aufgelistet werden.There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Wie das get - set Token und das-Token in einem Eigenschaften where Accessor ist das Token kein Schlüsselwort.Like the get and set tokens in a property accessor, the where token is not a keyword.

Die Liste der Einschränkungen, die in where einer-Klausel angegeben werden, kann eine der folgenden Komponenten in dieser Reihenfolge enthalten: eine einzelne primäre Einschränkung, eine oder mehrere sekundäre Einschränkungen und die new()Konstruktoreinschränkung.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().

Eine primäre Einschränkung kann ein Klassentyp oder eine Verweistyp Einschränkung class oder die Werttyp Einschränkung structsein.A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. Eine sekundäre Einschränkung kann ein type_parameter oder INTERFACE_TYPEsein.A secondary constraint can be a type_parameter or interface_type.

Die Verweistyp Einschränkung gibt an, dass ein für den Typparameter verwendetes Typargument ein Verweistyp sein muss.The reference type constraint specifies that a type argument used for the type parameter must be a reference type. Alle Klassentypen, Schnittstellentypen, Delegattypen, Array Typen und Typparameter, die als Verweistyp bekannt sind (wie unten definiert), erfüllen diese Einschränkung.All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

Die Werttyp Einschränkung gibt an, dass das für den Typparameter verwendete Typargument ein Werttyp sein muss, der keine NULL-Werte zulässt.The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. Alle Strukturtypen, Enumerationstypen und Typparameter, die keine NULL-Werte zulassen und die Werttyp Einschränkung aufweisen, erfüllen diese Einschränkung.All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Beachten Sie, dass ein Typ, der NULL-Werte zulässt (Typen, dienull-Werte zulassen), nicht der Werttyp Einschränkung entspricht.Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. Ein Typparameter mit der Werttyp Einschränkung kann nicht auch über constructor_constraintverfügen.A type parameter having the value type constraint cannot also have the constructor_constraint.

Zeiger Typen dürfen nicht als Typargumente eingestuft werden und werden nicht berücksichtigt, um entweder den Verweistyp oder die Werttyp Einschränkungen zu erfüllen.Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

Wenn es sich bei einer Einschränkung um einen Klassentyp, einen Schnittstellentyp oder einen Typparameter handelt, gibt dieser Typ einen minimalen "Basistyp" an, den jedes Typargument für diesen Typparameter unterstützen muss.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. Wenn ein konstruierter Typ oder eine generische Methode verwendet wird, wird das Typargument zur Kompilierzeit mit den Einschränkungen für den Typparameter verglichen.Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. Das angegebene Typargument muss die unter " erfüllen von Einschränkungen" beschriebenen Bedingungen erfüllen.The type argument supplied must satisfy the conditions described in Satisfying constraints.

Eine class_type -Einschränkung muss die folgenden Regeln erfüllen:A class_type constraint must satisfy the following rules:

  • Der Typ muss ein Klassentyp sein.The type must be a class type.
  • Der Typ darf nicht sein sealed.The type must not be sealed.
  • Der Typ darf keinem der folgenden Typen sein System.Array:, System.Delegate, System.Enumoder System.ValueType.The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • Der Typ darf nicht sein object.The type must not be object. Da alle Typen von objectabgeleitet sind, hätte eine solche Einschränkung keine Auswirkung, wenn Sie zulässig wäre.Because all types derive from object, such a constraint would have no effect if it were permitted.
  • Höchstens eine Einschränkung für einen angegebenen Typparameter kann ein Klassentyp sein.At most one constraint for a given type parameter can be a class type.

Ein Typ, der als INTERFACE_TYPE -Einschränkung angegeben ist, muss die folgenden Regeln erfüllen:A type specified as an interface_type constraint must satisfy the following rules:

  • Der Typ muss ein Schnittstellentyp sein.The type must be an interface type.
  • Ein Typ darf in einer gegebenen where Klausel nicht mehrmals angegeben werden.A type must not be specified more than once in a given where clause.

In beiden Fällen kann die Einschränkung einen der Typparameter der zugeordneten Typ-oder Methoden Deklaration als Teil eines konstruierten Typs einschließen und den Typ einschließen, der deklariert wird.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.

Jeder Klassen-oder Schnittstellentyp, der als Typparameter Einschränkung angegeben ist, muss mindestens so zugänglich sein (BarrierefreiheitsEinschränkungen) wie der generische Typ oder die Methode, der deklariert wird.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.

Ein Typ, der als type_parameter -Einschränkung angegeben ist, muss die folgenden Regeln erfüllen:A type specified as a type_parameter constraint must satisfy the following rules:

  • Der Typ muss ein Typparameter sein.The type must be a type parameter.
  • Ein Typ darf in einer gegebenen where Klausel nicht mehrmals angegeben werden.A type must not be specified more than once in a given where clause.

Außerdem dürfen im Abhängigkeits Diagramm der Typparameter keine Zyklen vorhanden sein, bei denen die Abhängigkeit eine transitiv Beziehung ist, die durch definiert wird:In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

  • Wenn ein Typparameter T als Einschränkung für den S Typparameter S verwendet wird, hängt von ab T.If a type parameter T is used as a constraint for type parameter S then S depends on T.
  • Wenn ein Typparameter S von einem Typparameter T abhängt und T von einem Typparameter U U S abhängt, hängt von ab .If a type parameter S depends on a type parameter T and T depends on a type parameter U then S depends on U.

Bei dieser Beziehung handelt es sich um einen Kompilierzeitfehler für einen Typparameter, der direkt oder indirekt von sich selbst abhängig ist.Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Alle Einschränkungen müssen zwischen abhängigen Typparametern einheitlich sein.Any constraints must be consistent among dependent type parameters. Wenn der Typparameter S vom Typparameter T abhängt, dann:If type parameter S depends on type parameter T then:

  • Tdarf nicht über die Werttyp Einschränkung verfügen.T must not have the value type constraint. Andernfalls ist tatsächlich versiegelt, sodass S gezwungen wird, denselben Typ wie Tzu haben, sodass zwei Typparameter nicht mehr benötigt werden. TOtherwise, T is effectively sealed so S would be forced to be the same type as T, eliminating the need for two type parameters.
  • Wenn S die Werttyp Einschränkung aufweist, darf T keine class_type -Einschränkung aufweisen.If S has the value type constraint then T must not have a class_type constraint.
  • Wenn S über eine class_type -Einschränkung verfügt A und T eine class_type -Einschränkung B, muss eine Identitäts Konvertierung oder eine implizite Verweis Konvertierung von A in B oder eine implizite Verweis Konvertierung von B auf A.If 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.
  • Wenn S auch vom Typparameter U und U über eine class_type -Einschränkung verfügt A und T eine class_type -Einschränkung B, muss eine Identitäts Konvertierung oder eine implizite Verweis Konvertierung von A erfolgen. zum B oder eine implizite Verweis Konvertierung von 0 in 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.

Es ist zulässig, S dass die Werttyp Einschränkung und T die Verweistyp Einschränkung aufweisen.It is valid for S to have the value type constraint and T to have the reference type constraint. Dies schränkt T praktisch die Typen System.Object, System.ValueType, System.Enumund alle Schnittstellentypen ein.Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

Wenn die where -Klausel für einen Typparameter eine Konstruktoreinschränkung (die new()das-Format aufweist) enthält, kann der new -Operator verwendet werden, um Instanzen des-Typs (Objekt Erstellungs Ausdrücke) zu erstellen.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). Alle Typargumente, die für einen Typparameter mit einer Konstruktoreinschränkung verwendet werden, müssen über einen öffentlichen Parameter losen Konstruktor verfügen (dieser Konstruktor ist implizit für jeden Werttyp vorhanden), oder es handelt sich um einen Typparameter mit der Werttyp Einschränkung oder Konstruktoreinschränkung (siehe Typparameter Einschränkungen für Details).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).

Im folgenden finden Sie Beispiele für Einschränkungen: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()
{
    ...
}

Das folgende Beispiel ist fehlerhaft, da es eine Zirkularität im Abhängigkeits Diagramm der Typparameter verursacht: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
{
    ...
}

In den folgenden Beispielen werden zusätzliche ungültige Situationen veranschaulicht: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
{
    ...
}

Die effektive Basisklasse eines Typparameters T wird wie folgt definiert:The effective base class of a type parameter T is defined as follows:

  • Wenn T keine Primary-Einschränkungen oder Typparameter Einschränkungen aufweist, ist objectdie effektive Basisklasse.If T has no primary constraints or type parameter constraints, its effective base class is object.
  • Wenn T die Werttyp Einschränkung aufweist, ist System.ValueTypedie effektive Basisklasse.If T has the value type constraint, its effective base class is System.ValueType.
  • Wenn T über eine class_type -Einschränkung C, aber keine type_parameter -Einschränkungen verfügt, ist die effektive Basisklasse C.If T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • Wenn T keine class_type -Einschränkung hat, aber mindestens eine type_parameter -Einschränkung aufweist, handelt es sich bei der effektiven Basisklasse um den am häufigsten als Typ (gesteigerte Konvertierungs Operator) im Satz effektiver Basisklassen der Type_ Parameter Einschränkungen.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. Durch die Konsistenzregeln wird sichergestellt, dass ein solcher Typ vorhanden ist.The consistency rules ensure that such a most encompassed type exists.
  • Wenn T sowohl eine class_type -Einschränkung als auch eine oder mehrere type_parameter -Einschränkungen aufweist, ist die effektive Basisklasse der am meisten eingeschlossenen Typ (gesteigerte Konvertierungs Operatoren) in der Menge, die aus dem class_type besteht. Einschränkung von T und den effektiven Basisklassen der type_parameter -Einschränkungen.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. Durch die Konsistenzregeln wird sichergestellt, dass ein solcher Typ vorhanden ist.The consistency rules ensure that such a most encompassed type exists.
  • Wenn T die Verweistyp Einschränkung, aber keine class_type -Einschränkungen aufweist, ist die effektive Basisklasse object.If T has the reference type constraint but no class_type constraints, its effective base class is object.

Verwenden Sie für diese Regeln stattdessen den spezifischsten Basistyp von V, bei dem es sich um eine Einschränkung V handelt, bei der es sich um eine value_type handelt.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. Dies kann in einer explizit angegebenen Einschränkung nie vorkommen, kann jedoch auftreten, wenn die Einschränkungen einer generischen Methode implizit von einer über schreibenden Methoden Deklaration oder einer expliziten Implementierung einer Schnittstellen Methode geerbt werden.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.

Diese Regeln stellen sicher, dass die effektive Basisklasse immer ein class_typeist.These rules ensure that the effective base class is always a class_type.

Der effektive Schnittstellen Satz eines Typparameters T wird wie folgt definiert:The effective interface set of a type parameter T is defined as follows:

  • Wenn T keinen secondary_constraintshat, ist der effektive Schnittstellen Satz leer.If T has no secondary_constraints, its effective interface set is empty.
  • Wenn T INTERFACE_TYPE -Einschränkungen, aber keine type_parameter -Einschränkungen aufweist, ist der effektive Schnittstellen Satz der Satz von INTERFACE_TYPE -Einschränkungen.If T has interface_type constraints but no type_parameter constraints, its effective interface set is its set of interface_type constraints.
  • Wenn T keine INTERFACE_TYPE -Einschränkungen aufweist, aber über type_parameter -Einschränkungen verfügt, ist der effektive Schnittstellen Satz die Vereinigung der effektiven Schnittstellen Sätze der type_parameter -Einschränkungen.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.
  • Wenn T sowohl INTERFACE_TYPE -Einschränkungen als auch type_parameter -Einschränkungen aufweist, ist der effektive Schnittstellen Satz die Kombination aus dem Satz von INTERFACE_TYPE -Einschränkungen und den effektiven Schnittstellen Sätzen seiner type_parameter Auflagen.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.

Ein Typparameter ist bekanntermaßen ein Referenztyp, wenn er über die Verweistyp Einschränkung oder seine effektive Basisklasse nicht object oder System.ValueTypeist.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.

Werte eines eingeschränkten Typparameter Typs können für den Zugriff auf die Instanzmember verwendet werden, die durch die Einschränkungen impliziert sind.Values of a constrained type parameter type can be used to access the instance members implied by the constraints. Im BeispielIn the example

interface IPrintable
{
    void Print();
}

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

die-Methoden IPrintable von können direkt auf x aufgerufen werden T , da eingeschränkt ist, IPrintableum immer zu implementieren.the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Klassen TextClass body

Der class_body einer Klasse definiert die Member dieser Klasse.The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

Partial types (Partielle Typen)Partial types

Eine Typdeklaration kann über mehrere partielle Typdeklarationenhinweg aufgeteilt werden.A type declaration can be split across multiple partial type declarations. Die Typdeklaration wird anhand der in diesem Abschnitt aufgeführten Regeln erstellt, woraufhin Sie im Rest der Kompilierzeit-und Lauf Zeit Verarbeitung des Programms als eine einzige Deklaration behandelt wird.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.

Eine class_declaration, struct_declaration oder interface_declaration stellt eine partielle Typdeklaration dar, wenn Sie einen partial-Modifizierer enthält.A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partialist kein Schlüsselwort und fungiert nur als Modifizierer, wenn er unmittelbar vor classeinem der Schlüsselwörter struct oder interface in einer Typdeklaration oder vor dem Typ void in einer Methoden Deklaration angezeigt wird.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 anderen Kontexten kann es als normaler Bezeichner verwendet werden.In other contexts it can be used as a normal identifier.

Jeder Teil einer partiellen Typdeklaration muss einen partial Modifizierer enthalten.Each part of a partial type declaration must include a partial modifier. Er muss denselben Namen haben und in derselben Namespace-oder Typdeklaration wie die anderen Teile deklariert werden.It must have the same name and be declared in the same namespace or type declaration as the other parts. Der partial -Modifizierer gibt an, dass zusätzliche Teile der Typdeklaration an anderer Stelle vorhanden sein können, aber das vorhanden sein solcher zusätzlicher Teile ist nicht erforderlich. es ist für einen Typ mit einer partial einzelnen Deklaration gültig, den Modifizierer einzuschließen.The 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.

Alle Teile eines partiellen Typs müssen zusammen kompiliert werden, sodass die Teile zur Kompilierzeit in eine einzelne Typdeklaration zusammengeführt werden können.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. Bei partiellen Typen können nicht bereits kompilierte Typen erweitert werden.Partial types specifically do not allow already compiled types to be extended.

Mit dem-Modifizierer können mit dem partial -Modifizierer in mehreren Teilen deklarierte Typen deklariert werden.Nested types may be declared in multiple parts by using the partial modifier. In der Regel wird der enthaltende Typ partial auch mit deklariert, und jeder Teil des untergeordneten Typs wird in einem anderen Teil des enthaltenden Typs deklariert.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.

Der partial -Modifizierer ist in Delegaten-oder Enumerationsdeklarationen unzulässig.The partial modifier is not permitted on delegate or enum declarations.

AttributeAttributes

Die Attribute eines partiellen Typs werden festgelegt, indem die Attribute der einzelnen Teile in einer nicht angegebenen Reihenfolge kombiniert werden.The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. Wenn ein Attribut in mehreren Teilen platziert wird, entspricht es dem mehrfachen angeben des Attributs für den Typ.If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. Die beiden Teile sind z. b.:For example, the two parts:

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

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

Äquivalent zu einer Deklaration, z. b.:are equivalent to a declaration such as:

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

Attribute für Typparameter werden auf ähnliche Weise kombiniert.Attributes on type parameters combine in a similar fashion.

ModifiziererModifiers

Wenn eine partielle Typdeklaration eine Barrierefreiheits Spezifikation public( protecteddie internalModifizierer,, und private ) enthält, muss Sie mit allen anderen Teilen übereinstimmen, die eine Barrierefreiheits Spezifikation enthalten.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. Wenn kein Teil eines partiellen Typs eine Barrierefreiheits Spezifikation enthält, erhält der Typ die entsprechende Standard Barrierefreiheit (alsBarrierefreiheit deklariert).If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

Wenn eine oder mehrere partielle Deklarationen eines in einem Typ geänderten Typs einen new Modifizierer enthalten, wird keine Warnung ausgegeben, wenn der Typ eines geerbten Members (durch Vererbungausblenden) ausgeblendet wird.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).

Wenn eine oder mehrere partielle Deklarationen einer Klasse einen abstract Modifizierer enthalten, gilt die Klasse als abstrakt (abstrakte Klassen).If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). Andernfalls gilt die Klasse als nicht abstrakt.Otherwise, the class is considered non-abstract.

Wenn mindestens eine partielle Deklaration einer Klasse einen sealed Modifizierer enthält, gilt die Klasse als versiegelt (versiegelte Klassen).If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). Andernfalls wird die Klasse als nicht versiegelt angesehen.Otherwise, the class is considered unsealed.

Beachten Sie, dass eine Klasse nicht gleichzeitig abstrakt und versiegelt sein kann.Note that a class cannot be both abstract and sealed.

Wenn der unsafe -Modifizierer für eine partielle Typdeklaration verwendet wird, wird nur dieser bestimmte Teil als unsicherer Kontext (unsichere Kontexte) betrachtet.When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (Unsafe contexts).

Typparameter und EinschränkungenType parameters and constraints

Wenn ein generischer Typ in mehreren Teilen deklariert ist, muss jeder Teil die Typparameter angeben.If a generic type is declared in multiple parts, each part must state the type parameters. Jeder Teil muss die gleiche Anzahl von Typparametern und den gleichen Namen für jeden Typparameter in der richtigen Reihenfolge aufweisen.Each part must have the same number of type parameters, and the same name for each type parameter, in order.

Wenn eine partielle generische Typdeklaration Einschränkungenwhere (Klauseln) enthält, müssen die Einschränkungen allen anderen Teilen, die Einschränkungen einschließen, zustimmen.When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. Insbesondere müssen alle Teile, die Einschränkungen enthalten, Einschränkungen für denselben Satz von Typparametern aufweisen, und für jeden Typparameter müssen die Sätze der primären, sekundären und Konstruktoreinschränkungen gleichwertig sein.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. Zwei Sätze von Einschränkungen sind äquivalent, wenn Sie dieselben Member enthalten.Two sets of constraints are equivalent if they contain the same members. Wenn kein Teil eines partiellen generischen Typs Typparameter Einschränkungen angibt, gelten die Typparameter als nicht eingeschränkt.If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

Das BeispielThe 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>
{
    ...
}

ist richtig, da diese Teile, die Einschränkungen enthalten (die ersten beiden), effektiv denselben Satz von primären, sekundären und Konstruktoreinschränkungen für denselben Satz von Typparametern angeben.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.

BasisklasseBase class

Wenn eine partielle Klassen Deklaration eine Basisklassen Spezifikation enthält, muss Sie mit allen anderen Teilen übereinstimmen, die eine Basisklassen Spezifikation enthalten.When a partial class declaration includes a base class specification it must agree with all other parts that include a base class specification. Wenn kein Teil einer partiellen Klasse eine Basisklassen Spezifikation enthält, wird die Basisklasse System.Object zu (Basisklassen).If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

Basis SchnittstellenBase interfaces

Der Satz von Basis Schnittstellen für einen in mehreren Teilen deklarierten Typ ist die Vereinigung der Basis Schnittstellen, die für jeden Teil angegeben werden.The set of base interfaces for a type declared in multiple parts is the union of the base interfaces specified on each part. Eine bestimmte Basisschnittstelle kann nur einmal pro Teil benannt werden, es ist jedoch zulässig, dass mehrere Teile die gleichen Basis Schnittstellen benennen.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). Es darf nur eine Implementierung der Member einer bestimmten Basisschnittstelle vorhanden sein.There must only be one implementation of the members of any given base interface.

Im BeispielIn the example

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

partial class C: IC {...}

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

der Satz von Basis Schnittstellen für die C - IAKlasse IBist, ICund.the set of base interfaces for class C is IA, IB, and IC.

In der Regel stellt jeder Teil eine Implementierung der Schnittstellen bereit, die für diesen Teil deklariert werden. Dies ist jedoch keine Voraussetzung.Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. Ein Teil kann die Implementierung für eine Schnittstelle bereitstellen, die für einen anderen Teil deklariert wurde: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
{
    ...
}

MemberMembers

Mit Ausnahme von partiellen Methoden (partielle Methoden) ist der Satz von Membern eines Typs, der in mehreren Teilen deklariert wurde, einfach die Vereinigung der Menge von Membern, die in jedem Teil deklariert werden.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. Die Texte aller Teile der Typdeklaration verwenden denselben Deklarations Bereich (Deklarationen), und derGültigkeitsBereich der einzelnen Member (Bereiche) erstreckt sich auf den Text aller Teile.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. Die Zugriffs Domäne eines beliebigen Members enthält immer alle Teile des einschließenden Typs. ein private Member, der in einem Teil deklariert ist, kann aus einem anderen Teil frei zugänglich sein.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. Es handelt sich um einen Kompilierzeitfehler, um denselben Member in mehr als einem Teil des Typs zu deklarieren, es sei denn, dieser partial Member ist ein Typ mit dem-Modifizierer.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;
    }
}

Die Reihenfolge von Membern innerhalb eines Typs ist nur C# selten wichtig für Code, kann jedoch bei der Schnittstelle mit anderen Sprachen und Umgebungen von Bedeutung sein.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 diesen Fällen ist die Reihenfolge von Membern innerhalb eines in mehreren Teilen deklarierten Typs nicht definiert.In these cases, the ordering of members within a type declared in multiple parts is undefined.

Partielle MethodenPartial methods

Partielle Methoden können in einem Teil einer Typdeklaration definiert und in einem anderen implementiert werden.Partial methods can be defined in one part of a type declaration and implemented in another. Die Implementierung ist optional. Wenn kein Teil die partielle Methode implementiert, werden die Deklaration der partiellen Methode und alle Aufrufe an die Deklaration aus der Typdeklaration entfernt, die sich aus der Kombination der Teile ergibt.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.

Partielle Methoden können keine Zugriffsmodifizierer definieren, sondern implizit private.Partial methods cannot define access modifiers, but are implicitly private. Der Rückgabetyp voidmuss sein, und ihre Parameter dürfen out nicht den-Modifizierer aufweisen.Their return type must be void, and their parameters cannot have the out modifier. Der Bezeichner partial wird in einer Methoden Deklaration nur dann als sonderschlüsselwort erkannt, wenn void er direkt vor dem Typ angezeigt wird. andernfalls kann er als normaler Bezeichner verwendet werden.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. Eine partielle Methode kann Schnittstellen Methoden nicht explizit implementieren.A partial method cannot explicitly implement interface methods.

Es gibt zwei Arten von partiellen Methoden Deklarationen: Wenn der Text der Methoden Deklaration ein Semikolon ist, wird die Deklaration als eine definierende partielle Methoden Deklarationbezeichnet.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. Wenn der Text als- Blockangegeben wird, wird die Deklaration als eine implementierende partielle Methoden Deklarationbezeichnet.If the body is given as a block, the declaration is said to be an implementing partial method declaration. In den Teilen einer Typdeklaration darf nur eine partielle Methoden Deklaration mit einer bestimmten Signatur definiert werden, und es kann nur eine partielle Methoden Deklaration mit einer bestimmten Signatur implementiert werden.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. Wenn eine implementierende partielle Methoden Deklaration angegeben wird, muss eine entsprechende definierende partielle Methoden Deklaration vorhanden sein, und die Deklarationen müssen übereinstimmen, wie im folgenden angegeben: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:

  • Die Deklarationen müssen die gleichen modifiziererer (auch nicht unbedingt in derselben Reihenfolge), den Methodennamen, die Anzahl der Typparameter und die Anzahl von Parametern aufweisen.The declarations must have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters.
  • Die entsprechenden Parameter in den Deklarationen müssen dieselben Modifizierer aufweisen (obwohl Sie nicht notwendigerweise in derselben Reihenfolge sind) und dieselben Typen (Modulo-Unterschiede in Typparameter Namen).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).
  • Die entsprechenden Typparameter in den Deklarationen müssen dieselben Einschränkungen aufweisen (Modulo-Unterschiede in Typparameter Namen).Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

Eine implementierende partielle Methoden Deklaration kann im gleichen Teil wie die entsprechende definierende partielle Methoden Deklaration vorkommen.An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

Nur eine definierende partielle Methode ist an der Überladungs Auflösung beteiligt.Only a defining partial method participates in overload resolution. Unabhängig davon, ob eine implementierende Deklaration angegeben wird, können Aufruf Ausdrücke in Aufrufe der partiellen Methode aufgelöst werden.Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Da eine partielle Methode immer voidzurückgibt, sind solche Aufruf Ausdrücke immer Ausdrucks Anweisungen.Because a partial method always returns void, such invocation expressions will always be expression statements. Da eine partielle Methode implizit privateist, treten diese Anweisungen immer innerhalb eines der Teile der Typdeklaration auf, in der die partielle Methode deklariert ist.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.

Wenn kein Teil einer partiellen Typdeklaration eine implementierende Deklaration für eine bestimmte partielle Methode enthält, wird jede Ausdrucks Anweisung, die Sie aufruft, einfach aus der kombinierten Typdeklaration entfernt.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. Folglich hat der Aufruf Ausdruck, einschließlich aller konstituierender Ausdrücke, keine Auswirkung zur Laufzeit.Thus the invocation expression, including any constituent expressions, has no effect at run-time. Die partielle Methode selbst wird ebenfalls entfernt und ist kein Member der kombinierten Typdeklaration.The partial method itself is also removed and will not be a member of the combined type declaration.

Wenn eine implementierende Deklaration für eine bestimmte partielle Methode vorhanden ist, werden die Aufrufe der partiellen Methoden beibehalten.If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. Die partielle Methode führt zu einer Methoden Deklaration, die der Implementierung der partiellen Methoden Deklaration ähnelt, mit Ausnahme folgender:The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • Der partial -Modifizierer ist nicht eingeschlossen.The partial modifier is not included
  • Die Attribute in der resultierenden Methoden Deklaration sind die kombinierten Attribute der definierenden und der implementierenden partiellen Methoden Deklaration in einer nicht angegebenen Reihenfolge.The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. Duplikate werden nicht entfernt.Duplicates are not removed.
  • Die Attribute für die Parameter der resultierenden Methoden Deklaration sind die kombinierten Attribute der entsprechenden Parameter der definierenden und der implementierenden partiellen Methoden Deklaration in einer nicht angegebenen Reihenfolge.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. Duplikate werden nicht entfernt.Duplicates are not removed.

Wenn eine definierende Deklaration, aber keine implementierende Deklaration für eine partielle Methode M angegeben wird, gelten die folgenden Einschränkungen:If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

Partielle Methoden sind hilfreich, um einem Teil einer Typdeklaration das Anpassen des Verhaltens eines anderen Teils zu ermöglichen, z. b. eines, das von einem Tool generiert wird.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. Beachten Sie die folgende Deklaration der partiellen Klasse: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();
}

Wenn diese Klasse ohne andere Teile kompiliert wird, werden die definierenden partiellen Methoden Deklarationen und deren Aufrufe entfernt, und die resultierende kombinierte Klassen Deklaration entspricht Folgendem: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; }
    }
}

Angenommen, es wird jedoch ein anderer Teil angegeben, der die Implementierung von Deklarationen der partiellen Methoden bereitstellt: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);
    }
}

Dann entspricht die resultierende kombinierte Klassen Deklaration folgendem: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);
    }
}

Namens BindungName binding

Obwohl jeder Teil eines erweiterbaren Typs innerhalb desselben Namespace deklariert werden muss, werden die Teile in der Regel in verschiedenen Namespace Deklarationen geschrieben.Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. Daher können für using jeden Teil verschiedene Direktiven (using-Direktiven) vorhanden sein.Thus, different using directives (Using directives) may be present for each part. Bei der Interpretation von einfachen Namen (Typrückschluss) innerhalb eines Teils using werden nur die Direktiven der Namespace Deklaration (en) berücksichtigt, die diesen Teil einschließen.When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. Dies kann dazu führen, dass derselbe Bezeichner in unterschiedlichen Teilen mit unterschiedlichen Bedeutungen übereinstimmen: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
    }
}

KlassenmemberClass members

Die Member einer Klasse bestehen aus den Membern, die von den class_member_declarations eingeführt wurden, und den Membern, die von der direkten Basisklasse geerbt wurden.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
    ;

Die Member eines Klassen Typs sind in die folgenden Kategorien unterteilt:The members of a class type are divided into the following categories:

  • Konstanten, die Konstante Werte darstellen, die der-Klasse (Konstanten) zugeordnet sind.Constants, which represent constant values associated with the class (Constants).
  • Felder, bei denen es sich um die Variablen der-Klasse (Felder) handelt.Fields, which are the variables of the class (Fields).
  • -Methoden, die die Berechnungen und Aktionen implementieren, die von der-Klasse ausgeführt werden können (-Methoden).Methods, which implement the computations and actions that can be performed by the class (Methods).
  • Eigenschaften, die benannte Merkmale und die Aktionen definieren, die mit dem Lesen und Schreiben dieser Eigenschaften verknüpft sind (Eigenschaften).Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • Ereignisse, die Benachrichtigungen definieren, die von der-Klasse (Ereignissen) generiert werden können.Events, which define notifications that can be generated by the class (Events).
  • Indexer, die es ermöglichen, Instanzen der Klasse auf die gleiche Weise (syntaktisch) als Arrays (Indexer) zu indizieren.Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • Operatoren, die die Ausdrucks Operatoren definieren, die auf Instanzen der-Klasse (Operatoren) angewendet werden können.Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • Instanzkonstruktoren, die die zum Initialisieren von Instanzen der-Klasse erforderlichen Aktionen implementieren (Instanzkonstruktoren).Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • Dekonstruktoren, die die auszuführenden Aktionen implementieren, bevor Instanzen der Klasse dauerhaft verworfen werden (Dekonstruktoren).Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • Statische Konstruktoren, die die zum Initialisieren der Klasse selbst erforderlichen Aktionen implementieren (statische Konstruktoren).Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • Typen, die die Typen darstellen, die für die-Klasse lokal sind (unterTypen).Types, which represent the types that are local to the class (Nested types).

Member, die ausführbaren Code enthalten können, werden zusammen mit den Funktionsmembern des Klassen Typs bezeichnet.Members that can contain executable code are collectively known as the function members of the class type. Die Funktionsmember eines Klassen Typs sind die Methoden, Eigenschaften, Ereignisse, Indexer, Operatoren, Instanzkonstruktoren, destrukturtoren und statischen Konstruktoren dieses Klassen Typs.The function members of a class type are the methods, properties, events, indexers, operators, instance constructors, destructors, and static constructors of that class type.

Ein class_declaration erstellt einen neuen Deklarations Raum (Deklarationen), und die class_member_declaration-Elemente, die direkt in der class_declaration enthalten sind, stellen neue Member in diesen Deklarations Bereich ein.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. Die folgenden Regeln gelten für class_member_declarations:The following rules apply to class_member_declarations:

  • Instanzkonstruktoren, Dekonstruktoren und statische Konstruktoren müssen den gleichen Namen wie die unmittelbar einschließende Klasse haben.Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. Alle anderen Member müssen Namen haben, die sich von dem Namen der unmittelbar einschließenden Klasse unterscheiden.All other members must have names that differ from the name of the immediately enclosing class.
  • Der Name einer Konstante, eines Felds, einer Eigenschaft, eines Ereignisses oder eines Typs muss sich von den Namen aller anderen Member unterscheiden, die in derselben Klasse deklariert sind.The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • Der Name einer Methode muss sich von den Namen aller anderen nicht-Methoden unterscheiden, die in derselben Klasse deklariert sind.The name of a method must differ from the names of all other non-methods declared in the same class. Außerdem müssen sich die Signatur (Signaturen und überladen) einer Methode von den Signaturen aller anderen Methoden unterscheiden, die in derselben Klasse deklariert sind, und zwei Methoden, die in derselben Klasse deklariert sind, dürfen keine Signaturen aufweisen, ref die sich ausschließlich durch und unterscheiden. 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.
  • Die Signatur eines Instanzkonstruktors muss sich von den Signaturen aller anderen Instanzkonstruktoren unterscheiden, die in derselben Klasse deklariert sind, und zwei in derselben Klasse deklarierte Konstruktoren verfügen möglicherweise nicht ref über outSignaturen, die sich ausschließlich durch und unterscheiden.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.
  • Die Signatur eines Indexer muss sich von den Signaturen aller anderen Indexer unterscheiden, die in derselben Klasse deklariert sind.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • Die Signatur eines Operators muss sich von den Signaturen aller anderen Operatoren unterscheiden, die in derselben Klasse deklariert sind.The signature of an operator must differ from the signatures of all other operators declared in the same class.

Die geerbten Member eines Klassen Typs (Vererbung) sind nicht Teil des Deklarations Raums einer Klasse.The inherited members of a class type (Inheritance) are not part of the declaration space of a class. Folglich kann eine abgeleitete Klasse einen Member mit demselben Namen oder derselben Signatur wie ein geerbten Member deklarieren (wodurch der geerbte Member in der Tat ausgeblendet wird).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).

Der Instanztyp.The instance type

Jede Klassen Deklaration verfügt über einen zugeordneten gebundenen Typ (gebundene und ungebundene Typen), den Instanztyp.Each class declaration has an associated bound type (Bound and unbound types), the instance type. Bei einer generischen Klassen Deklaration wird der Instanztyp durch Erstellen eines konstruierten Typs (konstruierte Typen) aus der Typdeklaration gebildet, wobei jedes der angegebenen Typargumente der entsprechende Typparameter ist.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. Da der Instanztyp die Typparameter verwendet, kann er nur dort verwendet werden, wo sich die Typparameter im Gültigkeitsbereich befinden. Das heißt, innerhalb der Klassen Deklaration.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. Der Instanztyp ist der Typ this von für Code, der innerhalb der Klassen Deklaration geschrieben wurde.The instance type is the type of this for code written inside the class declaration. Bei nicht generischen Klassen ist der Instanztyp einfach die deklarierte Klasse.For non-generic classes, the instance type is simply the declared class. Das folgende Beispiel zeigt mehrere Klassen Deklarationen zusammen mit ihren Instanztypen: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

Member konstruierter TypenMembers of constructed types

Die nicht geerbten Member eines konstruierten Typs werden abgerufen, indem für jede type_parameter in der Element Deklaration der entsprechende type_argument des konstruierten Typs ersetzt wird.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. Der Ersetzungs Vorgang basiert auf der semantischen Bedeutung von Typdeklarationen und ist nicht einfach die Text Ersetzung.The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

Beispielsweise bei der Deklaration der generischen KlasseFor 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) {...}
}

der konstruierte Typ Gen<int[],IComparable<string>> verfügt über die folgenden Member: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) {...}

Der Typ des a Members in der generischen Klassen Deklaration Gen ist das zweidimensionale Array von T, sodass der Typ des a Members im konstruierten Typ oben "zweidimensionales Array eines eindimensionalen Arrays aus "ist.int", oder 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[,][].

Innerhalb von instanzfunktionsmembern this ist der Typ von der Instanztyp (der Instanztyp) der enthaltenden Deklaration.Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

Alle Member einer generischen Klasse können Typparameter aus einer beliebigen einschließenden Klasse verwenden, entweder direkt oder als Teil eines konstruierten Typs.All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. Wenn ein bestimmter, von einem Typ geschlossenes konstruierter Typ (Open-und Closed-Typen) zur Laufzeit verwendet wird, wird jede Verwendung eines Typparameters durch das tatsächliche Typargument ersetzt, das für den konstruierten Typ angegeben wird.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. Zum Beispiel: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
    }
}

VererbungInheritance

Eine Klasse erbt die Member ihres direkten Basisklassen Typs.A class inherits the members of its direct base class type. Vererbung bedeutet, dass eine Klasse implizit alle Member ihres direkten Basisklassen Typs enthält, mit Ausnahme der Instanzkonstruktoren, Dekonstruktoren und statischer Konstruktoren der Basisklasse.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. Einige wichtige Aspekte der Vererbung sind:Some important aspects of inheritance are:

  • Vererbung ist transitiv.Inheritance is transitive. Wenn C von C A B abgeleitet ist und B vonAabgeleitet ist, erbt die in deklarierten Member sowie die in deklarierten Member. BIf 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.
  • Eine abgeleitete Klasse erweitert die direkte Basisklasse.A derived class extends its direct base class. Eine abgeleitete Klasse kann den geerbten Membern neue Member hinzufügen, aber die Definition eines geerbten Members kann nicht entfernt werden.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • Instanzkonstruktoren, destrukturtoren und statische Konstruktoren werden nicht geerbt, aber alle anderen Member sind, unabhängig von ihrer deklarierten Barrierefreiheit (Member Access).Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). Abhängig von der deklarierten Barrierefreiheit sind vererbte Member jedoch möglicherweise in einer abgeleiteten Klasse nicht zugänglich.However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • Eine abgeleitete Klasse kann vererbte Member ausblenden (durch Vererbung Ausblenden ), indem neue Member mit demselben Namen oder derselben Signatur deklariert werden.A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. Beachten Sie jedoch, dass beim Ausblenden eines geerbten Members dieser Member nicht entfernt wird – er macht diesen Member lediglich direkt über die abgeleitete Klasse zugänglich.Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • Eine Instanz einer Klasse enthält einen Satz aller Instanzfelder, die in der-Klasse und den zugehörigen Basisklassen deklariert sind, und eine implizite Konvertierung (implizite Verweis Konvertierungen) ist von einem abgeleiteten Klassentyp zu einem der zugehörigen Basisklassen Typen vorhanden.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. Folglich kann ein Verweis auf eine Instanz einer abgeleiteten Klasse als Verweis auf eine Instanz der zugehörigen Basisklassen behandelt werden.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.
  • Eine Klasse kann virtuelle Methoden, Eigenschaften und Indexer deklarieren, und abgeleitete Klassen können die Implementierung dieser Funktionsmember überschreiben.A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. Dadurch können Klassen polymorphes Verhalten darstellen, wobei die von einem Funktionselement Aufruf ausgeführten Aktionen je nach Lauf Zeittyp der Instanz variieren, durch die der Funktions Member aufgerufen wird.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.

Der geerbte Member eines konstruierten Klassen Typs sind die Member des unmittelbaren Basisklassen Typs (Basisklassen), die gefunden werden, indem die Typargumente des konstruierten Typs für jedes Vorkommen der entsprechenden Typparameter im class_base -Spezifikation.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. Diese Member werden wiederum transformiert, indem Sie für jede type_parameter in der Element Deklaration den entsprechenden type_argument der class_base -Spezifikation ersetzen.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) {...}
}

Im obigen Beispiel hat der konstruierte D<int> Typ einen nicht geerbten Member public int G(string s) , der durch das Typargument int für den Typparameter Tabgerufen wurde.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>verfügt auch über einen geerbten Member aus der BKlassen Deklaration.D<int> also has an inherited member from the class declaration B. Dieser geerbte Member wird bestimmt, indem zuerst B<int[]> der Basis Klassentyp D<int> bestimmt wird int , T indem in der Basisklassen B<T[]>Spezifikation ersetzt wird.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[]>. Anschließend int[] wird als Typargument Bfür U in public U F(long index)durch ersetzt, wodurch der geerbte Member public int[] F(long index)bereitstellt wird.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).

Der New-Modifizierer.The new modifier

Ein class_member_declaration -Element darf einen Member mit demselben Namen oder derselben Signatur wie ein geerbte Member deklarieren.A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. In diesem Fall wird der Member der abgeleiteten Klasse zum Ausblenden des Basisklassenmembers bezeichnet.When this occurs, the derived class member is said to hide the base class member. Das Ausblenden eines geerbten Members wird nicht als Fehler betrachtet, bewirkt jedoch, dass der Compiler eine Warnung ausgibt.Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. Um die Warnung zu unterdrücken, kann die Deklaration des Members der abgeleiteten Klasse einen new -Modifizierer einschließen, um anzugeben, dass der abgeleitete Member den Basismember ausblenden soll.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. Dieses Thema wird unter Ausblenden durch Vererbungausführlicher erläutert.This topic is discussed further in Hiding through inheritance.

Wenn ein new Modifizierer in einer Deklaration enthalten ist, die einen geerbten Member nicht verbirgt, wird eine Warnung für diesen Effekt ausgegeben.If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. Diese Warnung wird durch Entfernen des new -Modifizierers unterdrückt.This warning is suppressed by removing the new modifier.

Modifizierer für ZugriffeAccess modifiers

Ein class_member_declaration kann eine der fünf möglichen Arten von deklarierten Barrierefreiheits (deklarierter Barrierefreiheit) haben: public, protected internal, protected, internal oder private.A class_member_declaration can have any one of the five possible kinds of declared accessibility (Declared accessibility): public, protected internal, protected, internal, or private. Mit Ausnahme der protected internal Kombination ist es ein Kompilierzeitfehler, mehr als einen Zugriffsmodifizierer anzugeben.Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. Wenn ein class_member_declaration keine Zugriffsmodifizierer enthält, wird private angenommen.When a class_member_declaration does not include any access modifiers, private is assumed.

Konstituierende TypenConstituent types

Typen, die in der Deklaration eines Members verwendet werden, werden als konstituierende Typen dieses Members bezeichnet.Types that are used in the declaration of a member are called the constituent types of that member. Mögliche Typen sind der Typ einer Konstante, eines Felds, einer Eigenschaft, eines Ereignisses oder eines Indexers, der Rückgabetyp einer Methode oder eines Operators und die Parametertypen einer Methode, eines Indexers, eines Operators oder eines Instanzkonstruktors.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. Die einzelnen Typen eines Members müssen mindestens so zugänglich sein, wie der Member selbst (BarrierefreiheitsEinschränkungen).The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

Statische Member und InstanzmemberStatic and instance members

Member einer Klasse sind entweder statische Member oder Instanzmember.Members of a class are either static members or instance members. Im Allgemeinen ist es sinnvoll, statische Member als zu den Klassentypen und Instanzmembern gehörend zu betrachten (Instanzen von Klassentypen).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).

Wenn ein Feld, eine Methode, eine Eigenschaft, eine Ereignis-, Operator-oder Konstruktordeklaration einen static -Modifizierer enthält, wird ein statischer Member deklariert.When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. Außerdem deklariert eine Konstante oder Typdeklaration implizit einen statischen Member.In addition, a constant or type declaration implicitly declares a static member. Statische Member haben die folgenden Merkmale:Static members have the following characteristics:

  • Wenn auf einen statischen Member M in einem member_access -Element (Member Access) der Form E.M verwiesen wird, muss E einen Typ mit M bezeichnen.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. Es handelt sich um einen Kompilierzeitfehler E , mit dem eine Instanz bezeichnet wird.It is a compile-time error for E to denote an instance.
  • Ein statisches Feld identifiziert genau einen Speicherort, der von allen Instanzen eines gegebenen geschlossenen Klassen Typs freigegeben werden soll.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. Unabhängig davon, wie viele Instanzen eines bestimmten geschlossenen Klassen Typs erstellt werden, gibt es nur eine Kopie eines statischen Felds.No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.
  • Ein statisches Funktionsmember (Methode, Eigenschaft, Ereignis, Operator oder Konstruktor) funktioniert nicht für eine bestimmte Instanz, und es handelt sich um einen Kompilierzeitfehler, this auf den in einem derartigen Funktionsmember verwiesen wird.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.

Wenn ein Feld, eine Methode, eine Eigenschaft, ein Ereignis, ein Indexer, ein Konstruktor oder eine static destrukturerdeklaration keinen Modifizierer enthält, wird ein Instanzmember deklariert.When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (Ein Instanzmember wird manchmal als nicht statischer Member bezeichnet.) Instanzmember haben die folgenden Merkmale:(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • Wenn auf einen Instanzmember M in einem member_access (Member Access) der Form E.M verwiesen wird, muss E eine Instanz eines Typs mit M bezeichnen.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. Es handelt sich um einen Bindungs Zeit Fehler E , mit dem ein Typ bezeichnet wird.It is a binding-time error for E to denote a type.
  • Jede Instanz einer Klasse enthält einen separaten Satz aller Instanzfelder der Klasse.Every instance of a class contains a separate set of all instance fields of the class.
  • Ein Instanzfunktionsmember (Methode, Eigenschaft, Indexer, Instanzkonstruktor oder Dekonstruktor) arbeitet auf einer bestimmten Instanz der Klasse, und auf diese Instanz kann this als (dieser Zugriff) zugegriffen werden.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).

Das folgende Beispiel veranschaulicht die Regeln für den Zugriff auf statische Member und Instanzmember: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
    }
}

Die F-Methode zeigt, dass in einem Instanzfunktionsmember ein Simple_name (simple names) für den Zugriff auf Instanzmember und statische Member verwendet werden kann.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. Die G-Methode zeigt, dass es sich bei einem statischen Funktionsmember um einen Kompilierzeitfehler handelt, der über eine Simple_nameauf einen Instanzmember zugreifen kann.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. Die Main-Methode zeigt, dass in einem member_access (Member Access) auf Instanzmember über-Instanzen zugegriffen werden muss und auf statische Member über-Typen zugegriffen werden muss.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.

Geschachtelte TypenNested types

Ein Typ, der innerhalb einer Klassen-oder Struktur Deklaration deklariert wird, wird als geschachtelter Typbezeichnet.A type declared within a class or struct declaration is called a nested type. Ein Typ, der in einer Kompilierungseinheit oder einem Namespace deklariert wird, wird als nicht geschachtelter Typbezeichnet.A type that is declared within a compilation unit or namespace is called a non-nested type.

Im BeispielIn the example

using System;

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

die B Klasse ist ein geschachtelter Typ, da Sie innerhalb Ader Klasse deklariert A wird und die Klasse ein nicht geschachtelter Typ ist, da Sie innerhalb einer Kompilierungseinheit deklariert wird.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.

Voll qualifizierter NameFully qualified name

Der voll qualifizierte Name (vollgekennzeichnete Namen) für einen Typ ist S.N , wobei S der voll qualifizierte Name des Typs ist, in dem der N Typ deklariert ist.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.

Deklarierter ZugriffDeclared accessibility

Nicht--nicht--- public - internal ---typtypen können Barrierefreiheit haben oder deklarieren und sind internal standardmäßig alsNon-nested types can have public or internal declared accessibility and have internal declared accessibility by default. In Form von untergeordneten Typen können auch diese Formen der deklarierten Barrierefreiheit sowie eine oder mehrere zusätzliche Formen der deklarierten Barrierefreiheit enthalten sein, je nachdem, ob der enthaltende Typ eine Klasse oder Struktur ist: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:

  • Ein in einer Klasse deklarierterpublic protected, in einer Klasse deklarierter Typ kann eine beliebige von fünf Formen der deklarierten Barrierefreiheit internalaufweisen ( private, protected internal,, private oder), und wie bei anderen Klassenmembern werden standardmäßig deklarierte Barrierefreiheit.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.
  • Ein in einer Struktur deklarierter, in einer Struktur deklarierter Typ kann über eine von drei Formen derpublicdeklarierten Barrierefreiheit private(, internaloder) verfügen, und wie bei anderen private Strukturmembern werden standardmäßig deklarierte Zugriffsmöglichkeiten verwendet.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.

Das BeispielThe 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 {...} }
}

deklariert eine private, in einer NodeKlasse.declares a private nested class Node.

ZieherHiding

Ein-Typ kann einen Basismember ausblenden (Nameausblenden).A nested type may hide (Name hiding) a base member. Der new -Modifizierer ist für Klassentyp Deklarationen zulässig, damit das Ausblenden explizit ausgedrückt werden kann.The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. Das BeispielThe 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();
    }
}

zeigt eine in der Liste M definierte Klasse, die M die in Basedefinierte Methode verbirgt.shows a nested class M that hides the method M defined in Base.

Dieser Zugriffthis access

Ein-Typ und der enthaltende Typ haben keine besondere Beziehung hinsichtlich this_access (dieser Zugriff).A nested type and its containing type do not have a special relationship with regard to this_access (This access). this Insbesondere innerhalb eines geschachtelten Typs kann nicht verwendet werden, um auf Instanzmember des enthaltenden Typs zu verweisen.Specifically, this within a nested type cannot be used to refer to instance members of the containing type. In Fällen, in denen ein geclusterter Typ Zugriff auf die Instanzmember seines enthaltenden Typs benötigt, kann der this Zugriff bereitgestellt werden, indem der für die Instanz des enthaltenden Typs als Konstruktorargument für den schsted Type bereitgestellt wird.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. Im folgenden BeispielThe 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();
    }
}

zeigt diese Methode.shows this technique. Eine Instanz von C erstellt eine Instanz von Nested und übergibt ihren eigenen this an Nestedden-Konstruktor, um den nachfolgenden Zugriff Cauf die Instanzmember bereitzustellen.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.

Zugriff auf private und geschützte Member des enthaltenden TypsAccess to private and protected members of the containing type

Ein Typ, der einen Typ aufweist, kann auf alle Member zugreifen, auf die der enthaltende Typ zugreifen kann, einschließlich der Member des private enthaltenden Typs, die über die Berechtigung "Barrierefreiheit" verfügen 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. Das BeispielThe 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();
    }
}

zeigt eine Klasse C an, die eine Nested-Klasse enthält.shows a class C that contains a nested class Nested. In NestedRuft die- G Methode die statische Methode F auf, Cdie in F definiert ist, und verfügt über eine private deklarierte Barrierefreiheit.Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

Ein-Typ kann auch auf geschützte Member zugreifen, die in einem Basistyp seines enthaltenden Typs definiert sind.A nested type also may access protected members defined in a base type of its containing type. Im BeispielIn 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();
    }
}

die schsted Class Derived.Nested greift auf die geschützte F Methode zu Derived, die in der BaseBasisklasse von definiert ist, indem DerivedSie durch eine Instanz von aufruft.the nested class Derived.Nested accesses the protected method F defined in Derived's base class, Base, by calling through an instance of Derived.

Typen in generischen Klassen in generischen KlassenNested types in generic classes

Eine generische Klassen Deklaration kann eine Typdeklaration für einen Typ enthalten.A generic class declaration can contain nested type declarations. Die Typparameter der einschließenden Klasse können innerhalb der geschachtelten Typen verwendet werden.The type parameters of the enclosing class can be used within the nested types. Eine Typdeklaration in einem Typ kann zusätzliche Typparameter enthalten, die nur für den Typ "nsted" gelten.A nested type declaration can contain additional type parameters that apply only to the nested type.

Jede in einer generischen Klassen Deklaration enthaltene Typdeklaration ist implizit eine generische Typdeklaration.Every type declaration contained within a generic class declaration is implicitly a generic type declaration. Beim Schreiben eines Verweises auf einen Typ, der in einem generischen Typ geschachtelt ist, muss der enthaltende konstruierte Typ, einschließlich der Typargumente, benannt werden.When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. Allerdings kann der geschachtelte Typ innerhalb der äußeren Klasse ohne Qualifizierung verwendet werden. der Instanztyp der äußeren Klasse kann beim Konstruieren des-Typs implizit verwendet werden.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. Im folgenden Beispiel werden drei verschiedene korrekte Möglichkeiten zum Verweisen auf einen konstruierten Typ gezeigt Inner, der aus erstellt wurde. die ersten beiden sind äquivalent: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
    }
}

Obwohl es sich um einen ungültigen Programmierstil handelt, kann ein Typparameter in einem Schraffurtyp einen Member oder Typparameter ausblenden, der im äußeren Typ deklariert ist: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
    }
}

Reservierte ElementnamenReserved member names

Um die zugrunde liegende C# Lauf Zeit Implementierung zu vereinfachen, muss die Implementierung für jede Deklaration des Quell Members, bei der es sich um eine Eigenschaft, ein Ereignis oder einen Indexer handelt, zwei Methoden Signaturen reservieren, basierend auf der Art der Element Deklaration, dem Namen und dem Typ.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. Es ist ein Kompilierzeitfehler für ein Programm, einen Member zu deklarieren, dessen Signatur mit einer der reservierten Signaturen übereinstimmt, auch wenn die zugrunde liegende Lauf Zeit Implementierung diese Reservierungen nicht verwendet.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.

Die reservierten Namen führen keine Deklarationen ein, sodass Sie nicht an der Member-Suche beteiligt sind.The reserved names do not introduce declarations, thus they do not participate in member lookup. Die zugeordneten reservierten Methoden Signaturen einer Deklaration nehmen jedoch an der Vererbung (Vererbung) Teil und können mit new dem Modifizierer (dem neuen Modifizierer) ausgeblendet werden.However, a declaration's associated reserved method signatures do participate in inheritance (Inheritance), and can be hidden with the new modifier (The new modifier).

Die Reservierung dieser Namen dient drei Zwecken:The reservation of these names serves three purposes:

  • , Damit die zugrunde liegende Implementierung einen normalen Bezeichner als Methodennamen zum Abrufen oder Festlegen des Zugriffs auf die C# Sprachfunktion verwendet.To allow the underlying implementation to use an ordinary identifier as a method name for get or set access to the C# language feature.
  • , Damit andere Sprachen mithilfe eines normalen Bezeichners als Methodenname interagieren können, um den Zugriff auf die C# Sprachfunktion zu erhalten oder festzulegen.To allow other languages to interoperate using an ordinary identifier as a method name for get or set access to the C# language feature.
  • Um sicherzustellen, dass die von einem konformen Compiler akzeptierte Quelle von einer anderen akzeptiert wird, indem die Besonderheiten von reservierten Elementnamen für C# alle Implementierungen konsistent gemacht werden.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.

Die Deklaration eines destrukturtors(destrukturatoren) bewirkt auch, dass eine Signatur reserviert wird (für destrukturtoren reservierte Elementnamen).The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

Für Eigenschaften reservierte ElementnamenMember names reserved for properties

Für eine Eigenschaft P (Eigenschaften) vom Typ Tsind die folgenden Signaturen reserviert:For a property P (Properties) of type T, the following signatures are reserved:

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

Beide Signaturen sind reserviert, auch wenn die Eigenschaft schreibgeschützt oder schreibgeschützt ist.Both signatures are reserved, even if the property is read-only or write-only.

Im BeispielIn 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());
    }
}

eine Klasse A definiert eine schreibgeschützte Eigenschaft Pund reserviert somit Signaturen für get_P -und- set_P Methoden.a class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. Eine- B Klasse wird A von abgeleitet und verbirgt beide reservierten Signaturen.A class B derives from A and hides both of these reserved signatures. Das Beispiel erzeugt die Ausgabe:The example produces the output:

123
123
456

Für Ereignisse reservierte ElementnamenMember names reserved for events

Für ein Ereignis E (Ereignisse) des Delegattyps Tsind die folgenden Signaturen reserviert:For an event E (Events) of delegate type T, the following signatures are reserved:

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

Für Indexer reservierte ElementnamenMember names reserved for indexers

Für einen Indexer (Indexer) vom Typ T mit Parameter-List Lsind die folgenden Signaturen reserviert: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);

Beide Signaturen sind reserviert, auch wenn der Indexer schreibgeschützt oder schreibgeschützt ist.Both signatures are reserved, even if the indexer is read-only or write-only.

Außerdem ist der Element Item Name reserviert.Furthermore the member name Item is reserved.

Für destrukturtoren reservierte ElementnamenMember names reserved for destructors

Für eine Klasse, die einen Dekonstruktor (Dekonstruktoren) enthält, ist die folgende Signatur reserviert:For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

KonstantenConstants

Eine Konstante ist ein Klassenmember, der einen konstanten Wert darstellt: einen Wert, der zur Kompilierzeit berechnet werden kann.A constant is a class member that represents a constant value: a value that can be computed at compile-time. Ein constant_declaration führt eine oder mehrere Konstanten eines bestimmten Typs ein.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
    ;

Ein constant_declaration kann einen Satz von Attributen (Attribute), einen new-Modifizierer (den neuen Modifizierer) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer) enthalten.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). Die Attribute und Modifizierer gelten für alle Member, die von constant_declarationdeklariert werden.The attributes and modifiers apply to all of the members declared by the constant_declaration. Obwohl Konstanten als statische Member angesehen werden, ist ein constant_declaration weder erforderlich noch ein static-Modifizierer zulässig.Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. Es ist ein Fehler, dass derselbe Modifizierer mehrmals in einer Konstanten Deklaration angezeigt wird.It is an error for the same modifier to appear multiple times in a constant declaration.

Der Typ eines constant_declaration gibt den Typ der Member an, die von der Deklaration eingeführt wurden.The type of a constant_declaration specifies the type of the members introduced by the declaration. Auf den Typ folgt eine Liste von constant_declarators, von denen jeder einen neuen Member einführt.The type is followed by a list of constant_declarators, each of which introduces a new member. Ein constant_declarator besteht aus einem Bezeichner , der den Member benennt, gefolgt von einem "="-Token, gefolgt von einem constant_expression (Konstantenausdrücken), das den Wert des Members liefert.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.

Der in einer Konstanten Deklaration angegebene Typ muss sbyte, byte, short, ushort, int, uint, long, ulong, char, 0, 1, 2, 3, 4, ein enum_typeoder ein reference_ GebenSie ein.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. Jede constant_expression muss einen Wert des Zieltyps oder eines Typs liefern, der durch eine implizite Konvertierung (implizite Konvertierungen) in den Zieltyp konvertiert werden kann.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).

Der Typ einer Konstanten muss mindestens so zugänglich sein, wie die Konstante selbst (BarrierefreiheitsEinschränkungen).The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

Der Wert einer Konstanten wird in einem Ausdruck mithilfe eines Simple_name (simple names) oder eines member_access (Member Access) abgerufen.The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

Eine Konstante kann selbst an einem constant_expressionteilnehmen.A constant can itself participate in a constant_expression. Daher kann eine Konstante in jedem Konstrukt verwendet werden, das ein constant_expressionerfordert.Thus, a constant may be used in any construct that requires a constant_expression. Beispiele für derartige Konstrukte case sind Bezeichnungen goto case , Anweisungen enum , Element Deklarationen, Attribute und andere Konstante Deklarationen.Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

Wie in konstanten Ausdrückenbeschrieben, ist ein constant_expression ein Ausdruck, der zur Kompilierzeit vollständig ausgewertet werden kann.As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. Da die einzige Möglichkeit zum Erstellen eines nicht-NULL-Werts eines anderen reference_type als string ist, den new-Operator anzuwenden, und da der new-Operator in einem constant_expression-Operator nicht zulässig ist, ist der einzige mögliche Wert für Konstanten von . reference_types (außer string) ist null.Since 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.

Wenn ein symbolischer Name für einen konstanten Wert gewünscht ist, aber wenn der Typ dieses Werts in einer Konstanten Deklaration nicht zulässig ist oder wenn der Wert nicht zur Kompilierzeit von einem constant_expressionberechnet werden kann, kann ein readonly-Feld (schreibgeschützteFelder) Verwenden Sie stattdessen.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.

Eine Konstante Deklaration, die mehrere Konstanten deklariert, entspricht mehreren Deklarationen von einzelnen Konstanten mit denselben Attributen, modifizierertypen und Typen.A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Beispiel:For example

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

für die folgende Syntax:is equivalent to

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

Konstanten dürfen von anderen Konstanten innerhalb desselben Programms abhängen, solange die Abhängigkeiten nicht aus Zirkel Natur bestehen.Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. Der Compiler ordnet automatisch an, die Konstanten Deklarationen in der entsprechenden Reihenfolge auszuwerten.The compiler automatically arranges to evaluate the constant declarations in the appropriate order. Im BeispielIn 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;
}

der Compiler A.Ywertet zunächst die Werte B.Z A.X 10, 11und ausundwertetSieanschließendaus.12the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. Konstante Deklarationen können von Konstanten anderer Programme abhängen, aber solche Abhängigkeiten sind nur in einer Richtung möglich.Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. A Wenn und B.Z A.X A.Y B.Z in separaten Programmen in Bezug auf das obige Beispiel deklariert wurden, kann es möglich sein, von abhängig zu sein, aber es kann nicht gleichzeitig von abhängen. BReferring 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.

FelderFields

Ein Feld ist ein Member, der eine Variable darstellt, die einem Objekt oder einer Klasse zugeordnet ist.A field is a member that represents a variable associated with an object or class. Ein field_declaration führt ein oder mehrere Felder eines bestimmten Typs ein.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
    ;

Ein field_declaration kann einen Satz von Attributen (Attribute), einen new-Modifizierer (den neuen Modifizierer), eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer) und einen static-Modifizierer ( Statische Felder und Instanzfelder).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). Außerdem kann ein field_declaration einen readonly-Modifizierer (schreibgeschützteFelder) oder einen volatile-Modifizierer (flüchtige Felder) enthalten, aber nicht beides.In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. Die Attribute und Modifizierer gelten für alle Member, die von field_declarationdeklariert werden.The attributes and modifiers apply to all of the members declared by the field_declaration. Es ist ein Fehler, dass derselbe Modifizierer mehrmals in einer Feld Deklaration angezeigt wird.It is an error for the same modifier to appear multiple times in a field declaration.

Der Typ eines field_declaration gibt den Typ der Member an, die von der Deklaration eingeführt wurden.The type of a field_declaration specifies the type of the members introduced by the declaration. Auf den Typ folgt eine Liste von variable_declarators, von denen jeder einen neuen Member einführt.The type is followed by a list of variable_declarators, each of which introduces a new member. Ein variable_declarator besteht aus einem Bezeichner , der diesen Member benennt, optional gefolgt von einem "="-Token und einem variable_initializer (Variableninitialisierer), der den Anfangswert dieses Members enthält.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.

Der Typ eines Felds muss mindestens so zugänglich sein wie das Feld selbst (BarrierefreiheitsEinschränkungen).The type of a field must be at least as accessible as the field itself (Accessibility constraints).

Der Wert eines Felds wird in einem Ausdruck mithilfe eines Simple_name (simple names) oder eines member_access (Member Access) abgerufen.The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). Der Wert eines nicht schreibgeschützten Felds wird mithilfe einer Zuweisung (Zuweisungs Operatoren) geändert.The value of a non-readonly field is modified using an assignment (Assignment operators). Der Wert eines nicht schreibgeschützten Felds kann mit Postfix-Inkrement-und Dekrementoperatoren (postfix-Inkrement-und Dekrementoperatoren) und Präfix Inkrement-und Dekrementoperatoren (Präfix Inkrement und Dekrement) abgerufen und geändert werden. Operatoren).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).

Eine Feld Deklaration, die mehrere Felder deklariert, entspricht mehreren Deklarationen von einzelnen Feldern mit denselben Attributen, modifizierertypen und Typen.A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. Beispiel:For example

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

für die folgende Syntax:is equivalent to

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

Statische Felder und InstanzfelderStatic and instance fields

Wenn eine Feld Deklaration einen static Modifizierer enthält, sind die von der Deklaration eingeführten Felder statische Felder.When a field declaration includes a static modifier, the fields introduced by the declaration are static fields. Wenn kein static Modifizierer vorhanden ist, sind die von der Deklaration eingeführten Felder Instanzfelder.When no static modifier is present, the fields introduced by the declaration are instance fields. Statische Felder und Instanzfelder sind zwei der verschiedenen Arten von Variablen (Variablen), die C#von unterstützt werden. manchmal werden Sie als statische Variablen bzw. Instanzvariablenbezeichnet.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.

Ein statisches Feld ist nicht Teil einer bestimmten Instanz. Stattdessen wird Sie von allen Instanzen eines geschlossenen Typs (offene und geschlossene Typen) gemeinsam verwendet.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). Unabhängig davon, wie viele Instanzen eines geschlossenen Klassen Typs erstellt werden, gibt es nur eine Kopie eines statischen Felds für die zugehörige Anwendungsdomäne.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.

Zum Beispiel: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
    }
}

Ein Instanzfeld gehört zu einer Instanz.An instance field belongs to an instance. Insbesondere enthält jede Instanz einer Klasse einen separaten Satz aller Instanzfelder dieser Klasse.Specifically, every instance of a class contains a separate set of all the instance fields of that class.

Wenn auf ein Feld in einem member_access -Element (Member Access) der Form E.M verwiesen wird, wenn M ein statisches Feld ist, muss E einen Typ mit M angeben. wenn M ein Instanzfeld ist, muss E eine Instanz eines Typs angeben, der enthält. M.When 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.

Die Unterschiede zwischen statischen und Instanzmembern werden in statischen und Instanzmembernausführlicher erläutert.The differences between static and instance members are discussed further in Static and instance members.

Schreibgeschützte FelderReadonly fields

Wenn ein field_declaration einen readonly-Modifizierer enthält, sind die von der Deklaration eingeführten Felder schreibgeschützte Felder.When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Direkte Zuweisungen zu schreibgeschützten Feldern können nur als Teil der Deklaration oder in einem Instanzkonstruktor oder statischen Konstruktor in derselben Klasse erfolgen.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. (Ein Schreib geschütztes Feld kann in diesen Kontexten mehrmals zugewiesen werden.) Direkte Zuweisungen zu einem readonly Feld sind nur in den folgenden Kontexten zulässig:(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:

  • In der variable_declarator , die das Feld einführt (durch Einschließen eines variable_initializer in die Deklaration).In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • Für ein Instanzfeld in den Instanzkonstruktoren der-Klasse, die die Feld Deklaration enthält; für ein statisches Feld im statischen Konstruktor der Klasse, die die Feld Deklaration enthält.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. Dies sind auch die einzigen Kontexte, in denen es zulässig ist, ein readonly Feld out als-oder ref -Parameter zu übergeben.These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Der Versuch, einem readonly Feld zuzuweisen oder es out als-oder ref -Parameter in einem anderen Kontext zu übergeben, ist ein Kompilierzeitfehler.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.

Verwenden statischer Schreib geschützter Felder für KonstantenUsing static readonly fields for constants

Ein static readonly Feld ist nützlich, wenn ein symbolischer Name für einen konstanten Wert erwünscht ist, aber wenn der Typ des Werts nicht in einer const Deklaration zulässig ist oder wenn der Wert nicht zur Kompilierzeit berechnet werden kann.A static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration, or when the value cannot be computed at compile-time. Im BeispielIn 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;
    }
}

die BlackMember White,, Red, undkönnenBlue nicht alsconst Member deklariert werden, da ihre Werte zur Kompilierzeit nicht berechnet werden können. Greenthe Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. Das deklarieren static readonly der Dateien hat jedoch die gleiche Wirkung.However, declaring them static readonly instead has much the same effect.

Versionierung von Konstanten und statischen schreibgeschützten FeldernVersioning of constants and static readonly fields

Konstanten und schreibgeschützte Felder haben unterschiedliche Semantik für die binäre Versionsverwaltung.Constants and readonly fields have different binary versioning semantics. Wenn ein Ausdruck auf eine Konstante verweist, wird der Wert der Konstante zur Kompilierzeit abgerufen. Wenn jedoch ein Ausdruck auf ein Schreib geschütztes Feld verweist, wird der Wert des Felds bis zur Laufzeit nicht abgerufen.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. Stellen Sie sich eine Anwendung vor, die aus zwei separaten Programmen besteht: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);
        }
    }
}

Die Program1 Namespaces und Program2 bezeichnen zwei Programme, die separat kompiliert werden.The Program1 and Program2 namespaces denote two programs that are compiled separately. Da Program1.Utils.X als statisches Schreib geschütztes Feld deklariert ist, ist der Wert, der Console.WriteLine von der-Anweisung ausgegeben wird, zur Kompilierzeit nicht bekannt, sondern wird zur Laufzeit abgerufen.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. Wenn also X der Wert von geändert und Program1 neu kompiliert wird, gibt die Console.WriteLine Anweisung den neuen Wert auch dann aus, wenn Program2 nicht neu kompiliert wird.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. Program2 X Program2 Program1 War jedoch eine Konstante. der Wert von wurde zum Zeitpunkt der Kompilierung abgerufen und bleibt von Änderungen in bis zur erneuten Kompilierung nicht beeinträchtigt. XHowever, 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.

Flüchtige FelderVolatile fields

Wenn ein field_declaration einen volatile-Modifizierer enthält, sind die von dieser Deklaration eingeführten Felder flüchtige Felder.When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

Bei nicht flüchtigen Feldern können Optimierungstechniken, die Anweisungen neu anordnen, zu unerwarteten und unvorhersehbaren Ergebnissen in Multithreadprogrammen führen, die auf Felder ohne Synchronisierung zugreifen, wie z. b. die von lock_statement bereitgestellte (die Lock-Anweisung).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). Diese Optimierungen können vom Compiler, vom Laufzeitsystem oder von der Hardware ausgeführt werden.These optimizations can be performed by the compiler, by the run-time system, or by hardware. Für flüchtige Felder sind solche neubestellungs Optimierungen eingeschränkt:For volatile fields, such reordering optimizations are restricted:

  • Ein Lesevorgang eines flüchtigen Felds wird als flüchtiger LeseVorgang bezeichnet.A read of a volatile field is called a volatile read. Ein flüchtiger Lesevorgang hat "Semantik abrufen". Das heißt, es wird sichergestellt, dass es vor allen in der Anweisungs Sequenz auftretenden verweisen auf den Speicher auftritt.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.
  • Ein Schreibvorgang eines flüchtigen Felds wird als flüchtiges schreibenbezeichnet.A write of a volatile field is called a volatile write. Ein flüchtiger Schreibvorgang weist die Freigabe Semantik auf. Das heißt, es wird sichergestellt, dass es nach allen Speicher verweisen vor der Schreib Anweisung in der Anweisungs Sequenz erfolgt.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.

Diese Einschränkungen stellen sicher, dass alle Threads volatile-Schreibvorgänge, die von einem anderen Thread ausgeführt werden, in der Reihenfolge ihrer Ausführung berücksichtigen.These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. Eine konforme Implementierung ist nicht erforderlich, um eine einzelne Gesamt Reihenfolge von flüchtigen Schreibvorgängen zu gewährleisten, die von allen Ausführungs Threads erkannt werden.A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. Der Typ eines flüchtigen Felds muss einer der folgenden sein:The type of a volatile field must be one of the following:

  • Ein reference_type.A reference_type.
  • Der Typ byte, sbyte, short, ,ushort, ,float,,, oderSystem.IntPtr. char uint int bool System.UIntPtrThe type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • Ein enum_type mit dem Aufzählungs Basistyp byte, sbyte, short, ushort, int oder uint.An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

Das BeispielThe 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;
            }
        }
    }
}

erzeugt die Ausgabe:produces the output:

result = 143

In diesem Beispiel startet die- Main Methode einen neuen Thread, der die- Thread2Methode ausführt.In this example, the method Main starts a new thread that runs the method Thread2. Diese Methode speichert einen Wert in ein nicht flüchtiges Feld mit resultdem Namen und true speichert dann im flüchtigen finishedFeld.This method stores a value into a non-volatile field called result, then stores true in the volatile field finished. Der Haupt Thread wartet darauf, dass finished das Feld auf truefestgelegt wird, und liest resultdann das Feld.The main thread waits for the field finished to be set to true, then reads the field result. Da finished 143 resultdeklariert volatilewurde, muss der Haupt Thread den Wert aus dem Feld lesen.Since finished has been declared volatile, the main thread must read the value 143 from the field result. Wenn das Feld finished nicht deklariert volatilewurde, ist es result zulässig, dass der Speicher für den Haupt Thread nach dem Speichern in finishedsichtbar ist, sodass der Haupt Thread den Wert 0 aus dem ein result.If the field finished had not been declared volatile, then it would be permissible for the store to result to be visible to the main thread after the store to finished, and hence for the main thread to read the value 0 from the field result. Das finished deklarieren volatile als Feld verhindert jegliche Inkonsistenz.Declaring finished as a volatile field prevents any such inconsistency.

Feld InitialisierungField initialization

Der Anfangswert eines Felds, unabhängig davon, ob es sich um ein statisches Feld oder ein Instanzfeld handelt, ist der Standardwert (Standardwerte) des Feldtyps.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. Es ist nicht möglich, den Wert eines Felds zu beobachten, bevor diese Standard Initialisierung erfolgt ist, und ein Feld wird daher nie "nicht initialisiert".It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". Das BeispielThe 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);
    }
}

erzeugt die Ausgabeproduces the output

b = False, i = 0

, b da i und automatisch mit den Standardwerten initialisiert werden.because b and i are both automatically initialized to default values.

VariableninitialisiererVariable initializers

Feld Deklarationen können variable_initializers enthalten.Field declarations may include variable_initializers. Bei statischen Feldern entsprechen Variableninitialisierern Zuweisungs Anweisungen, die während der Initialisierung der Klasse ausgeführt werden.For static fields, variable initializers correspond to assignment statements that are executed during class initialization. Bei Instanzfeldern entsprechen Variableninitialisierern Zuweisungs Anweisungen, die ausgeführt werden, wenn eine Instanz der Klasse erstellt wird.For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

Das BeispielThe 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);
    }
}

erzeugt die Ausgabeproduces the output

x = 1.4142135623731, i = 100, s = Hello

eine Zuweisung zu erfolgt x , wenn statische Feldinitialisierer ausgeführt werden i und s Zuweisungen zu und auftreten, wenn die instanzfeldinitialisierer ausgeführt werden.because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

Die in der Feld Initialisierung beschriebene Standardwert Initialisierung tritt für alle Felder auf, einschließlich der Felder, die über Variableninitialisierer verfügen.The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. Wenn eine Klasse initialisiert wird, werden daher alle statischen Felder in dieser Klasse zuerst mit ihren Standardwerten initialisiert, und anschließend werden die statischen Feldinitialisierer in der Textfolge ausgeführt.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. Wenn eine Instanz einer Klasse erstellt wird, werden alle Instanzfelder in dieser Instanz zuerst mit ihren Standardwerten initialisiert, und anschließend werden die instanzfeldinitialisierer in der Textfolge ausgeführt.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.

Es ist möglich, dass statische Felder mit Variableninitialisierern in ihrem Standardwert Zustand beobachtet werden.It is possible for static fields with variable initializers to be observed in their default value state. Dies wird jedoch dringend von einem Stil abgeraten.However, this is strongly discouraged as a matter of style. Das BeispielThe 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);
    }
}

weist dieses Verhalten auf.exhibits this behavior. Trotz der zirkulären Definitionen von a und b ist das Programm gültig.Despite the circular definitions of a and b, the program is valid. Dadurch wird die Ausgabe ausgegeben.It results in the output

a = 1, b = 2

Da die statischen Felder a und b auf (den Standard 0 Wert für int) initialisiert werden, bevor Ihre Initialisierer ausgeführt werden.because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. Wenn der Initialisierer für a ausgeführt wird, ist der b Wert von 0 (NULL a ) und wird daher 1mit initialisiert.When the initializer for a runs, the value of b is zero, and so a is initialized to 1. Wenn der Initialisierer für b ausgeführt wird, ist der a Wert von 1bereits, und b wird daher mit 2initialisiert.When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

Initialisierung statischer FelderStatic field initialization

Die statischen feldvariableninitialisierer einer Klasse entsprechen einer Sequenz von Zuweisungen, die in der Text Reihenfolge ausgeführt werden, in der Sie in der Klassen Deklaration angezeigt werden.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. Wenn ein statischer Konstruktor (statischer Konstruktor) in der Klasse vorhanden ist, erfolgt die Ausführung der statischen Feldinitialisierer unmittelbar vor der Ausführung dieses statischen Konstruktors.If a static constructor (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Andernfalls werden die statischen Feldinitialisierer zu einem Implementierungs abhängigen Zeitpunkt vor der ersten Verwendung eines statischen Felds dieser Klasse ausgeführt.Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. Das BeispielThe 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");
}

erzeugt möglicherweise die folgende Ausgabe:might produce either the output:

Init A
Init B
1 1

oder die Ausgabe:or the output:

Init B
Init A
1 1

Da die Ausführung des XInitialisierers und Ydes Initialisierers in einer der beiden Reihenfolge auftreten kann, sind Sie nur so eingeschränkt, dass Sie vor den verweisen auf diese Felder auftreten.because the execution of X's initializer and Y's initializer could occur in either order; they are only constrained to occur before the references to those fields. Im Beispiel: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");
}

die Ausgabe muss wie folgt lauten:the output must be:

Init B
Init A
1 1

Da die Regeln für den Fall, dass statische Konstruktoren ausgeführt werden (wie in statischen Konstruktorendefiniert), den statischen Konstruktor B(und somit die statischen Feldinitialisierer) bereitstellen, Bmüssen vor Ader statischen ausgeführt werden. Konstruktor und Feldinitialisierer.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.

InstanzfeldinitialisierungInstance field initialization

Die instanzfeldvariableninitialisierer einer Klasse entsprechen einer Sequenz von Zuweisungen, die unmittelbar nach dem Eintrag für einen der Instanzkonstruktoren (Konstruktorinitialisierer) dieser Klasse ausgeführt werden.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. Die Variableninitialisierer werden in der Text Reihenfolge ausgeführt, in der Sie in der Klassen Deklaration angezeigt werden.The variable initializers are executed in the textual order in which they appear in the class declaration. Die Erstellung und Initialisierung der Klasseninstanz wird in Instanzkonstruktorenausführlicher beschrieben.The class instance creation and initialization process is described further in Instance constructors.

Ein Variableninitialisierer für ein Instanzfeld kann nicht auf die erstellte Instanz verweisen.A variable initializer for an instance field cannot reference the instance being created. Daher ist es ein Kompilierzeitfehler, auf this in einem Variableninitialisierer zu verweisen, da es sich um einen Kompilierzeitfehler für einen Variableninitialisierer handelt, der auf ein beliebiges Instanzmember über eine Simple_nameverweist.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. Im BeispielIn the example

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

der Variableninitialisierer y für führt zu einem Kompilierzeitfehler, da er auf einen Member der Instanz verweist, die erstellt wird.the variable initializer for y results in a compile-time error because it references a member of the instance being created.

MethodenMethods

Eine Methode ist ein Member, das eine Berechnung oder eine Aktion implementiert, die durch ein Objekt oder eine Klasse durchgeführt werden kann.A method is a member that implements a computation or action that can be performed by an object or class. Methoden werden mit method_declarations deklariert: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 ';'
    | ';'
    ;

Ein method_declaration kann einen Satz von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), den new (den neuen Modifizierer), static (statisch und Instanz) enthalten. Methoden), virtual (virtuelle Methoden), override (Überschreibungs Methoden), sealed (versiegelte Methoden), abstract (abstrakte Methoden) und extern-Modifizierer (Externe Methoden).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.

Eine-Deklaration hat eine gültige Kombination von Modifizierer, wenn Folgendes zutrifft:A declaration has a valid combination of modifiers if all of the following are true:

  • Die-Deklaration enthält eine gültige Kombination von zugriffsmodifizierermodifiziererThe declaration includes a valid combination of access modifiers (Access modifiers).
  • Die-Deklaration enthält nicht denselben Modifizierer mehrmals.The declaration does not include the same modifier multiple times.
  • Die-Deklaration enthält höchstens einen der folgenden Modifizierer static: virtual, und override.The declaration includes at most one of the following modifiers: static, virtual, and override.
  • Die-Deklaration enthält höchstens einen der folgenden Modifizierer new : overrideund.The declaration includes at most one of the following modifiers: new and override.
  • Wenn die Deklaration den abstract -Modifizierer enthält, enthält die Deklaration keinen der folgenden Modifizierer static: virtual, sealed oder extern.If the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • Wenn die Deklaration den private -Modifizierer enthält, enthält die Deklaration keinen der folgenden Modifizierer virtual: override, oder abstract.If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • Wenn die Deklaration den sealed -Modifizierer enthält, enthält die Deklaration auch den override -Modifizierer.If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • Wenn die Deklaration den partial -Modifizierer enthält, enthält Sie keinen der folgenden Modifizierer: new, public, protected, internal, private, virtual, sealed, override , abstractoder .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.

Eine Methode, die über async den-Modifizierer verfügt, ist eine Async-Funktion und befolgt die in Async-Funktionenbeschriebenen Regeln.A method that has the async modifier is an async function and follows the rules described in Async functions.

Der return_type einer Methoden Deklaration gibt den Typ des Werts an, der von der-Methode berechnet und zurückgegeben wird.The return_type of a method declaration specifies the type of the value computed and returned by the method. Der return_type -Wert ist void, wenn die Methode keinen Wert zurückgibt.The return_type is void if the method does not return a value. Wenn die Deklaration den partial -Modifizierer enthält, muss der Rückgabetyp sein. voidIf the declaration includes the partial modifier, then the return type must be void.

MEMBER_NAME gibt den Namen der Methode an.The member_name specifies the name of the method. Wenn die Methode keine explizite Schnittstellenmember-Implementierung ist (explizite Schnittstellenmember-Implementierungen), ist MEMBER_NAME einfach ein Bezeichner.Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. Bei einer expliziten Schnittstellenmember-Implementierung besteht das MEMBER_NAME aus einem INTERFACE_TYPE , gefolgt von einem "." und einem Bezeichner.For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

Der optionale type_parameter_list -Parameter gibt die Typparameter der Methode (Typparameter) an.The optional type_parameter_list specifies the type parameters of the method (Type parameters). Wenn type_parameter_list angegeben wird, handelt es sich bei der Methode um eine generische Methode.If a type_parameter_list is specified the method is a generic method. Wenn die Methode einen extern-Modifizierer aufweist, kann kein type_parameter_list angegeben werden.If the method has an extern modifier, a type_parameter_list cannot be specified.

Mit dem optionalen formal_parameter_list werden die Parameter der Methode (Methoden Parameter) angegeben.The optional formal_parameter_list specifies the parameters of the method (Method parameters).

Die optionalen type_parameter_constraints_clauses geben Einschränkungen für einzelne Typparameter an (Typparameter Einschränkungen) und können nur angegeben werden, wenn eine type_parameter_list ebenfalls bereitgestellt wird, und die Methode hat keine override-Modifizierer.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.

Die return_type und alle Typen, auf die im formal_parameter_list einer Methode verwiesen wird, müssen mindestens so zugänglich sein wie die Methode selbst (Barrierefreiheits Einschränkungen).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).

Der method_body ist entweder ein Semikolon, ein Anweisungs Text oder ein Ausdrucks Körper.The method_body is either a semicolon, a statement body or an expression body. Ein Anweisungs Text besteht aus einem- Block, der die auszuführenden Anweisungen angibt, wenn die-Methode aufgerufen wird.A statement body consists of a block, which specifies the statements to execute when the method is invoked. Ein Ausdrucks Körper besteht aus => , gefolgt von einem Ausdruck und einem Semikolon und deutet auf einen einzelnen Ausdruck hin, der beim Aufrufen der Methode ausgeführt werden soll.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the method is invoked.

Bei abstract-und extern-Methode besteht das method_body einfach aus einem Semikolon.For abstract and extern methods, the method_body consists simply of a semicolon. Bei partial-Methoden kann der method_body entweder aus einem Semikolon, einem Block Text oder einem Ausdrucks Körper bestehen.For partial methods the method_body may consist of either a semicolon, a block body or an expression body. Für alle anderen Methoden ist method_body entweder ein Block Körper oder ein Ausdrucks Text.For all other methods, the method_body is either a block body or an expression body.

Wenn method_body aus einem Semikolon besteht, enthält die Deklaration möglicherweise nicht den async-Modifizierer.If the method_body consists of a semicolon, then the declaration may not include the async modifier.

Der Name, die Typparameter Liste und die Liste der formalen Parameter einer Methode definieren die Signatur (Signaturen und überladen) der Methode.The name, the type parameter list and the formal parameter list of a method define the signature (Signatures and overloading) of the method. Insbesondere besteht die Signatur einer Methode aus dem Namen, der Anzahl der Typparameter und der Anzahl, den modifizierertypen und den Typen der formalen Parameter.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. Zu diesem Zweck wird jeder Typparameter der Methode, die im Typ eines formalen Parameters vorkommt, nicht anhand seines Namens identifiziert, sondern anhand seiner Ordinalposition in der Typargument Liste der Methode. Der Rückgabetyp ist weder Teil der Signatur einer Methode noch die Namen der Typparameter oder der formalen Parameter.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.

Der Name einer Methode muss sich von den Namen aller anderen nicht-Methoden unterscheiden, die in derselben Klasse deklariert sind.The name of a method must differ from the names of all other non-methods declared in the same class. Außerdem muss sich die Signatur einer Methode von den Signaturen aller anderen Methoden unterscheiden, die in derselben Klasse deklariert sind, und zwei Methoden, die in derselben Klasse deklariert werden, dürfen keine Signaturen aufweisen, ref die outsich ausschließlich durch und unterscheiden.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.

Die type_parameters der Methode befinden sich im Gültigkeitsbereich des method_declarationund können zum bilden von Typen in diesem Bereich in return_type, method_bodyund type_parameter_constraints_clauses verwendet werden, aber nicht in Attribute.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.

Alle formalen Parameter und Typparameter müssen unterschiedliche Namen aufweisen.All formal parameters and type parameters must have different names.

Methoden ParameterMethod parameters

Die Parameter einer Methode, sofern vorhanden, werden vom formal_parameter_listder Methode deklariert.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
    ;

Die Liste der formalen Parameter besteht aus mindestens einem durch Trennzeichen getrennten Parameter, bei dem nur der letzte ein parameter_arraysein kann.The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Ein fixed_parameter besteht aus einem optionalen Satz von Attributen (Attributen), einem optionalen ref-, out-oder this-Modifizierer, einem Typ, einem Bezeichner und einem optionalen 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. Jede fixed_parameter deklariert einen Parameter des angegebenen Typs mit dem angegebenen Namen.Each fixed_parameter declares a parameter of the given type with the given name. Der this -Modifizierer legt die-Methode als Erweiterungsmethode fest und ist nur für den ersten Parameter einer statischen Methode zulässig.The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method. Erweiterungs Methoden werden weiter unten in den Erweiterungs Methodenbeschrieben.Extension methods are further described in Extension methods.

Ein fixed_parameter mit einem default_argument -Wert wird als optionaler Parameterbezeichnet, wohingegen ein fixed_parameter ohne default_argument ein erforderlicher Parameterist.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. Ein erforderlicher Parameter darf nicht nach einem optionalen Parameter in einer formal_parameter_listangezeigt werden.A required parameter may not appear after an optional parameter in a formal_parameter_list.

Ein ref-oder out-Parameter darf keinen default_argumentaufweisen.A ref or out parameter cannot have a default_argument. Der Ausdruck in einem default_argument muss einer der folgenden sein:The expression in a default_argument must be one of the following:

  • eine constant_expressiona constant_expression
  • ein Ausdruck der Form new S() , in S der ein Werttyp ist.an expression of the form new S() where S is a value type
  • ein Ausdruck der Form default(S) , in S der ein Werttyp ist.an expression of the form default(S) where S is a value type

Der Ausdruck muss implizit durch eine Identität oder eine Konvertierung in den Typ des Parameters konvertiert werden können.The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

Wenn optionale Parameter in einer implementierenden partiellen Methoden Deklaration (partielle Methoden) auftreten, eine explizite Schnittstellenmember-Implementierung (explizite Schnittstellenmember-Implementierungen) oder in einer Indexer-Deklaration mit einem einzelnen Parameter ( Indexer) der Compiler sollte eine Warnung angeben, da diese Member niemals auf eine Weise aufgerufen werden können, die das Weglassen von Argumenten zulässt.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.

Ein parameter_array besteht aus einem optionalen Satz von Attributen (Attributen), einem params-Modifizierer, einem array_typeund einem Bezeichner.A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. Ein Parameter Array deklariert einen einzelnen Parameter des angegebenen Array Typs mit dem angegebenen Namen.A parameter array declares a single parameter of the given array type with the given name. Der array_type eines Parameter Arrays muss ein eindimensionaler Arraytyp (Array Typen) sein.The array_type of a parameter array must be a single-dimensional array type (Array types). Bei einem Methodenaufruf ermöglicht ein Parameter Array, dass entweder ein einzelnes Argument des angegebenen Array Typs angegeben wird, oder es lässt NULL oder mehr Argumente des Array Elementtyps angegeben werden.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 werden in Parameter Arraysausführlicher beschrieben.Parameter arrays are described further in Parameter arrays.

Ein parameter_array kann nach einem optionalen Parameter auftreten, kann aber keinen Standardwert aufweisen. das Weglassen von Argumenten für eine parameter_array würde stattdessen zur Erstellung eines leeren Arrays führen.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.

Das folgende Beispiel veranschaulicht verschiedene Arten von Parametern: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
) { }

In formal_parameter_list für M ist i ein erforderlicher ref-Parameter. d ist ein erforderlicher Wert Parameter, b, s, o und t sind optionale Wert Parameter und a ein Parameter Array.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.

Eine Methoden Deklaration erstellt einen separaten Deklarations Bereich für Parameter, Typparameter und lokale Variablen.A method declaration creates a separate declaration space for parameters, type parameters and local variables. Namen werden in diesem Deklarations Bereich durch die Typparameter Liste und die Liste formaler Parameter der-Methode und durch lokale Variablen Deklarationen im- Block der-Methode eingeführt.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. Es ist ein Fehler, wenn zwei Member eines Methoden Deklarations Raums denselben Namen haben.It is an error for two members of a method declaration space to have the same name. Es ist ein Fehler für den Deklarations Bereich der Methode und der Deklarations Bereich der lokalen Variablen eines in der Tabelle enthaltenen Deklarations Raums, der Elemente mit dem gleichen Namen enthalten soll.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.

Ein Methodenaufruf (Methodenaufrufe) erstellt eine Kopie, die für diesen Aufruf spezifisch ist, der formalen Parameter und lokalen Variablen der Methode, und die Argumentliste des Aufrufs weist Werte oder Variablen Verweise auf den neu erstellten formalen Metern.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. Innerhalb des- Blocks einer Methode kann von ihren bezeichtern in Simple_name -Ausdrücken (einfachen Namen) auf formale Parameter verwiesen werden.Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

Es gibt vier Arten von formalen Parametern:There are four kinds of formal parameters:

  • Wert Parameter, die ohne modifiziererer deklariert werden.Value parameters, which are declared without any modifiers.
  • Verweis Parameter, die mit dem ref -Modifizierer deklariert werden.Reference parameters, which are declared with the ref modifier.
  • Ausgabeparameter, die mit dem out -Modifizierer deklariert werden.Output parameters, which are declared with the out modifier.
  • Parameter Arrays, die mit dem params -Modifizierer deklariert werden.Parameter arrays, which are declared with the params modifier.

Wie in Signaturen und überladenbeschrieben, sind ref die out Modifizierer und Teil der Signatur einer Methode, der params -Modifizierer ist jedoch nicht.As described in Signatures and overloading, the ref and out modifiers are part of a method's signature, but the params modifier is not.

Wert ParameterValue parameters

Ein Parameter, der ohne Modifizierer deklariert wird, ist ein value-Parameter.A parameter declared with no modifiers is a value parameter. Ein value-Parameter entspricht einer lokalen Variablen, die den Anfangswert aus dem entsprechenden Argument abruft, das im Methodenaufruf angegeben wird.A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

Wenn ein formaler Parameter ein value-Parameter ist, muss das entsprechende Argument in einem Methodenaufruf ein Ausdruck sein, der implizit konvertierbar ist (implizite Konvertierungen), in den formalen Parametertyp.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.

Eine Methode darf einem Wert Parameter neue Werte zuweisen.A method is permitted to assign new values to a value parameter. Solche Zuweisungen wirken sich nur auf den lokalen Speicherort aus, der durch den value-Parameter dargestellt wird – Sie haben keine Auswirkung auf das tatsächliche Argument, das im Methodenaufruf angegeben wurde.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.

VerweisparameterReference parameters

Ein Parameter, der mit ref einem-Modifizierer deklariert wird, ist ein Verweis Parameter.A parameter declared with a ref modifier is a reference parameter. Anders als bei einem value-Parameter erstellt ein Verweis Parameter keinen neuen Speicherort.Unlike a value parameter, a reference parameter does not create a new storage location. Stattdessen stellt ein Verweis Parameter denselben Speicherort wie die Variable dar, die im Methodenaufruf als Argument angegeben wurde.Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

Wenn ein formaler Parameter ein Verweis Parameter ist, muss das entsprechende Argument in einem Methodenaufruf aus dem Schlüsselwort ref gefolgt von einem variable_reference (exakte Regeln zum Bestimmen der eindeutigen Zuweisung) desselben Typs bestehen wie der formale Parameter.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. Eine Variable muss definitiv zugewiesen werden, bevor Sie als Verweis Parameter übergeben werden kann.A variable must be definitely assigned before it can be passed as a reference parameter.

Innerhalb einer Methode wird ein Verweis Parameter immer als definitiv zugewiesen betrachtet.Within a method, a reference parameter is always considered definitely assigned.

Eine Methode, die als Iterator (Iteratoren) deklariert ist, darf keine Verweis Parameter aufweisen.A method declared as an iterator (Iterators) cannot have reference parameters.

Das BeispielThe 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);
    }
}

erzeugt die Ausgabeproduces the output

i = 2, j = 1

Für den Swap Aufruf von Mainin stellt iund dary .j xFor the invocation of Swap in Main, x represents i and y represents j. Folglich hat der Aufruf die Auswirkungen, die Werte von i und jauszutauschen.Thus, the invocation has the effect of swapping the values of i and j.

In einer Methode, die Verweis Parameter annimmt, ist es möglich, dass mehrere Namen denselben Speicherort darstellen.In a method that takes reference parameters it is possible for multiple names to represent the same storage location. Im BeispielIn 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);
    }
}

der F Aufruf von bin G übergibt einen Verweis auf s sowohl a für als auch für.the invocation of F in G passes a reference to s for both a and b. Daher beziehen sich bei sdiesem Aufruf die Namen a, und b alle auf denselben Speicherort, und die drei Zuweisungen ändern das Instanzfeld s.Thus, 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.

AusgabeparameterOutput parameters

Ein mit einem out -Modifizierer deklarierter Parameter ist ein Output-Parameter.A parameter declared with an out modifier is an output parameter. Ähnlich wie ein Verweis Parameter wird von einem Output-Parameter kein neuer Speicherort erstellt.Similar to a reference parameter, an output parameter does not create a new storage location. Stattdessen stellt ein Ausgabeparameter denselben Speicherort wie die Variable dar, die im Methodenaufruf als Argument angegeben wurde.Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

Wenn ein formaler Parameter ein Ausgabeparameter ist, muss das entsprechende Argument in einem Methodenaufruf aus dem Schlüsselwort out gefolgt von einem variable_reference (exakte Regeln zum Bestimmen der eindeutigen Zuweisung) desselben Typs wie dem bestehen. formaler Parameter.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. Eine Variable muss nicht definitiv zugewiesen werden, bevor Sie als Output-Parameter übergeben werden kann. nach einem Aufruf, bei dem eine Variable als Output-Parameter übergeben wurde, wird die Variable als definitiv zugewiesen betrachtet.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.

Innerhalb einer Methode wird ein Ausgabeparameter, wie eine lokale Variable, anfänglich als nicht zugewiesen betrachtet und muss definitiv zugewiesen werden, bevor der Wert verwendet wird.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.

Jeder Ausgabeparameter einer Methode muss definitiv zugewiesen werden, bevor die Methode zurückgegeben wird.Every output parameter of a method must be definitely assigned before the method returns.

Eine Methode, die als partielle Methode (partielle Methoden) oder Iterator (Iteratoren) deklariert ist, darf keine Ausgabeparameter aufweisen.A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

Ausgabeparameter werden in der Regel in Methoden verwendet, die mehrere Rückgabewerte liefern.Output parameters are typically used in methods that produce multiple return values. Zum Beispiel: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);
    }
}

Das Beispiel erzeugt die Ausgabe:The example produces the output:

c:\Windows\System\
hello.txt

Beachten Sie, dir dass name die Zuweisung der-Variable und der-Variablen aufgeh SplitPathoben werden kann, bevor Sie an übermittelt werden, und dass Sie nach dem-Befehl definitiv zugewiesen werdenNote 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.

ParameterarraysParameter arrays

Ein Parameter, der mit params einem-Modifizierer deklariert wird, ist ein Parameter Array.A parameter declared with a params modifier is a parameter array. Wenn eine Liste formaler Parameter ein Parameter Array enthält, muss es sich um den letzten Parameter in der Liste handeln, und es muss sich um einen eindimensionalen Arraytyp handeln.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. Beispielsweise können die- string[] Typen string[][] und als Typ eines Parameter Arrays verwendet werden, aber der-Typ string[,] ist nicht möglich.For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. Es ist nicht möglich, den params -Modifizierer mit den Modifizierern ref und outzu kombinieren.It is not possible to combine the params modifier with the modifiers ref and out.

Ein Parameter Array ermöglicht das Angeben von Argumenten in einem Methodenaufruf auf zwei Arten:A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • Das für ein Parameter Array angegebene Argument kann ein einzelner Ausdruck sein, der implizit konvertierbar ist (implizite Konvertierungen), in den Typ des Parameter Arrays.The argument given for a parameter array can be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. In diesem Fall verhält sich das Parameter Array genau wie ein value-Parameter.In this case, the parameter array acts precisely like a value parameter.
  • Alternativ kann der Aufruf NULL oder mehr Argumente für das Parameter Array angeben, wobei jedes Argument ein Ausdruck ist, der implizit konvertierbar ist (implizite Konvertierungen), in den Elementtyp des Parameter Arrays.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 diesem Fall erstellt der Aufruf eine Instanz des Parameter Array Typs mit einer Länge, die der Anzahl der Argumente entspricht, initialisiert die Elemente der Array Instanz mit den angegebenen Argument Werten und verwendet die neu erstellte Array Instanz als die tatsächliche gestritten.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.

Mit der Ausnahme, dass eine Variable Anzahl von Argumenten in einem Aufruf zugelassen wird, entspricht ein Parameter Array exakt einem Wert Parameter (value-Parameter) desselben Typs.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.

Das BeispielThe 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();
    }
}

erzeugt die Ausgabeproduces the output

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

Beim ersten Aufruf von F wird das Array a einfach als value-Parameter übergeben.The first invocation of F simply passes the array a as a value parameter. Der zweite Aufruf von F erstellt automatisch ein vier-Element int[] mit den angegebenen Element Werten und übergibt diese Array Instanz als Wert Parameter.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. Ebenso erstellt der dritte Aufruf von F ein NULL-Element int[] und übergibt diese Instanz als value-Parameter.Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. Der zweite und der dritte Aufruf entsprechen genau dem Schreiben:The second and third invocations are precisely equivalent to writing:

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

Beim Durchführen der Überladungs Auflösung kann eine Methode mit einem Parameter Array entweder in der normalen Form oder in der erweiterten Form (anwendbares Funktionsmember) anwendbar sein.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). Die erweiterte Form einer Methode ist nur verfügbar, wenn die normale Form der Methode nicht anwendbar ist und nur, wenn eine anwendbare Methode mit derselben Signatur wie das erweiterte Formular nicht bereits im selben Typ deklariert ist.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.

Das BeispielThe 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);
    }
}

erzeugt die Ausgabeproduces the output

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

Im Beispiel sind zwei der möglichen erweiterten Formen der-Methode mit einem Parameter Array bereits als reguläre Methoden in der-Klasse enthalten.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. Diese erweiterten Formulare werden daher bei der Überladungs Auflösung nicht berücksichtigt, und der erste und dritte Methodenaufruf wählen daher die regulären Methoden aus.These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. Wenn eine Klasse eine Methode mit einem Parameter Array deklariert, ist es nicht ungewöhnlich, dass auch einige der erweiterten Formulare als reguläre Methoden enthalten sind.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. Auf diese Weise ist es möglich, die Zuordnung einer Array Instanz zu vermeiden, die auftritt, wenn eine erweiterte Form einer Methode mit einem Parameter Array aufgerufen wird.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.

Wenn der Typ eines Parameter Arrays ist object[], entsteht eine potenzielle Mehrdeutigkeit zwischen der normalen Form der-Methode und dem aufgewendet-Formular für einen einzelnen object Parameter.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. Der Grund für die Mehrdeutigkeit besteht darin object[] , dass eine selbst implizit in objectden Typ konvertiert werden kann.The reason for the ambiguity is that an object[] is itself implicitly convertible to type object. Die Mehrdeutigkeit stellt jedoch kein Problem dar, da Sie durch Einfügen einer Umwandlung bei Bedarf aufgelöst werden kann.The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

Das BeispielThe 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);
    }
}

erzeugt die Ausgabeproduces the output

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

Im ersten und letzten Aufruf von Fist die normale Form von F anwendbar, da eine implizite Konvertierung vom Argumenttyp in den Parametertyp besteht (beide sind vom Typ 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[]). Folglich wählt die Überladungs Auflösung die normale Form Fvon aus, und das-Argument wird als regulärer Wert Parameter übergeben.Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. Im zweiten und dritten Aufruf ist die normale Form von F nicht anwendbar, weil keine implizite Konvertierung vom Argumenttyp zum Parametertyp vorhanden ist (der Typ object kann nicht implizit in den Typ object[]konvertiert werden).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[]). Allerdings ist die erweiterte Form F von anwendbar, sodass Sie durch Überladungs Auflösung ausgewählt wird.However, the expanded form of F is applicable, so it is selected by overload resolution. Folglich wird ein One-Element object[] durch den Aufruf erstellt, und das einzelne Element des Arrays wird mit dem angegebenen Argument Wert initialisiert (der selbst ein Verweis auf ein object[]ist).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[]).

Statische Methoden und InstanzmethodenStatic and instance methods

Wenn eine Methoden Deklaration einen static -Modifizierer enthält, wird diese Methode als statische Methode bezeichnet.When a method declaration includes a static modifier, that method is said to be a static method. Wenn kein static Modifizierer vorhanden ist, wird die Methode als Instanzmethode bezeichnet.When no static modifier is present, the method is said to be an instance method.

Eine statische Methode funktioniert nicht für eine bestimmte Instanz, und Sie ist ein Kompilierzeitfehler, auf den this in einer statischen Methode verwiesen wird.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.

Eine Instanzmethode arbeitet auf einer bestimmten Instanz einer Klasse, und auf diese Instanz kann als this (dieser Zugriff) zugegriffen werden.An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

Wenn auf eine Methode in einem member_access -Element (Member Access) der Form E.M verwiesen wird, wenn M eine statische Methode ist, muss E einen Typ angeben, der M enthält, und wenn M eine Instanzmethode ist, muss E eine Instanz eines Typs bezeichnen. mit M.When 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.

Die Unterschiede zwischen statischen und Instanzmembern werden in statischen und Instanzmembernausführlicher erläutert.The differences between static and instance members are discussed further in Static and instance members.

Virtuelle MethodenVirtual methods

Wenn eine Instanzmethodendeklaration einen virtual -Modifizierer enthält, wird diese Methode als virtuelle Methode bezeichnet.When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. Wenn kein virtual Modifizierer vorhanden ist, wird die Methode als nicht virtuelle Methode bezeichnet.When no virtual modifier is present, the method is said to be a non-virtual method.

Die Implementierung einer nicht virtuellen Methode ist unveränderlich: Die-Implementierung ist identisch, unabhängig davon, ob die-Methode für eine Instanz der-Klasse, in der Sie deklariert ist, oder für eine Instanz einer abgeleiteten Klasse aufgerufen wird.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. Im Gegensatz dazu kann die Implementierung einer virtuellen Methode durch abgeleitete Klassen abgelöst werden.In contrast, the implementation of a virtual method can be superseded by derived classes. Der Prozess der Ersetzung der Implementierung einer geerbten virtuellen Methode wird als über Schreiben dieser Methode bezeichnet (Überschreibungs Methoden).The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

Beim Aufruf einer virtuellen Methode bestimmt der Lauf Zeittyp der Instanz, für die dieser Aufruf erfolgt, die tatsächliche Methoden Implementierung, die aufgerufen werden soll.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. Bei einem Aufruf der nicht virtuellen Methode ist der Kompilier Zeittyp der Instanz der bestimmende Faktor.In a non-virtual method invocation, the compile-time type of the instance is the determining factor. Genauer gesagt, wenn eine Methode mit dem N Namen mit einer Argumentliste A für eine Instanz mit einem Kompilier Zeittyp C und einem Lauf Zeittyp R aufgerufen wird R (wobei C entweder oder eine von abgeleitete Klasse ist). von C) wird der Aufruf wie folgt verarbeitet: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:

  • Zuerst wird die Überladungs Auflösung auf C, Nund Aangewendet, um eine bestimmte Methode M aus dem Satz von Methoden auszuwählen, der in deklariert und Cvon geerbt wurde.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. Dies wird unter Methodenaufrufebeschrieben.This is described in Method invocations.
  • Wenn M dann eine nicht virtuelle Methode ist, M wird aufgerufen.Then, if M is a non-virtual method, M is invoked.
  • Andernfalls ist eine virtuelle Methode, und die am meisten abgeleitete Implementierung M von in Bezug R auf wird aufgerufen. MOtherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

Für jede virtuelle Methode, die in der Klasse deklariert oder von einer Klasse geerbt wurde, gibt es in Bezug auf diese Klasse eine am meisten abgeleitete Implementierung der-Methode.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. Die am weitesten abgeleitete Implementierung einer virtuellen M Methode in Bezug auf eine R Klasse wird wie folgt bestimmt:The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • Wenn R die Introducing virtual -Deklaration Mvon enthält, dann handelt es sich hierbei um Mdie am meisten abgeleitete Implementierung von.If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • Wenn R andernfalls einen override von Menthält, ist dies die am meisten abgeleitete Implementierung von M.Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • Andernfalls ist die am meisten abgeleitete M Implementierung von in R Bezug auf die gleiche wie die am meisten abgeleitete Implementierung von M in Bezug auf die direkte RBasisklasse von.Otherwise, 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.

Im folgenden Beispiel werden die Unterschiede zwischen virtuellen und nicht virtuellen Methoden veranschaulicht: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();
    }
}

Im Beispiel wird A eine nicht virtuelle Methode F und eine virtuelle Methode Geingeführt.In the example, A introduces a non-virtual method F and a virtual method G. Die- B Klasse führt eine neue nicht virtuelle Methode Fein, wodurch die geerbte F-Methode ausgeblendet wird, und über Gschreibt auch die geerbte-Methode.The class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. Das Beispiel erzeugt die Ausgabe:The example produces the output:

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

Beachten Sie B.G, dass a.G() die-Anweisung A.Gaufruft, nicht.Notice that the statement a.G() invokes B.G, not A.G. Dies liegt daran, dass der Lauf Zeittyp der-Instanz ( Bd. h.), nicht der Kompilier Zeittyp der A-Instanz (d. h.), die tatsächliche Methoden Implementierung bestimmt, die aufgerufen werden soll.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.

Da Methoden vererbte Methoden ausblenden dürfen, kann eine Klasse mehrere virtuelle Methoden mit der gleichen Signatur enthalten.Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. Dies stellt kein mehrdeutigkeitsproblem dar, da alle außer der am weitesten abgeleiteten Methode ausgeblendet sind.This does not present an ambiguity problem, since all but the most derived method are hidden. Im BeispielIn 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();
    }
}

die C Klassen D und enthalten zwei virtuelle Methoden mit der gleichen Signatur: Der von A eingeführte und der von Ceingeführte.the C and D classes contain two virtual methods with the same signature: The one introduced by A and the one introduced by C. Die durch C eingeführte Methode blendet die von geerbte Methode aus A.The method introduced by C hides the method inherited from A. Folglich überschreibt die Überschreibungs D Deklaration in die von Ceingeführte Methode, und D es ist nicht möglich, die durch Aeingeführte Methode zu überschreiben.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. Das Beispiel erzeugt die Ausgabe:The example produces the output:

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

Beachten Sie, dass es möglich ist, die verborgene virtuelle Methode aufzurufen, indem D Sie auf eine Instanz von über einen weniger abgeleiteten Typ zugreifen, in dem die Methode nicht ausgeblendet ist.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.

Überschreibungs MethodenOverride methods

Wenn eine Instanzmethodendeklaration einen override -Modifizierer enthält, wird die-Methode als Überschreibungs Methodebezeichnet.When an instance method declaration includes an override modifier, the method is said to be an override method. Eine Überschreibungs Methode überschreibt eine geerbte virtuelle Methode mit derselben Signatur.An override method overrides an inherited virtual method with the same signature. Während eine Deklaration einer virtuellen Methode eine neue Methode einführt, spezialisiert eine Deklaration einer überschriebenen Methode eine vorhandene geerbte virtuelle Methode, indem eine neue Implementierung dieser Methode bereitgestellt wird.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.

Die Methode, die durch eine override -Deklaration überschrieben wird, wird als die überschriebene Basis Methodebezeichnet.The method overridden by an override declaration is known as the overridden base method. Bei einer in einer M Klasse Cdeklarierten Überschreibungs Methode wird die überschriebene Basis Methode bestimmt, indem jeder Basis Klassentyp von Cuntersucht wird, beginnend mit C dem direkten Basis Klassentyp von und mit jeder nachfolgenden direkter Basis Klassentyp, bis in einem gegebenen Basis Klassentyp mindestens eine barrierefreie Methode gefunden wird, die die gleiche Signatur M wie nach der Ersetzung von Typargumenten aufweist.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. Zum Ermitteln der überschriebenen Basis Methode wird eine Methode publicals verfügbar protected protected internalbetrachtet, wenn dies der Fall ist, wenn dies der Fall ist, oder wenn Sie in demselben internal Programm wie Cdeklariert und deklariert wird.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.

Ein Kompilierzeitfehler tritt auf, es sei denn, für eine Überschreibungs Deklaration gilt Folgendes:A compile-time error occurs unless all of the following are true for an override declaration:

  • Eine überschriebene Basis Methode kann wie oben beschrieben gefunden werden.An overridden base method can be located as described above.
  • Es gibt genau eine solche überschriebene Basis Methode.There is exactly one such overridden base method. Diese Einschränkung hat nur Auswirkungen, wenn der Basis Klassentyp ein konstruierter Typ ist, bei dem durch die Ersetzung von Typargumenten die Signatur zweier Methoden identisch ist.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.
  • Die überschriebene Basis Methode ist eine virtuelle Methode, eine abstrakte Methode oder eine Überschreibungs Methode.The overridden base method is a virtual, abstract, or override method. Anders ausgedrückt: die überschriebene Basis Methode kann nicht statisch oder nicht virtuell sein.In other words, the overridden base method cannot be static or non-virtual.
  • Die überschriebene Basis Methode ist keine versiegelte Methode.The overridden base method is not a sealed method.
  • Die Überschreibungs Methode und die überschriebene Basis Methode haben denselben Rückgabetyp.The override method and the overridden base method have the same return type.
  • Die Überschreibungs Deklaration und die überschriebene Basis Methode haben dieselbe deklarierte Zugriffsmethode.The override declaration and the overridden base method have the same declared accessibility. Anders ausgedrückt: eine Überschreibungs Deklaration kann nicht den Zugriff auf die virtuelle Methode ändern.In other words, an override declaration cannot change the accessibility of the virtual method. Wenn die überschriebene Basis Methode jedoch intern geschützt ist und in einer anderen Assembly als der Assembly deklariert ist, die die Überschreibungs Methode enthält, muss die deklarierte Barrierefreiheit der Überschreibungs Methode geschützt werden.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.
  • Die Überschreibungs Deklaration gibt keine Type-Parameter-Einschränkungs Klauseln an.The override declaration does not specify type-parameter-constraints-clauses. Stattdessen werden die Einschränkungen von der überschriebenen Basis Methode geerbt.Instead the constraints are inherited from the overridden base method. Beachten Sie, dass Einschränkungen, die Typparameter in der überschriebenen Methode sind, durch Typargumente in der geerbten Einschränkung ersetzt werden können.Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. Dies kann zu Einschränkungen führen, die nicht zulässig sind, wenn Sie explizit angegeben werden, z. b. Werttypen oder versiegelte Typen.This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

Im folgenden Beispiel wird veranschaulicht, wie die über schreibenden Regeln für generische Klassen funktionieren: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>
}

Eine Überschreibungs Deklaration kann mithilfe eines base_access (Basis Zugriffs) auf die überschriebene Basis Methode zugreifen.An override declaration can access the overridden base method using a base_access (Base access). Im BeispielIn 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);
    }
}

der base.PrintFields() Aufruf in B Ruft die PrintFields in Adeklarierte Methode auf.the base.PrintFields() invocation in B invokes the PrintFields method declared in A. Ein base_access deaktiviert den virtuellen Aufruf Mechanismus und behandelt die Basis Methode einfach als nicht virtuelle Methode.A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. Wenn B der Aufruf von geschrieben ((A)this).PrintFields()wurde, würde er die PrintFields in Bdeklarierte Methode rekursiv aufrufen, nicht die in Adeklarierte Methode PrintFields , da der virtuelle und der Lauf Zeittyp von ist.((A)this) ist .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.

Nur durch das einschließen override eines-Modifizierers kann eine Methode eine andere Methode überschreiben.Only by including an override modifier can a method override another method. In allen anderen Fällen verbirgt eine Methode, die dieselbe Signatur wie eine geerbte Methode hat, einfach die geerbte Methode.In all other cases, a method with the same signature as an inherited method simply hides the inherited method. Im BeispielIn the example

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

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

die F -Methode B in override enthält keinen-Modifizierer und überschreibt daher nicht F die- AMethode in.the F method in B does not include an override modifier and therefore does not override the F method in A. Stattdessen verbirgt die F -Methode B in die-Methode Ain, und es wird eine Warnung ausgegeben, da die Deklaration new keinen Modifizierer enthält.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.

Im BeispielIn 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
}

die F -Methode B in blendet F die von geerbte virtuelle Methode aus A.the F method in B hides the virtual F method inherited from A. Da das neue F in B privaten Zugriff hat, umfasst sein Bereich nur den Klassen Text von B und wird nicht auf Cerweitert.Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. Daher ist es zulässig, F die C Deklaration von in zu F überschreiben A, das von geerbt wurde.Therefore, the declaration of F in C is permitted to override the F inherited from A.

Versiegelte MethodenSealed methods

Wenn eine Instanzmethodendeklaration einen sealed -Modifizierer enthält, wird diese Methode als versiegelte Methodebezeichnet.When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. Wenn eine Instanzmethodendeklaration den sealed -Modifizierer enthält, muss Sie auch den override -Modifizierer einschließen.If an instance method declaration includes the sealed modifier, it must also include the override modifier. Die Verwendung des sealed -Modifizierers verhindert, dass eine abgeleitete Klasse die-Methode weiter überschreibt.Use of the sealed modifier prevents a derived class from further overriding the method.

Im BeispielIn 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");
    } 
}

die- B Klasse stellt zwei Überschreibungs Methoden F bereit: eine Methode sealed , die über den G -Modifizierer und eine-Methode verfügt, die nicht.the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. Bdie Verwendung des versiegelten modifier verhindert C , dass weiter Füberschrieben wird.B's use of the sealed modifier prevents C from further overriding F.

Abstrakte MethodenAbstract methods

Wenn eine Instanzmethodendeklaration einen abstract -Modifizierer enthält, wird diese Methode als abstrakte Methodebezeichnet.When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Obwohl eine abstrakte Methode implizit auch eine virtuelle Methode ist, kann Sie nicht den-Modifizierer virtualaufweisen.Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

Eine abstrakte Methoden Deklaration führt eine neue virtuelle Methode ein, stellt jedoch keine Implementierung dieser Methode bereit.An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Stattdessen sind nicht abstrakte abgeleitete Klassen erforderlich, um eine eigene Implementierung bereitzustellen, indem diese Methode überschrieben wird.Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Da eine abstrakte Methode keine tatsächliche Implementierung bereitstellt, besteht das method_body einer abstrakten Methode einfach aus einem Semikolon.Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

Abstrakte Methoden Deklarationen sind nur in abstrakten Klassen zulässig (abstrakte Klassen).Abstract method declarations are only permitted in abstract classes (Abstract classes).

Im BeispielIn 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);
    }
}

die Shape -Klasse definiert das abstrakte Konzept eines geometrischen Shape-Objekts, das sich selbst zeichnen kann.the Shape class defines the abstract notion of a geometrical shape object that can paint itself. Die Paint Methode ist abstrakt, da keine sinnvolle Standard Implementierung vorhanden ist.The Paint method is abstract because there is no meaningful default implementation. Die Ellipse Klassen Box und sind konkrete Shape Implementierungen.The Ellipse and Box classes are concrete Shape implementations. Da diese Klassen nicht abstrakt sind, müssen Sie die Paint -Methode überschreiben und eine tatsächliche Implementierung bereitstellen.Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

Es handelt sich um einen Kompilierzeitfehler für ein base_access (Base Access), der auf eine abstrakte Methode verweist.It is a compile-time error for a base_access (Base access) to reference an abstract method. Im BeispielIn the example

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

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

für den base.F() Aufruf wird ein Kompilierzeitfehler gemeldet, weil er auf eine abstrakte Methode verweist.a compile-time error is reported for the base.F() invocation because it references an abstract method.

Eine abstrakte Methoden Deklaration darf eine virtuelle Methode überschreiben.An abstract method declaration is permitted to override a virtual method. Dadurch kann eine abstrakte Klasse die erneute Implementierung der Methode in abgeleiteten Klassen erzwingen und die ursprüngliche Implementierung der Methode nicht verfügbar machen.This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. Im BeispielIn 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");
    }
}

die A -Klasse deklariert eine virtuelle Methode B , die-Klasse überschreibt diese Methode mit einer abstrakten C -Methode, und die-Klasse überschreibt die abstrakte-Methode, um eine eigene Implementierung bereitzustellen.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.

Externe MethodenExternal methods

Wenn eine Methoden Deklaration einen extern -Modifizierer enthält, wird diese Methode als externe Methodebezeichnet.When a method declaration includes an extern modifier, that method is said to be an external method. Externe Methoden werden extern implementiert, in der Regel mit einer anderen C#Sprache als.External methods are implemented externally, typically using a language other than C#. Da eine externe Methoden Deklaration keine tatsächliche Implementierung bereitstellt, besteht das method_body einer externen Methode einfach aus einem Semikolon.Because an external method declaration provides no actual implementation, the method_body of an external method simply consists of a semicolon. Eine externe Methode darf nicht generisch sein.An external method may not be generic.

Der extern -Modifizierer wird in der Regel in DllImport Verbindung mit einem-Attribut (Interoperation mit com-und Win32-Komponenten) verwendet, sodass externe Methoden durch DLLs (Dynamic Link Libraries) implementiert werden können.The extern modifier is typically used in conjunction with a DllImport attribute (Interoperation with COM and Win32 components), allowing external methods to be implemented by DLLs (Dynamic Link Libraries). Die Ausführungsumgebung unterstützt möglicherweise andere Mechanismen, bei denen Implementierungen externer Methoden bereitgestellt werden können.The execution environment may support other mechanisms whereby implementations of external methods can be provided.

Wenn eine externe Methode ein DllImport -Attribut enthält, muss die Methoden Deklaration auch einen static -Modifizierer enthalten.When an external method includes a DllImport attribute, the method declaration must also include a static modifier. Dieses Beispiel veranschaulicht die Verwendung des extern -Modifizierers und des DllImport -Attributs: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);
}

Partielle Methoden (Recap)Partial methods (recap)

Wenn eine Methoden Deklaration einen partial -Modifizierer enthält, wird diese Methode als partielle Methodebezeichnet.When a method declaration includes a partial modifier, that method is said to be a partial method. Partielle Methoden können nur als Member von partiellen Typen (partielle Typen) deklariert werden und unterliegen einer Reihe von Einschränkungen.Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. Partielle Methoden werden weiter unten in partiellen Methodenbeschrieben.Partial methods are further described in Partial methods.

ErweiterungsmethodenExtension methods

Wenn der erste Parameter einer Methode den this -Modifizierer enthält, wird diese Methode als Erweiterungsmethodebezeichnet.When the first parameter of a method includes the this modifier, that method is said to be an extension method. Erweiterungs Methoden können nur in nicht generischen, nicht in der Tabelle statischen Klassen deklariert werden.Extension methods can only be declared in non-generic, non-nested static classes. Der erste Parameter einer Erweiterungsmethode kann keine anderen Modifizierer als thisaufweisen, und der Parametertyp darf kein Zeigertyp sein.The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

Im folgenden finden Sie ein Beispiel für eine statische Klasse, die zwei Erweiterungs Methoden deklariert: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;
    }
}

Eine Erweiterungsmethode ist eine reguläre statische Methode.An extension method is a regular static method. Außerdem kann eine Erweiterungsmethode mit instanzmethodenaufruf-Syntax (Erweiterungs Methodenaufrufe) mithilfe des Empfänger Ausdrucks als erstes Argument aufgerufen werden, wenn sich Ihre einschließende statische Klasse im Gültigkeitsbereich befindet.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.

Im folgenden Programm werden die oben deklarierten Erweiterungs Methoden verwendet: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());
        }
    }
}

Die Slice -Methode ist string[]in verfügbar, und die ToInt32 -Methode ist in stringverfügbar, da Sie als Erweiterungs Methoden deklariert wurden.The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. Die Bedeutung des Programms ist mit den folgenden statischen Methoden aufrufen identisch: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));
        }
    }
}

Methoden TextMethod body

Der method_body einer Methoden Deklaration besteht aus einem Block Körper, einem Ausdrucks Körper oder einem Semikolon.The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

Der Ergebnistyp einer Methode ist void , wenn der Rückgabetyp ist void, oder wenn die Methode Async ist und der System.Threading.Tasks.TaskRückgabetyp ist.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. Andernfalls ist der Ergebnistyp einer nicht Async-Methode der Rückgabetyp, und der Ergebnistyp einer Async-Methode mit System.Threading.Tasks.Task<T> dem TRückgabetyp ist.Otherwise, 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.

Wenn eine Methode über einen void Ergebnistyp und einen Block Text return verfügt, ist es nicht zulässig, Anweisungen (die Return-Anweisung) im-Block anzugeben.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. Wenn die Ausführung des Blocks einer void-Methode normal abgeschlossen ist (d. h., der Ablauf der Steuerelemente wird vom Ende des Methoden Texts entfernt), kehrt diese Methode einfach an den aktuellen Aufrufer zurück.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.

Wenn eine Methode über ein void-Ergebnis und einen Ausdrucks Text verfügt, muss der Ausdruck E ein statement_expressionsein, und der Text entspricht genau einem Block Text in der Form { 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; }.

Wenn eine Methode einen nicht leeren Ergebnistyp und einen Block Text hat, muss return jede Anweisung im Block einen Ausdruck angeben, der implizit in den Ergebnistyp konvertiert werden kann.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. Der Endpunkt eines Block Texts einer Wert Rückgabe Methode darf nicht erreichbar sein.The endpoint of a block body of a value-returning method must not be reachable. Anders ausgedrückt: in einer Rückgabe Methode mit einem Block Text ist das Steuerelement nicht berechtigt, das Ende des Methoden Texts zu übertragen.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.

Wenn eine Methode einen nicht leeren Ergebnistyp und einen Ausdrucks Körper aufweist, muss der Ausdruck implizit in den Ergebnistyp konvertiert werden, und der Text entspricht genau dem Block Text des Formulars { 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; }.

Im BeispielIn 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;
}

die Wert Rückgabe F Methode führt zu einem Kompilierzeitfehler, da das Steuerelement vom Ende des Methoden Texts entfernt werden kann.the value-returning F method results in a compile-time error because control can flow off the end of the method body. Die G - H Methode und die-Methode sind korrekt, da alle möglichen Ausführungs Pfade in einer Return-Anweisung enden, die einen Rückgabewert angibt.The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. Die I Methode ist korrekt, da Ihr Text einem Anweisungsblock mit nur einer einzigen return-Anweisung entspricht.The I method is correct, because its body is equivalent to a statement block with just a single return statement in it.

MethodenüberladungMethod overloading

Die Regeln zur Auflösung von Methoden Überladungen werden unter Typrückschlussbeschrieben.The method overload resolution rules are described in Type inference.

EigenschaftenProperties

Eine Eigenschaft ist ein Member, der den Zugriff auf ein Merkmal eines Objekts oder einer Klasse ermöglicht.A property is a member that provides access to a characteristic of an object or a class. Beispiele für Eigenschaften sind die Länge einer Zeichenfolge, die Größe einer Schriftart, die Beschriftung eines Fensters, der Name eines Kunden usw.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. Eigenschaften sind eine natürliche Erweiterung von Feldern – beide sind benannte Member mit zugeordneten Typen, und die Syntax für den Zugriff auf Felder und Eigenschaften ist identisch.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. Im Gegensatz zu Feldern bezeichnen Eigenschaften jedoch keine Speicherorte.However, unlike fields, properties do not denote storage locations. Stattdessen verfügen Eigenschaften über Accessors zum Angeben der Anweisungen, die beim Lesen oder Schreiben ihrer Werte ausgeführt werden sollen.Instead, properties have accessors that specify the statements to be executed when their values are read or written. Eigenschaften bieten somit einen Mechanismus zum Zuordnen von Aktionen zum Lesen und Schreiben der Attribute eines Objekts. Außerdem können solche Attribute berechnet werden.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.

Eigenschaften werden mit property_declarations deklariert: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 ';'
    ;

Ein property_declaration kann einen Satz von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), den new (den neuen Modifizierer), static (statisch und Instanz) enthalten. Methoden), virtual (virtuelle Methoden), override (Überschreibungs Methoden), sealed (versiegelte Methoden), abstract (abstrakte Methoden) und extern-Modifizierer (Externe Methoden).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.

Eigenschafts Deklarationen unterliegen den gleichen Regeln wie Methoden Deklarationen (Methoden) in Bezug auf gültige Kombinationen von modifiziererobjekten.Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Der Typ einer Eigenschafts Deklaration gibt den Typ der Eigenschaft an, die von der Deklaration eingeführt wurde, und MEMBER_NAME gibt den Namen der Eigenschaft an.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. Wenn die Eigenschaft keine explizite Schnittstellenmember-Implementierung ist, ist MEMBER_NAME einfach ein Bezeichner.Unless the property is an explicit interface member implementation, the member_name is simply an identifier. Bei einer expliziten Schnittstellenmember-Implementierung (explizite Schnittstellenmember-Implementierungen) besteht das MEMBER_NAME aus einem INTERFACE_TYPE , gefolgt von einem "." und einem Bezeichner.For an explicit interface member implementation (Explicit interface member implementations), the member_name consists of an interface_type followed by a "." and an identifier.

Der Typ einer Eigenschaft muss mindestens so zugänglich sein wie die Eigenschaft selbst (BarrierefreiheitsEinschränkungen).The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Ein property_body kann entweder aus einem Accessor-Text oder einem Ausdrucks Körperbestehen.A property_body may either consist of an accessor body or an expression body. In einem Accessor-Text muss accessor_declarations, der in "{" und "}"-Token eingeschlossen werden muss, die Accessoren (Accessoren) der Eigenschaft deklarieren.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Die Accessoren geben die ausführbaren Anweisungen an, die dem Lesen und Schreiben der Eigenschaft zugeordnet sind.The accessors specify the executable statements associated with reading and writing the property.

Ein Ausdrucks Text, der => aus gefolgt von einem Ausdruck E und einem Semikolon besteht, entspricht exakt dem { get { return E; } }Anweisungs Text und kann daher nur zum Angeben von nur-Getter-Eigenschaften verwendet werden, bei denen das Ergebnis von der Getter wird durch einen einzelnen Ausdruck angegeben.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.

Ein property_initializer kann nur für eine automatisch implementierte Eigenschaft (automatisch implementierte Eigenschaften) angegeben werden und bewirkt die Initialisierung des zugrunde liegenden Felds dieser Eigenschaften mit dem Wert, der vom Ausdruck angegeben wird. .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.

Obwohl die Syntax für den Zugriff auf eine Eigenschaft mit der Syntax für ein Feld identisch ist, wird eine Eigenschaft nicht als Variable klassifiziert.Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Daher ist es nicht möglich, eine Eigenschaft als ref -oder out -Argument zu übergeben.Thus, it is not possible to pass a property as a ref or out argument.

Wenn eine Eigenschafts Deklaration extern einen-Modifizierer enthält, wird die-Eigenschaft als externe Eigenschaftbezeichnet.When a property declaration includes an extern modifier, the property is said to be an external property. Da eine externe Eigenschaften Deklaration keine tatsächliche Implementierung bereitstellt, besteht jede Ihrer accessor_declarations aus einem Semikolon.Because an external property declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Statische Eigenschaften und InstanzeigenschaftenStatic and instance properties

Wenn eine Eigenschafts Deklaration static einen-Modifizierer enthält, wird die-Eigenschaft als statische Eigenschaftbezeichnet.When a property declaration includes a static modifier, the property is said to be a static property. Wenn kein static Modifizierer vorhanden ist, wird die Eigenschaft als Instanzeigenschaftbezeichnet.When no static modifier is present, the property is said to be an instance property.

Eine statische Eigenschaft ist keiner bestimmten Instanz zugeordnet, und es handelt sich um einen Kompilierzeitfehler, auf den this in den Accessoren einer statischen Eigenschaft verwiesen wird.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.

Eine Instanzeigenschaft ist einer bestimmten Instanz einer Klasse zugeordnet, und auf diese Instanz kann als this (dieser Zugriff) in den Accessoren dieser Eigenschaft zugegriffen werden.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.

Wenn auf eine Eigenschaft in einem member_access (Member Access) der Form E.M verwiesen wird, wenn M eine statische Eigenschaft ist, muss E einen Typ angeben, der M enthält, und wenn M eine Instanzeigenschaft ist, muss E eine Instanz eines Typs angeben. mit M.When 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.

Die Unterschiede zwischen statischen und Instanzmembern werden in statischen und Instanzmembernausführlicher erläutert.The differences between static and instance members are discussed further in Static and instance members.

AccessorenAccessors

Der accessor_declarations -Wert einer Eigenschaft gibt die ausführbaren Anweisungen an, die dem Lesen und Schreiben dieser Eigenschaft zugeordnet sind.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
    | ';'
    ;

Die Accessordeklarationen bestehen aus get_accessor_declaration, set_accessor_declarationoder beidem.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Jede Accessor-Deklaration besteht aus dem Token get oder set, gefolgt von einem optionalen accessor_modifier und einem accessor_body.Each accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

Die Verwendung von accessor_modifiers unterliegt den folgenden Einschränkungen:The use of accessor_modifiers is governed by the following restrictions:

  • Ein accessor_modifier kann nicht in einer Schnittstelle oder in einer expliziten Schnittstellenmember-Implementierung verwendet werden.An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • Für eine Eigenschaft oder einen Indexer, der über keinen override-Modifizierer verfügt, ist ein accessor_modifier nur zulässig, wenn die Eigenschaft oder der Indexer sowohl einen get-als auch einen set-Accessor hat und dann nur für einen dieser Accessoren zulässig ist.For a property or indexer that has no override modifier, an accessor_modifier is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors.
  • Für eine Eigenschaft oder einen Indexer, die einen override-Modifizierer enthält, muss ein Accessor mit dem accessor_modifier(sofern vorhanden) der über schreibenden Zugriffsmethode identisch sein.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 muss eine Barrierefreiheit deklarieren, die strikt restriktiver ist als der deklarierte Zugriff auf die Eigenschaft oder den Indexer selbst.The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. Genauer gesagt:To be precise:
    • Wenn die Eigenschaft oder der Indexer über eine deklarierte Zugriffsmöglichkeit für public verfügt, kann accessor_modifier entweder protected internal, internal, protected oder private sein.If the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • Wenn die Eigenschaft oder der Indexer über eine deklarierte Zugriffsmöglichkeit für protected internal verfügt, kann accessor_modifier entweder internal, protected oder private sein.If the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • Wenn die Eigenschaft oder der Indexer eine deklarierte Zugriffsmöglichkeit für internal oder protected aufweist, muss accessor_modifier private sein.If the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • Wenn die Eigenschaft oder der Indexer über eine deklarierte Zugriffsmöglichkeit für private verfügt, darf kein accessor_modifier verwendet werden.If the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

Bei abstract-und extern-Eigenschaften ist die accessor_body für jeden angegebenen Accessor einfach ein Semikolon.For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. Eine nicht abstrakte, nicht externe Eigenschaft kann jede accessor_body ein Semikolon sein. in diesem Fall handelt es sich um eine automatisch implementierte Eigenschaft (automatisch implementierte Eigenschaften).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). Eine automatisch implementierte Eigenschaft muss mindestens über einen get-Accessor verfügen.An automatically implemented property must have at least a get accessor. Für die Accessoren anderer nicht abstrakter, nicht externer Eigenschaften ist accessor_body ein- Block , der die auszuführenden Anweisungen angibt, wenn der entsprechende-Accessor aufgerufen wird.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.

Ein get -Accessor entspricht einer Parameter losen Methode mit einem Rückgabewert des Eigenschaftentyps.A get accessor corresponds to a parameterless method with a return value of the property type. Wenn in einem Ausdruck get auf eine Eigenschaft verwiesen wird, wird der-Accessor der Eigenschaft aufgerufen, um den Wert der-Eigenschaft (Werte von Ausdrücken) zu berechnen, außer als Ziel einer Zuweisung.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). Der Text einer get -Zugriffsmethode muss den Regeln für die Rückgabe von Werten entsprechen, die im Methoden Textbeschrieben werden.The body of a get accessor must conform to the rules for value-returning methods described in Method body. Insbesondere müssen alle return Anweisungen im Textkörper einer get -Zugriffsmethode einen Ausdruck angeben, der implizit in den Eigenschaftentyp konvertiert werden kann.In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Außerdem darf der Endpunkt einer get -Zugriffsmethode nicht erreichbar sein.Furthermore, the endpoint of a get accessor must not be reachable.

Ein set -Accessor entspricht einer Methode mit einem einzelnen Wert Parameter des Eigenschaftentyps void und einem Rückgabetyp.A set accessor corresponds to a method with a single value parameter of the property type and a void return type. Der implizite Parameter einer set -Zugriffsmethode wird immer benannt. valueThe implicit parameter of a set accessor is always named value. Wenn auf eine Eigenschaft als Ziel einer Zuweisung (Zuweisungs Operatoren) oder ++ als Operand von oder -- (postfix Inkrement-und Dekrementoperatoren, Präfix Inkrement-und Dekrementoperatoren) verwiesen wird, wird der der Accessor wird mit einem Argument aufgerufen (dessen Wert dem der rechten Seite der Zuweisung oder dem Operanden ++ des or -- -Operators entspricht), der den neuen Wert (einfache Zuweisung) bereitstellt. 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). Der Text einer set -Zugriffsmethode muss den Regeln für void Methoden entsprechen, die im Methoden Textbeschrieben werden.The body of a set accessor must conform to the rules for void methods described in Method body. Insbesondere können return Anweisungen set im Accessor-Text keinen Ausdruck angeben.In particular, return statements in the set accessor body are not permitted to specify an expression. Da ein set -Accessor implizit einen Parameter mit valuedem Namen aufweist, handelt es sich um einen Kompilierzeitfehler für eine lokale Variable set oder eine Konstante Deklaration in einem-Accessor, der über diesen Namen verfügt.Since 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.

Basierend auf dem vorhanden sein oder fehlen get der-und- set Accessoren wird eine Eigenschaft wie folgt klassifiziert:Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • Eine Eigenschaft, die sowohl einen get -Accessor als set auch einen-Accessor enthält, wird als Lese-/Schreib-Eigenschaft bezeichnet.A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • Eine Eigenschaft, die nur einen get -Accessor aufweist, wird als Schreib geschützte Eigenschaft bezeichnet .A property that has only a get accessor is said to be a read-only property. Es handelt sich um einen Kompilierzeitfehler für eine schreibgeschützte Eigenschaft, die das Ziel einer Zuweisung ist.It is a compile-time error for a read-only property to be the target of an assignment.
  • Eine Eigenschaft, die nur einen set -Accessor aufweist, wird als Schreib geschützte Eigenschaft bezeichnet.A property that has only a set accessor is said to be a write-only property. Außer als Ziel einer Zuweisung ist es ein Kompilierzeitfehler, der auf eine schreibgeschützte Eigenschaft in einem Ausdruck verweist.Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

Im BeispielIn 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
    }
}

Das Button -Steuerelement deklariert Caption eine öffentliche Eigenschaft.the Button control declares a public Caption property. Der get -Accessor Caption der-Eigenschaft gibt die Zeichenfolge zurück, caption die im privaten Feld gespeichert ist.The get accessor of the Caption property returns the string stored in the private caption field. Der set -Accessor überprüft, ob sich der neue Wert vom aktuellen Wert unterscheidet. wenn dies der Fall ist, wird der neue Wert gespeichert und das Steuerelement neu gezeichnet.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. Eigenschaften folgen häufig dem oben gezeigten Muster: Der get -Accessor gibt einfach einen in einem privaten Feld gespeicherten Wert zurück, set und der-Accessor ändert dieses private Feld und führt dann alle zusätzlichen Aktionen aus, die erforderlich sind, um den Status des Objekts vollständig zu aktualisieren.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.

Bei der Button obigen Klasse ist Folgendes ein Beispiel für die Caption Verwendung der-Eigenschaft:Given 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

Hier wird der set -Accessor aufgerufen, indem der-Eigenschaft ein Wert zugewiesen wird, get und der-Accessor wird aufgerufen, indem auf die-Eigenschaft in einem Ausdruck verwiesen wird.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.

Die get - set und-Accessoren einer Eigenschaft sind keine unterschiedlichen Member, und es ist nicht möglich, die Accessoren einer Eigenschaft separat zu deklarieren.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. Daher ist es nicht möglich, dass die beiden Accessoren einer Eigenschaft mit Lese-/Schreibzugriff über unterschiedliche Zugriffsberechtigungen verfügen.As such, it is not possible for the two accessors of a read-write property to have different accessibility. Das BeispielThe 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; }
    }
}

deklariert keine einzige Lese-/Schreibeigenschaft.does not declare a single read-write property. Stattdessen werden zwei Eigenschaften mit demselben Namen deklariert, ein Schreib geschützter und ein Schreib geschützter.Rather, it declares two properties with the same name, one read-only and one write-only. Da zwei Member, die in derselben Klasse deklariert werden, nicht denselben Namen haben können, bewirkt das Beispiel, dass ein Kompilierzeitfehler auftritt.Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

Wenn eine abgeleitete Klasse eine Eigenschaft mit demselben Namen wie eine geerbte Eigenschaft deklariert, verbirgt die abgeleitete Eigenschaft die geerbte Eigenschaft in Bezug auf Lese-und Schreibvorgänge.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. Im BeispielIn the example

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

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

die P -Eigenschaft B in blendet die A -Eigenschaft in in Bezug auf das P lesen und schreiben aus.the P property in B hides the P property in A with respect to both reading and writing. Folglich in den AnweisungenThus, 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

die Zuweisung von b.P bewirkt, dass ein Kompilierzeitfehler gemeldet wird, da die P schreibgeschützte Eigenschaft in B die Schreib P geschützte Eigenschaft in Aausblendet.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. Beachten Sie jedoch, dass eine Umwandlung verwendet werden kann, um auf die P Hidden-Eigenschaft zuzugreifen.Note, however, that a cast can be used to access the hidden P property.

Im Gegensatz zu öffentlichen Feldern stellen Eigenschaften eine Trennung zwischen dem internen Zustand eines Objekts und seiner öffentlichen Schnittstelle dar.Unlike public fields, properties provide a separation between an object's internal state and its public interface. Sehen Sie sich das Beispiel an: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; }
    }
}

Hier verwendet die Label -Klasse zwei int Felder, x und y, um ihren Speicherort zu speichern.Here, the Label class uses two int fields, x and y, to store its location. X Der Speicherort wird sowohl als als Y auch Location als Eigenschaft des Typs Pointöffentlich verfügbar gemacht.The location is publicly exposed both as an X and a Y property and as a Location property of type Point. Wenn es in einer zukünftigen Version von Labelbequemer wird, den Speicherort Point als intern zu speichern, kann die Änderung vorgenommen werden, ohne dass sich dies auf die öffentliche Schnittstelle der Klasse auswirkt: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; }
    }
}

Waren x und y stattdessen Label Felder, wäre es unmöglich, eine solche Änderung an der-Klasse vorzunehmen. public readonlyHad x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

Das verfügbar machen des Zustands durch Eigenschaften ist nicht notwendigerweise weniger effizient, als Felder direkt verfügbar zu machen.Exposing state through properties is not necessarily any less efficient than exposing fields directly. Insbesondere wenn eine Eigenschaft nicht virtuell ist und nur eine kleine Menge an Code enthält, kann die Ausführungsumgebung Aufrufe von Accessoren durch den tatsächlichen Code der Accessoren ersetzen.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. Dieser Prozess wird als Inliningbezeichnet und macht den Eigenschafts Zugriff so effizient wie der Feld Zugriff, behält jedoch die höhere Flexibilität von Eigenschaften bei.This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Da das Aufrufen get eines Accessoren konzeptionell Äquivalent zum Lesen des Werts eines Felds ist, gilt es als ungültiges Programmier Format get für Accessoren, um Observable-Nebeneffekte zu haben.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. Im BeispielIn the example

class Counter
{
    private int next;

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

der Wert Next der-Eigenschaft hängt von der Häufigkeit ab, mit der zuvor auf die Eigenschaft zugegriffen wurde.the value of the Next property depends on the number of times the property has previously been accessed. Folglich erzeugt der Zugriff auf die-Eigenschaft einen beobachtbaren Nebeneffekt, und die-Eigenschaft sollte stattdessen als Methode implementiert werden.Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

Die "No Side-Effects"-Konvention get für Accessoren bedeutet nicht get , dass Accessoren immer so geschrieben werden müssen, dass Sie einfach in Feldern gespeicherte Werte zurückgeben.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. Accessoren get berechnen häufig den Wert einer Eigenschaft, indem Sie auf mehrere Felder zugreifen oder Methoden aufrufen.Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. Ein ordnungsgemäß entworfener get Accessor führt jedoch keine Aktionen aus, die Observable-Änderungen im Status des Objekts bewirken.However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

Eigenschaften können verwendet werden, um die Initialisierung einer Ressource zu verzögern, bis zu dem Zeitpunkt, zu dem Sie erstmals referenziert wird.Properties can be used to delay initialization of a resource until the moment it is first referenced. Zum Beispiel: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;
        }
    }
}

Die Console -Klasse enthält die drei InEigenschaften Out,, Errorund, die jeweils die standardmäßigen Eingabe-, Ausgabe-und Fehler Geräte darstellen.The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. Wenn diese Member als Eigenschaften verfügbar gemacht werden Console , kann die-Klasse Ihre Initialisierung verzögern, bis Sie tatsächlich verwendet werden.By exposing these members as properties, the Console class can delay their initialization until they are actually used. Beispielsweise nach dem ersten Verweis auf die Out -Eigenschaft, wie inFor example, upon first referencing the Out property, as in

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

der zugrunde TextWriter liegende für das Ausgabegerät wird erstellt.the underlying TextWriter for the output device is created. Wenn die Anwendung jedoch keinen Verweis auf die- In Eigenschaft Error und die-Eigenschaft durchführt, werden für diese Geräte keine Objekte erstellt.But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

Automatisch implementierte EigenschaftenAutomatically implemented properties

Eine automatisch implementierte Eigenschaft (oder Auto-Eigenschaft für Short) ist eine nicht abstrakte nicht-externe Eigenschaft mit nur Semikolon-Zugriffsmethoden.An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Auto-Eigenschaften müssen über einen get-Accessor verfügen und optional über einen Set-Accessor verfügen.Auto-properties must have a get accessor and can optionally have a set accessor.

Wenn eine Eigenschaft als automatisch implementierte Eigenschaft angegeben wird, ist ein verborgenes dahinter liegendes Feld für die Eigenschaft automatisch verfügbar, und die Accessoren werden implementiert, um aus dem dahinter liegenden Feld zu lesen und in dieses zu schreiben.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. Wenn die Auto-Eigenschaft keinen Set-Accessor aufweist, wird das Unterstützungs Feld readonly als (schreibgeschützteFelder) betrachtet.If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Genau wie ein readonly Feld kann auch eine nur-Getter-Auto-Eigenschaft im Text eines Konstruktors der einschließenden Klasse zugewiesen werden.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. Eine solche Zuweisung wird direkt dem schreibgeschützten Unterstützungs Feld der-Eigenschaft zugewiesen.Such an assignment assigns directly to the readonly backing field of the property.

Eine Auto-Eigenschaft kann optional über ein property_initializerverfügen, das direkt auf das Unterstützungs Feld als variable_initializer (Variableninitialisierer) angewendet wird.An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

Im Beispiel unten geschieht Folgendes:The following example:

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

entspricht der folgenden Deklaration: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; } }
}

Im Beispiel unten geschieht Folgendes:The following example:

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

entspricht der folgenden Deklaration: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; }
}

Beachten Sie, dass die Zuweisungen zum schreibgeschützten Feld zulässig sind, da Sie im Konstruktor auftreten.Notice that the assignments to the readonly field are legal, because they occur within the constructor.

ZugriffAccessibility

Wenn ein Accessor über eine accessor_modifierverfügt, wird die Zugriffs Domäne (Barrierefreiheits Domänen) der Zugriffsmethode mithilfe des deklarierten zugriffszugriffs auf die accessor_modifierfestgelegt.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. Wenn ein Accessor nicht über eine accessor_modifierverfügt, wird die Zugriffs Domäne des Accessors anhand der deklarierten Zugriffsmethode der Eigenschaft oder des Indexers bestimmt.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.

Das vorhanden sein eines accessor_modifier hat niemals Auswirkungen auf die Suche nach Membern (Operatoren) oder Überladungs Auflösung (Überladungs Auflösung).The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). Die Modifizierer für die Eigenschaft oder den Indexer bestimmen unabhängig vom Kontext des Zugriffs immer, an welche Eigenschaft oder welcher Indexer gebunden ist.The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

Nachdem eine bestimmte Eigenschaft oder ein Indexer ausgewählt wurde, werden die Barrierefreiheits Domänen der beteiligten spezifischen Accessoren verwendet, um zu bestimmen, ob diese Verwendung gültig ist: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:

  • Wenn die Verwendung als Wert (Werte von Ausdrücken) verwendet wird, muss get der Accessor vorhanden und zugänglich sein.If the usage is as a value (Values of expressions), the get accessor must exist and be accessible.
  • Wenn die Verwendung als Ziel einer einfachen Zuweisung (einfache Zuweisung) verwendet wird, muss der set -Accessor vorhanden und zugänglich sein.If the usage is as the target of a simple assignment (Simple assignment), the set accessor must exist and be accessible.
  • Wenn die Verwendung als Ziel der Verbund Zuweisung (Verbund Zuweisung) oder ++ als Ziel der Operatoren oder -- (Funktionsmember0,9, Aufruf Ausdrücke) verwendet wird, werden sowohl die get Accessoren als auch der set -Accessor muss vorhanden und zugänglich sein.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.

Im folgenden Beispiel wird die-Eigenschaft A.Text durch die-Eigenschaft B.Textausgeblendet, auch in Kontexten, in denen set nur der-Accessor aufgerufen wird.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. Im Gegensatz dazu kann die B.Count -Eigenschaft nicht von der M-Klasse aufgerufen werden, A.Count sodass stattdessen die barrierefreie Eigenschaft verwendet wird.In 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
    }
}

Ein Accessor, der zur Implementierung einer Schnittstelle verwendet wird, verfügt möglicherweise nicht über eine accessor_modifier.An accessor that is used to implement an interface may not have an accessor_modifier. Wenn nur ein Accessor verwendet wird, um eine Schnittstelle zu implementieren, kann der andere Accessor mit einem accessor_modifierdeklariert werden: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-und Abstract-EigenschaftenaccessorenVirtual, sealed, override, and abstract property accessors

Eine virtual Eigenschafts Deklaration gibt an, dass die Accessoren der Eigenschaft virtuell sind.A virtual property declaration specifies that the accessors of the property are virtual. Der virtual -Modifizierer gilt für beide Accessoren einer Lese-/Schreibeigenschaft – es ist nicht möglich, dass nur ein Accessor einer Eigenschaft mit Lese-/Schreibzugriff virtuell ist.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.

Eine abstract Eigenschafts Deklaration gibt an, dass die Accessoren der Eigenschaft virtuell sind, aber keine tatsächliche Implementierung der Accessoren bereitstellt.An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. Stattdessen sind nicht abstrakte abgeleitete Klassen erforderlich, um eine eigene Implementierung für die Accessoren bereitzustellen, indem die-Eigenschaft überschrieben wird.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Da ein Accessor für eine abstrakte Eigenschaften Deklaration keine tatsächliche Implementierung bereitstellt, besteht seine accessor_body einfach aus einem Semikolon.Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

Eine Eigenschafts Deklaration, die abstract den override -Modifizierer und den-Modifizierer enthält, gibt an, dass die Eigenschaft abstrakt ist, und überschreibtA property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Die Accessoren einer solchen Eigenschaft sind ebenfalls abstrakt.The accessors of such a property are also abstract.

Abstrakte Eigenschafts Deklarationen sind nur in abstrakten Klassen zulässig (abstrakte Klassen). Die Accessoren einer geerbten virtuellen Eigenschaft können in einer abgeleiteten Klasse überschrieben werden, indem Sie eine Eigenschaften Deklaration override einschließen, die eine-Direktive angibt.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. Dies wird als über schreibende Eigenschaften Deklarationbezeichnet.This is known as an overriding property declaration. Eine über schreibende Eigenschaften Deklaration deklariert keine neue Eigenschaft.An overriding property declaration does not declare a new property. Stattdessen werden lediglich die Implementierungen der Accessoren einer vorhandenen virtuellen Eigenschaft spezialisiert.Instead, it simply specializes the implementations of the accessors of an existing virtual property.

Eine über schreibende Eigenschaften Deklaration muss genau dieselben Zugriffsmodifizierer, denselben Typ und denselben Namen wie die geerbte Eigenschaft angeben.An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. Wenn die geerbte Eigenschaft nur über einen einzigen Accessor verfügt (d. h., wenn die geerbte Eigenschaft schreibgeschützt oder schreibgeschützt ist), muss die über schreibende Eigenschaft nur den Accessor enthalten.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. Wenn die geerbte Eigenschaft beide Accessoren enthält (d. h., wenn die geerbte Eigenschaft Lese-/Schreibzugriff hat), kann die über schreibende Eigenschaft entweder einen einzelnen Accessor oder beide Accessoren einschließen.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.

Eine über schreibende Eigenschaften Deklaration sealed kann den-Modifizierer enthalten.An overriding property declaration may include the sealed modifier. Durch die Verwendung dieses Modifizierers wird verhindert, dass eine abgeleitete Klasse die Eigenschaft weiter überschreibt.Use of this modifier prevents a derived class from further overriding the property. Die Accessoren einer versiegelten Eigenschaft sind ebenfalls versiegelt.The accessors of a sealed property are also sealed.

Mit Ausnahme der Unterschiede in der Deklaration und der Aufruf Syntax Verhalten sich virtuelle, versiegelte, Überschreibungs-und abstrakte Accessoren genauso wie virtuelle, versiegelte, Überschreibungs-und abstrakte Methoden.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Insbesondere gelten die in virtuellen Methoden, Methodenzum überschreiben, versiegelten Methodenund abstrakten Methoden beschriebenen Regeln so, als wären Accessoren Methoden einer entsprechenden Form:Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • Ein get -Accessor entspricht einer Parameter losen Methode mit einem Rückgabewert des Eigenschaftentyps und denselben Modifiziererwerten wie die enthaltende Eigenschaft.A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • Ein set -Accessor entspricht einer Methode mit einem einzelnen Wert Parameter des Eigenschaftentyps void , einem Rückgabetyp und denselben Modifiziererwerten wie die enthaltende Eigenschaft.A set accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property.

Im BeispielIn 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; }
}

Xist eine virtuelle schreibgeschützte Eigenschaft, Y ist eine virtuelle Lese-/Schreibeigenschaft und Z eine abstrakte Lese-/Schreibeigenschaft.X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. Da Z abstrakt ist, muss die enthaltende Klasse A auch als abstrakt deklariert werden.Because Z is abstract, the containing class A must also be declared abstract.

Eine Klasse, die von A abgeleitet wird, wird unten angezeigt: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; }
    }
}

Hier überschreiben die Deklarationen Yvon X, Z und Eigenschafts Deklarationen.Here, the declarations of X, Y, and Z are overriding property declarations. Jede Eigenschaften Deklaration stimmt genau mit den zugriffsmodifizierertypen und dem Namen der entsprechenden geerbten Eigenschaft überein.Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. Der get -Accessor X von und set der-Accessor von base Y verwenden das-Schlüsselwort, um auf die geerbten Accessoren zuzugreifen.The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. Die Deklaration Z von überschreibt beide abstrakten Accessoren – folglich gibt es keine ausstehenden abstrakten Funktionsmember Bin, B und es darf sich um eine nicht abstrakte Klasse handeln.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.

Wenn eine Eigenschaft als overridedeklariert wird, müssen alle überschriebenen Accessoren für den über schreibenden Code zugänglich sein.When a property is declared as an override, any overridden accessors must be accessible to the overriding code. Außerdem muss der deklarierte Zugriff sowohl für die Eigenschaft oder den Indexer selbst als auch für die Accessoren mit der der überschriebenen Member und Accessoren identisch sein.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. Zum Beispiel: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
    }
}

EreignisseEvents

Ein Ereignis ist ein Member, mit dem ein Objekt oder eine Klasse Benachrichtigungen bereitstellen kann.An event is a member that enables an object or class to provide notifications. Clients können ausführbaren Code durch Bereitstellen von Ereignis Handlernfür Ereignisse anfügen.Clients can attach executable code for events by supplying event handlers.

Ereignisse werden mit event_declarations deklariert: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
    ;

Ein event_declaration kann einen Satz von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), den new (den neuen Modifizierer), static (statisch und Instanz) enthalten. Methoden), virtual (virtuelle Methoden), override (Überschreibungs Methoden), sealed (versiegelte Methoden), abstract (abstrakte Methoden) und extern-Modifizierer (Externe Methoden).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.

Ereignis Deklarationen unterliegen den gleichen Regeln wie Methoden Deklarationen (Methoden) in Bezug auf gültige Kombinationen von modifiziererereignissen.Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Der Typ einer Ereignis Deklaration muss ein delegate_type (Verweis Typen) sein, und delegate_type muss mindestens so zugänglich sein wie das Ereignis selbst (Barrierefreiheits Einschränkungen).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).

Eine Ereignis Deklaration kann event_accessor_declarationsenthalten.An event declaration may include event_accessor_declarations. Wenn dies jedoch nicht der Fall ist, stellt der Compiler Sie für nicht-externe, nicht abstrakte Ereignisse automatisch bereit (Feld ähnliche Ereignisse). bei externen Ereignissen werden die Accessoren extern bereitgestellt.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.

Eine Ereignis Deklaration, die event_accessor_declarations auslässt, definiert mindestens ein Ereignis – eines für jede variable_declarators.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarators. Die Attribute und Modifizierer gelten für alle Member, die von einem solchen event_declarationdeklariert werden.The attributes and modifiers apply to all of the members declared by such an event_declaration.

Es handelt sich um einen Kompilierzeitfehler für ein event_declaration -Objekt, das sowohl den abstract-Modifizierer als auch das mit geschweiften Klammern getrennte event_accessor_declarationseinschließt.It is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

Wenn eine Ereignis Deklaration einen extern Modifizierer enthält, wird das Ereignis als externes Ereignisbezeichnet.When an event declaration includes an extern modifier, the event is said to be an external event. Da eine externe Ereignis Deklaration keine tatsächliche Implementierung bereitstellt, ist es ein Fehler, dass Sie sowohl den extern-Modifizierer als auch event_accessor_declarationseinschließen.Because an external event declaration provides no actual implementation, it is an error for it to include both the extern modifier and event_accessor_declarations.

Es handelt sich um einen Kompilierzeitfehler für eine variable_declarator einer Ereignis Deklaration mit einem abstract-oder external-Modifizierer, um ein variable_initializer-Zeichen einzuschließen.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.

Ein Ereignis kann als Linker Operand des += Operators und -= (Ereignis Zuweisung) verwendet werden.An event can be used as the left-hand operand of the += and -= operators (Event assignment). Diese Operatoren werden zum Anfügen von Ereignis Handlern an oder zum Entfernen von Ereignis Handlern aus einem Ereignis verwendet, und die Zugriffsmodifizierer des Ereignisses steuern die Kontexte, in denen solche Vorgänge zulässig sind.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.

Da += und-= die einzigen Vorgänge sind, die für ein Ereignis außerhalb des Typs zulässig sind, der das Ereignis deklariert, kann externer Code Handler für ein Ereignis hinzufügen und entfernen, aber nicht auf andere Weise die zugrunde liegende Ereignisliste abrufen oder ändern. Handler.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.

Bei einem Vorgang des Formulars x += y oder x -= y, wenn x ein Ereignis und der Verweis außerhalb des Typs, der die Deklaration von xenthält, erfolgt, hat das Ergebnis des Vorgangs den Typ void (im Gegensatz zu der Typ von xmit dem Wert von x nach der Zuweisung).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). Diese Regel verhindert, dass externer Code indirekt den zugrunde liegenden Delegaten eines Ereignisses untersucht.This rule prohibits external code from indirectly examining the underlying delegate of an event.

Das folgende Beispiel zeigt, wie Ereignishandler an Instanzen der Button -Klasse angefügt werden: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
    }
}

Hier erstellt der LoginDialog Instanzkonstruktor zwei Button -Instanzen und fügt Ereignishandler an die Click -Ereignisse an.Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

Feld ähnliche EreignisseField-like events

Im Programmtext der Klasse oder Struktur, die die Deklaration eines Ereignisses enthält, können bestimmte Ereignisse wie Felder verwendet werden.Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. Um auf diese Weise verwendet zu werden, darf ein Ereignis nicht abstract oder extern sein und darf event_accessor_declarationsnicht explizit enthalten.To be used in this way, an event must not be abstract or extern, and must not explicitly include event_accessor_declarations. Ein derartiges Ereignis kann in jedem Kontext verwendet werden, der ein Feld zulässt.Such an event can be used in any context that permits a field. Das-Feld enthält einen Delegaten(Delegaten), der auf die Liste der Ereignishandler verweist, die dem-Ereignis hinzugefügt wurden.The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. Wenn keine Ereignishandler hinzugefügt wurden, enthält nulldas Feld.If no event handlers have been added, the field contains null.

Im BeispielIn 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;
    }
}

Clickwird als Feld in der Button -Klasse verwendet.Click is used as a field within the Button class. Wie das Beispiel zeigt, kann das-Feld überprüft, geändert und in Delegataufrufausdrücken verwendet werden.As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. Die OnClick -Methode in Button der-Klasse löst das Click -Ereignis aus.The OnClick method in the Button class "raises" the Click event. Das Auslösen eines Ereignisses entspricht exakt dem Aufrufen des Delegaten, der durch das Ereignis repräsentiert wird, es gibt deshalb keine besonderen Sprachkonstrukte zum Auslösen von Ereignissen.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. Beachten Sie, dass dem Delegataufruf eine Prüfung vorangestellt ist, die sicherstellt, dass der Delegat nicht NULL ist.Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Außerhalb der Deklaration Button der += -Klasse kann Click der Member nur auf der linken Seite der Operatoren und -= verwendet werden, wie inOutside 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(...);

, der einen Delegaten an die Aufruf Liste des Click Ereignisses anfügt, undwhich appends a delegate to the invocation list of the Click event, and

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

entfernt einen Delegaten aus der Aufruf Liste des Click Ereignisses.which removes a delegate from the invocation list of the Click event.

Beim Kompilieren eines Feld ähnlichen Ereignisses erstellt der Compiler automatisch Speicher, um den Delegaten aufzunehmen, und erstellt Accessoren für das Ereignis, mit dem Ereignishandler zum Delegatfeld hinzugefügt oder daraus entfernt werden.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. Die hinzu Füge-und Entfernungs Vorgänge sind Thread sicher und können (jedoch nicht erforderlich) ausgeführt werden, während die Sperre (lock-Anweisung) für das enthaltende Objekt für ein Instanzereignis oder das Typobjekt (Anonyme Objekt Erstellungs Ausdrücke) aufrechterhalten werden. für ein statisches Ereignis.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.

Daher ist eine Instanzereignisdeklaration der folgenden Form:Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

wird in eine entsprechende kompiliert: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 */
        }
    }
}

In der += - XKlasse bewirken Verweise Ev auf auf der linken Seite der Operatoren und -= , dass die Add-und remove-Accessoren aufgerufen werden.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. Alle anderen Verweise auf Ev werden kompiliert, um stattdessen auf das __Ev ausgeblendete Feld zu verweisen (Member Access).All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). Der Name "__Ev" ist willkürlich; das ausgeblendete Feld kann einen beliebigen Namen oder keinen Namen haben.The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

EreignisaccessorenEvent accessors

Ereignis Deklarationen lassen event_accessor_declarationsüblicherweise aus, wie im obigen Beispiel Button.Event declarations typically omit event_accessor_declarations, as in the Button example above. Eine Situation hierfür ist der Fall, in dem die Speicherkosten eines Felds pro Ereignis nicht zulässig sind.One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. In solchen Fällen kann eine Klasse event_accessor_declarations einschließen und einen privaten Mechanismus zum Speichern der Liste von Ereignis Handlern verwenden.In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

Der event_accessor_declarations eines Ereignisses gibt die ausführbaren Anweisungen an, die dem Hinzufügen und Entfernen von Ereignis Handlern zugeordnet sind.The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

Die Accessordeklarationen bestehen aus einem add_accessor_declaration -und einem remove_accessor_declaration-Operator.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Jede Accessordeklaration besteht aus dem add Token remove oder gefolgt von einem- Block.Each accessor declaration consists of the token add or remove followed by a block. Der einem add_accessor_declaration zugeordnete Block gibt die Anweisungen an, die beim Hinzufügen eines Ereignis Handlers ausgeführt werden sollen, und der mit einem remove_accessor_declaration -Block verknüpfte Block gibt die auszuführenden Anweisungen an. Wenn ein Ereignishandler entfernt wird.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.

Jede add_accessor_declaration und remove_accessor_declaration entspricht einer Methode mit einem einzelnen Wert Parameter des Ereignis Typs und einem void-Rückgabetyp.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. Der implizite Parameter eines Ereignis Accessors heißt value.The implicit parameter of an event accessor is named value. Wenn ein Ereignis in einer Ereignis Zuweisung verwendet wird, wird der entsprechende Ereignis Accessor verwendet.When an event is used in an event assignment, the appropriate event accessor is used. Insbesondere, wenn der Zuweisungs Operator += ist, wird der Add-Accessor verwendet, und wenn der Zuweisungs Operator ist -= , wird der remove-Accessor verwendet.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 beiden Fällen wird der rechte Operand des Zuweisungs Operators als Argument für den Ereignis Accessor verwendet.In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. Der-Block eines add_accessor_declaration -oder remove_accessor_declaration -muss den Regeln für void-Methoden entsprechen, die im Methoden Textbeschrieben werden.The block of an add_accessor_declaration or a remove_accessor_declaration must conform to the rules for void methods described in Method body. Insbesondere return -Anweisungen in einem solchen Block dürfen keinen Ausdruck angeben.In particular, return statements in such a block are not permitted to specify an expression.

Da ein Ereignis Accessor implizit einen Parameter mit dem valueNamen aufweist, handelt es sich um einen Kompilierzeitfehler für eine lokale Variable oder Konstante, die in einem Ereignis Accessor deklariert wurde, um diesen Namen zu haben.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.

Im BeispielIn 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);
    }
}

die Control -Klasse implementiert einen internen Speichermechanismus für-Ereignisse.the Control class implements an internal storage mechanism for events. Die AddEventHandler -Methode ordnet einem Schlüssel einen Delegatwert zu GetEventHandler , die Methode gibt den derzeit einem Schlüssel zugeordneten Delegaten RemoveEventHandler zurück, und die Methode entfernt einen Delegaten als Ereignishandler für das angegebene Ereignis.The AddEventHandler method associates a delegate value with a key, the GetEventHandler method returns the delegate currently associated with a key, and the RemoveEventHandler method removes a delegate as an event handler for the specified event. Vermutlich ist der zugrunde liegende Speichermechanismus so konzipiert, dass es keine Kosten für das Zuordnen eines null Delegatwerts zu einem Schlüssel gibt. daher verbrauchen nicht behandelte Ereignisse keinen Speicherplatz.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.

Statische Ereignisse und InstanzereignisseStatic and instance events

Wenn eine Ereignis Deklaration einen static Modifizierer enthält, wird das Ereignis als statisches Ereignisbezeichnet.When an event declaration includes a static modifier, the event is said to be a static event. Wenn kein static Modifizierer vorhanden ist, wird das Ereignis als Instanzereignisbezeichnet.When no static modifier is present, the event is said to be an instance event.

Ein statisches Ereignis ist nicht mit einer bestimmten Instanz verknüpft, und es ist ein Kompilierzeitfehler, auf this den in den Accessoren eines statischen Ereignisses verwiesen wird.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.

Ein Instanzereignis ist einer bestimmten Instanz einer Klasse zugeordnet, und auf diese Instanz kann in den Accessoren dieses Ereignisses als this (dieser Zugriff) zugegriffen werden.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.

Wenn auf ein Ereignis in einem member_access -Element (Member Access) der Form E.M verwiesen wird, muss, wenn M ein statisches Ereignis ist, E einen Typ angeben, der M enthält, und wenn M ein Instanzereignis ist, muss E eine Instanz eines Typs angeben, der enthält. M.When 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.

Die Unterschiede zwischen statischen und Instanzmembern werden in statischen und Instanzmembernausführlicher erläutert.The differences between static and instance members are discussed further in Static and instance members.

Virtual-, sealed-, override-und Abstract-EreignisaccessorenVirtual, sealed, override, and abstract event accessors

Eine virtual -Ereignis Deklaration gibt an, dass die Accessoren dieses Ereignisses virtuell sind.A virtual event declaration specifies that the accessors of that event are virtual. Der virtual -Modifizierer gilt für beide Accessoren eines Ereignisses.The virtual modifier applies to both accessors of an event.

Eine abstract Ereignis Deklaration gibt an, dass die Accessoren des Ereignisses virtuell sind, aber keine tatsächliche Implementierung der Accessoren bereitstellt.An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. Stattdessen sind nicht abstrakte abgeleitete Klassen erforderlich, um eine eigene Implementierung für die Accessoren bereitzustellen, indem das-Ereignis überschrieben wird.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Da eine abstrakte Ereignis Deklaration keine tatsächliche Implementierung bereitstellt, kann Sie keine durch Klammern getrennte event_accessor_declarationsbereitstellen.Because an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

Eine Ereignis Deklaration, die den abstract - override Modifizierer und den-Modifizierer enthält, gibt an, dass das Ereignis abstrakt ist, und überschreibt einAn event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. Die Accessoren eines solchen Ereignisses sind ebenfalls abstrakt.The accessors of such an event are also abstract.

Abstrakte Ereignis Deklarationen sind nur in abstrakten Klassen zulässig (abstrakte Klassen).Abstract event declarations are only permitted in abstract classes (Abstract classes).

Die Accessoren eines geerbten virtuellen Ereignisses können durch Einschließen einer Ereignis Deklaration, die einen override Modifizierer angibt, in einer abgeleiteten Klasse überschrieben werden.The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override modifier. Dies wird als über schreibende Ereignis Deklarationbezeichnet.This is known as an overriding event declaration. Eine über schreibende Ereignis Deklaration deklariert kein neues Ereignis.An overriding event declaration does not declare a new event. Stattdessen werden lediglich die Implementierungen der Accessoren eines vorhandenen virtuellen Ereignisses spezialisiert.Instead, it simply specializes the implementations of the accessors of an existing virtual event.

Eine über schreibende Ereignis Deklaration muss genau dieselben Zugriffsmodifizierer, denselben Typ und denselben Namen wie das überschriebene Ereignis angeben.An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

Eine über schreibende Ereignis Deklaration sealed kann den-Modifizierer enthalten.An overriding event declaration may include the sealed modifier. Durch die Verwendung dieses Modifizierers wird verhindert, dass eine abgeleitete Klasse das Ereignis weiter überschreibt.Use of this modifier prevents a derived class from further overriding the event. Die Accessoren eines versiegelten Ereignisses werden ebenfalls versiegelt.The accessors of a sealed event are also sealed.

Es ist ein Kompilierzeitfehler, wenn eine über schreibende Ereignis Deklaration einen new -Modifizierer einschließt.It is a compile-time error for an overriding event declaration to include a new modifier.

Mit Ausnahme der Unterschiede in der Deklaration und der Aufruf Syntax Verhalten sich virtuelle, versiegelte, Überschreibungs-und abstrakte Accessoren genauso wie virtuelle, versiegelte, Überschreibungs-und abstrakte Methoden.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Insbesondere gelten die in Virtual Methods, override Methods, sealed Methodsund abstract Methods beschriebenen Regeln so, als wären Accessoren Methoden einer entsprechenden Form.Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form. Jeder-Accessor entspricht einer Methode mit einem einzelnen value-Parameter des Ereignis Typs, einem void Rückgabetyp und denselben Modifiziererwerten wie das enthaltende Ereignis.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.

IndexerIndexers

Ein Indexer ist ein Member, mit dem ein Objekt auf die gleiche Weise wie ein Array indiziert werden kann.An indexer is a member that enables an object to be indexed in the same way as an array. Indexer werden mithilfe von indexer_declarations deklariert: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 ';'
    ;

Ein indexer_declaration kann einen Satz von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), den new (den neuen Modifizierer), virtual (virtuelle Methoden) enthalten. ), override (Überschreibungs Methoden), sealed (versiegelte Methoden), abstract (abstrakte Methoden) und extern (Externe Methoden)-Modifizierer.An indexer_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

Indexer-Deklarationen unterliegen den gleichen Regeln wie Methoden Deklarationen (Methoden) in Bezug auf gültige Kombinationen von Modifizierern, wobei die einzige Ausnahme darin besteht, dass der statische Modifizierer in einer Indexer-Deklaration nicht zulässig ist.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.

Die virtualmodifiziererer override, und abstract schließen sich gegenseitig aus, außer in einem Fall.The modifiers virtual, override, and abstract are mutually exclusive except in one case. Die abstract Modifizierer und override können zusammen verwendet werden, damit ein abstrakter Indexer einen virtuellen überschreiben kann.The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

Der Typ einer Indexer-Deklaration gibt den Elementtyp des Indexers an, der von der Deklaration eingeführt wird.The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. Es sei denn , der Indexer ist eine explizite Schnittstellenmember-Implementierung, gefolgt vom this-Schlüsselwort.Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. Für eine explizite Implementierung des Schnittstellenmembers folgt der Typ INTERFACE_TYPE, a "." und das Schlüsselwort this.For an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. Im Gegensatz zu anderen Membern haben Indexer keine benutzerdefinierten Namen.Unlike other members, indexers do not have user-defined names.

Formal_parameter_list gibt die Parameter des Indexers an.The formal_parameter_list specifies the parameters of the indexer. Die Liste formaler Parameter eines Indexers entspricht der einer Methode (Methoden Parameter), mit dem Unterschied, dass mindestens ein Parameter angegeben werden muss und dass die ref - out und-Parametermodifizierer nicht zulässig sind.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.

Der Typ eines Indexers und alle Typen, auf die in formal_parameter_list verwiesen wird, müssen mindestens so zugänglich sein wie der Indexer selbst (Barrierefreiheits Einschränkungen).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).

Ein indexer_body kann entweder aus einem Accessor-Text oder einem Ausdrucks Körperbestehen.An indexer_body may either consist of an accessor body or an expression body. In einem Accessor-Text muss accessor_declarations, der in "{" und "}"-Token eingeschlossen werden muss, die Accessoren (Accessoren) der Eigenschaft deklarieren.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Die Accessoren geben die ausführbaren Anweisungen an, die dem Lesen und Schreiben der Eigenschaft zugeordnet sind.The accessors specify the executable statements associated with reading and writing the property.

Ein Ausdrucks Text, der aus=>"" gefolgt von einem E Ausdruck und einem Semikolon besteht, entspricht exakt dem { get { return E; } }Anweisungs Text und kann daher nur zum Angeben von nur-Getter-indexatoren verwendet werden, bei denen das Ergebnis des Getters wird von einem einzelnen Ausdruck angegeben.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.

Obwohl die Syntax für den Zugriff auf ein Indexer-Element mit der Syntax für ein Array Element identisch ist, wird ein Indexer-Element nicht als Variable klassifiziert.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. Folglich ist es nicht möglich, ein Indexer-Element als ref -oder out -Argument zu übergeben.Thus, it is not possible to pass an indexer element as a ref or out argument.

Die Liste der formalen Parameter eines Indexers definiert die Signatur (Signaturen und überladen) des Indexers.The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. Insbesondere besteht die Signatur eines Indexers aus der Anzahl und den Typen der formalen Parameter.Specifically, the signature of an indexer consists of the number and types of its formal parameters. Der Elementtyp und die Namen der formalen Parameter sind nicht Teil der Signatur eines Indexers.The element type and names of the formal parameters are not part of an indexer's signature.

Die Signatur eines Indexer muss sich von den Signaturen aller anderen Indexer unterscheiden, die in derselben Klasse deklariert sind.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Indexer und Eigenschaften sind in der Konzeption sehr ähnlich, unterscheiden sich jedoch wie folgt:Indexers and properties are very similar in concept, but differ in the following ways:

  • Eine Eigenschaft wird anhand ihres Namens identifiziert, während ein Indexer durch die Signatur identifiziert wird.A property is identified by its name, whereas an indexer is identified by its signature.
  • Der Zugriff auf eine Eigenschaft erfolgt über einen Simple_name (simple names) oder einen member_access (Member Access), während auf ein Indexer-Element über einen element_access (Indexer-Zugriff) zugegriffen wird.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).
  • Eine Eigenschaft kann ein static Member sein, während ein Indexer immer ein Instanzmember ist.A property can be a static member, whereas an indexer is always an instance member.
  • Ein get -Accessor einer Eigenschaft entspricht einer Methode ohne Parameter, während ein get -Accessor eines Indexers einer Methode mit derselben formalen Parameterliste wie der Indexer entspricht.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.
  • Ein set -Accessor einer Eigenschaft entspricht einer Methode mit einem einzelnen Parameter mit dem valueNamen, während set ein Accessor eines Indexers einer Methode mit derselben formalen Parameterliste wie der Indexer entspricht, zuzüglich eines zusätzlichen Parameters. mit valuedem Namen.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.
  • Es ist ein Kompilierzeitfehler für einen Indexer-Accessor, eine lokale Variable mit dem gleichen Namen wie ein Indexer-Parameter zu deklarieren.It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • In einer über schreibenden Eigenschaften Deklaration wird auf die geerbte Eigenschaft base.Pmithilfe der P -Syntax zugegriffen, wobei der Eigenschaftsname ist.In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. In einer über schreibenden Indexer-Deklaration wird auf den geerbten Indexer mithilfe E der-Syntax base[E]zugegriffen, wobei eine durch Kommas getrennte Liste von Ausdrücken ist.In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • Es gibt kein Konzept für einen "automatisch implementierten Indexer".There is no concept of an "automatically implemented indexer". Es ist ein Fehler, wenn ein nicht abstrakter, nicht externer Indexer mit Semikolon-Accessoren vorliegt.It is an error to have a non-abstract, non-external indexer with semicolon accessors.

Abgesehen von diesen Unterschieden gelten alle in Accessoren und automatisch implementierten Eigenschaften definierten Regeln sowohl für Indexer-Accessoren als auch für Eigenschaftenaccessoren.Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

Wenn eine Indexer-Deklaration extern einen Modifizierer enthält, wird der Indexer als externer Indexerbezeichnet.When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer. Da eine externe Indexer-Deklaration keine tatsächliche Implementierung bereitstellt, besteht jede Ihrer accessor_declarations aus einem Semikolon.Because an external indexer declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Im folgenden Beispiel wird eine BitArray Klasse deklariert, die einen Indexer für den Zugriff auf die einzelnen Bits im BitArray implementiert.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);
            }
        }
    }
}

Eine Instanz der BitArray -Klasse beansprucht wesentlich weniger Arbeitsspeicher als eine bool[] entsprechende (da jeder Wert des ersten-Werts nur ein Bit und nicht das zweite Byte) beansprucht, aber die gleichen Vorgänge wie eine bool[]zulässt.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[].

Die folgende CountPrimes Klasse verwendet einen BitArray und den klassischen "Sieve"-Algorithmus, um die Anzahl von PRIMES zwischen 1 und einem angegebenen Maximum zu berechnen: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);
    }
}

Beachten Sie, dass die Syntax für den Zugriff BitArray auf Elemente von exakt der gleiche ist bool[]wie für ein.Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

Im folgenden Beispiel wird eine 26 * 10-Raster Klasse gezeigt, die über einen Indexer mit zwei Parametern verfügt.The following example shows a 26 * 10 grid class that has an indexer with two parameters. Der erste Parameter muss ein groß-oder Kleinbuchstabe im Bereich A-Z sein, und der zweite Parameter muss eine ganze Zahl im Bereich 0-9 sein.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;
        }
    }
}

Überladen von IndexernIndexer overloading

Die Regeln der Indexer-Überladungs Auflösung werden unter Typrückschlussbeschrieben.The indexer overload resolution rules are described in Type inference.

OperatorenOperators

Ein Operator ist ein Member, der die Bedeutung eines Ausdrucks Operators definiert, der auf Instanzen der Klasse angewendet werden kann.An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. Operatoren werden mit operator_declarations deklariert: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 ';'
    | ';'
    ;

Es gibt drei Kategorien von über ladbaren Operatoren: Unäre Operatoren (unäre Operatoren), binäre Operatoren (binäre Operatoren) und Konvertierungs Operatoren (Konvertierungs Operatoren).There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

Der operator_body ist entweder ein Semikolon, ein Anweisungs Text oder ein Ausdrucks Körper.The operator_body is either a semicolon, a statement body or an expression body. Ein Anweisungs Text besteht aus einem- Block, der die auszuführenden Anweisungen angibt, wenn der Operator aufgerufen wird.A statement body consists of a block, which specifies the statements to execute when the operator is invoked. Der- Block muss den Regeln für Rückgabe Methoden entsprechen, die im Methoden Textbeschrieben werden.The block must conform to the rules for value-returning methods described in Method body. Ein Ausdrucks Körper besteht aus => , gefolgt von einem Ausdruck und einem Semikolon und deutet auf einen einzelnen Ausdruck hin, der beim Aufrufen des Operators ausgeführt werden soll.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked.

Bei extern-Operatoren besteht das operator_body einfach aus einem Semikolon.For extern operators, the operator_body consists simply of a semicolon. Für alle anderen Operatoren ist operator_body entweder ein Block Körper oder ein Ausdrucks Körper.For all other operators, the operator_body is either a block body or an expression body.

Die folgenden Regeln gelten für alle Operator Deklarationen:The following rules apply to all operator declarations:

  • Eine Operator Deklaration muss sowohl einen public -als static auch einen-Modifizierer enthalten.An operator declaration must include both a public and a static modifier.
  • Die Parameter eines Operators müssen Wert Parameter (value-Parameter) sein.The parameter(s) of an operator must be value parameters (Value parameters). Es handelt sich um einen Kompilierzeitfehler für eine Operator Deklaration out zum angeben ref von-oder-Parametern.It is a compile-time error for an operator declaration to specify ref or out parameters.
  • Die Signatur eines Operators (unäre Operatoren, binäre Operatorenund Konvertierungs Operatoren) muss sich von den Signaturen aller anderen Operatoren unterscheiden, die in derselben Klasse deklariert sind.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.
  • Alle Typen, auf die in einer Operator Deklaration verwiesen wird, müssen mindestens so zugänglich sein wie der Operator selbst (Barrierefreiheits Einschränkungen).All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • Es ist ein Fehler, dass derselbe Modifizierer mehrmals in einer Operator Deklaration angezeigt wird.It is an error for the same modifier to appear multiple times in an operator declaration.

Jede Operator Kategorie erzwingt zusätzliche Einschränkungen, wie in den folgenden Abschnitten beschrieben.Each operator category imposes additional restrictions, as described in the following sections.

Wie bei anderen Membern werden Operatoren, die in einer Basisklasse deklariert werden, von abgeleiteten Klassen geerbt.Like other members, operators declared in a base class are inherited by derived classes. Da Operator Deklarationen immer die Klasse oder Struktur erfordern, in der der Operator deklariert ist, um an der Signatur des Operators teilzunehmen, ist es nicht möglich, dass ein Operator, der in einer abgeleiteten Klasse deklariert ist, einen in einer Basisklasse deklarierten Operator ausblenden kann.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. Daher ist der new -Modifizierer in einer Operator Deklaration nie erforderlich und daher nie zulässig.Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

Weitere Informationen zu unären und binären Operatoren finden Sie unter Operatoren.Additional information on unary and binary operators can be found in Operators.

Weitere Informationen zu Konvertierungs Operatoren finden Sie unter benutzerdefinierte Konvertierungen.Additional information on conversion operators can be found in User-defined conversions.

Unäre OperatorenUnary operators

Die folgenden Regeln gelten für unäre Operator Deklarationen, T wobei den Instanztyp der Klasse oder Struktur bezeichnet, die die Operator Deklaration enthält:The following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Ein unärer +Operator -, !, oder ~ muss einen einzelnen Parameter vom Typ T oder T? annehmen und kann jeden beliebigen Typ zurückgeben.A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • Ein unärer ++ or -- -Operator muss einen einzelnen Parameter vom Typ T oder T? annehmen, und er muss denselben Typ oder einen von ihm abgeleiteten Typ zurückgeben.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.
  • Ein unärer true or false -Operator muss einen einzelnen Parameter vom Typ T oder T? annehmen und muss den boolTyp zurückgeben.A unary true or false operator must take a single parameter of type T or T? and must return type bool.

Die Signatur eines unären Operators besteht aus dem Operator Token (+, -, !, ~, ++, --, trueoder false) und dem Typ des einzelnen formalen Parameters.The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. Der Rückgabetyp ist weder Teil der Signatur eines unären Operators noch der Name des formalen Parameters.The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

Die true unären Operatoren und false erfordern eine paarweise Deklaration.The true and false unary operators require pair-wise declaration. Ein Kompilierzeitfehler tritt auf, wenn eine Klasse einen dieser Operatoren deklariert, ohne auch den anderen zu deklarieren.A compile-time error occurs if a class declares one of these operators without also declaring the other. Die true Operatoren und false werden weiter unten in benutzerdefinierten bedingten logischen Operatoren und booleschen Ausdrückenbeschrieben.The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

Das folgende Beispiel zeigt eine-Implementierung und die nachfolg operator ++ Ende Verwendung von für eine ganzzahlige Vektor Klasse: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
    }
}

Beachten Sie, dass die Operator-Methode den Wert zurückgibt, der durch das Hinzufügen von 1 zum Operanden erzeugt wird, genau wie die Postfix-Inkrement-und Dekrementoperatoren (postfix-Inkrement-und Dekrementoperatoren) und die Präfix-Inkrement- Inkrement-und Dekrementoperatoren).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). Anders als C++in muss diese Methode den Wert des Operanden nicht direkt ändern.Unlike in C++, this method need not modify the value of its operand directly. Tatsächlich würde das Ändern des Operanden-Werts gegen die Standard Semantik des Postfix-Inkrementoperators verstoßen.In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

Binäre OperatorenBinary operators

Die folgenden Regeln gelten für binäre Operator Deklarationen, T wobei den Instanztyp der Klasse oder Struktur bezeichnet, die die Operator Deklaration enthält:The following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Ein binärer nicht Verschiebungs Operator muss zwei Parameter annehmen, von denen mindestens eine den Typ T oder T?aufweisen muss und jeden beliebigen Typ zurückgeben kann.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.
  • Ein << binärer >> or T? -Operator muss zwei Parameter annehmen. der erste muss den- T Typ aufweisen, und der zweite muss den- int Typ int?oder aufweisen und jeden beliebigen Typ zurückgeben.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.

Die Signatur+eines binären Operators besteht aus dem Operator Token (, << | * & / -,,, %,,, ^,, >>, ==, ,,>, oder>=) unddieTypenderbeidenformalenParameter<=. != <The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. Der Rückgabetyp und die Namen der formalen Parameter sind nicht Teil der Signatur eines binären Operators.The return type and the names of the formal parameters are not part of a binary operator's signature.

Bestimmte binäre Operatoren erfordern eine paarweise Deklaration.Certain binary operators require pair-wise declaration. Für jede Deklaration eines der beiden Operatoren eines Paares muss eine entsprechende Deklaration des anderen Operators des Paars vorhanden sein.For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Zwei Operator Deklarationen stimmen überein, wenn Sie den gleichen Rückgabetyp und denselben Typ für jeden Parameter aufweisen.Two operator declarations match when they have the same return type and the same type for each parameter. Die folgenden Operatoren erfordern eine paarweise Deklaration:The following operators require pair-wise declaration:

  • operator == und operator !=operator == and operator !=
  • operator > und operator <operator > and operator <
  • operator >= und operator <=operator >= and operator <=

KonvertierungsoperatorenConversion operators

Eine Konvertierungs Operator Deklaration führt eine benutzerdefinierte Konvertierung (benutzerdefinierte Konvertierungen) ein, mit der die vordefinierten und expliziten Konvertierungen erweitert werden.A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

Eine Konvertierungs Operator Deklaration, implicit die das-Schlüsselwort enthält, führt eine benutzerdefinierte implizite Konvertierung ein.A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implizite Konvertierungen können in einer Vielzahl von Situationen auftreten, einschließlich Funktionsmember-Aufrufe, Umwandlungs Ausdrücke und Zuweisungen.Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. Dies wird in implizite Konvertierungenbeschrieben.This is described further in Implicit conversions.

Eine Konvertierungs Operator Deklaration, explicit die das-Schlüsselwort enthält, führt eine benutzerdefinierte explizite Konvertierung ein.A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Explizite Konvertierungen können in Umwandlungs Ausdrücken auftreten und werden weiter unten in expliziten Konvertierungenbeschrieben.Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

Ein Konvertierungs Operator konvertiert von einem Quelltyp, der durch den Parametertyp des Konvertierungs Operators angegeben ist, in einen Zieltyp, der durch den Rückgabetyp des Konvertierungs Operators angegeben wird.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.

Verwenden Sie für einen angegebenen S Quelltyp und TZieltyp S , T wenn oder NULL-Werte zulassen S0 T0 , T0 die zugrunde liegenden Typen, und verweisen S0 Sie andernfalls auf. S gleichT bzw.For 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. Eine Klasse oder Struktur darf nur dann eine Konvertierung von einem Quelltyp S in einen Zieltyp T deklarieren, wenn Folgendes zutrifft: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:

  • S0und T0 sind unterschiedliche Typen.S0 and T0 are different types.
  • Entweder S0 oderT0 ist der Klassen-oder Strukturtyp, in dem die Operator Deklaration stattfindet.Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • Weder S0 noch T0 ist ein INTERFACE_TYPE-Wert.Neither S0 nor T0 is an interface_type.
  • Ohne Benutzer S definierte Konvertierungen ist eine Konvertierung von zu T oder von T zu Snicht vorhanden.Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

Bei diesen Regeln werden alle Typparameter, die mit S oder T verknüpft sind, als eindeutige Typen betrachtet, die keine Vererbungs Beziehung mit anderen Typen aufweisen, und Einschränkungen für diese Typparameter werden ignoriert.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.

Im BeispielIn 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
}

die ersten beiden Operator Deklarationen sind zulässig, da für T Indexer-Indexer, und int string bzw. als eindeutige Typen ohne Beziehung angesehen werden.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. Der dritte Operator ist jedoch ein Fehler, da C<T> die Basisklasse von D<T>ist.However, the third operator is an error because C<T> is the base class of D<T>.

Aus der zweiten Regel folgt, dass ein Konvertierungs Operator entweder in oder aus dem Klassen-oder Strukturtyp konvertieren muss, in dem der Operator deklariert ist.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. Beispielsweise ist es möglich, dass ein Klassen- C oder Strukturtyp eine Konvertierung von C in int und von int in C, aber nicht von int in booldefiniert.For example, it is possible for a class or struct type C to define a conversion from C to int and from int to C, but not from int to bool.

Es ist nicht möglich, eine vordefinierte Konvertierung direkt neu zu definieren.It is not possible to directly redefine a pre-defined conversion. Folglich ist es nicht zulässig, Konvertierungs Operatoren von oder object in zu konvertieren, da zwischen object und allen anderen Typen bereits implizite und explizite Konvertierungen vorhanden sind.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. Ebenso kann weder die Quelle noch die Zieltypen einer Konvertierung ein Basistyp der anderen sein, da eine Konvertierung dann bereits vorhanden wäre.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.

Es ist jedoch möglich, Operatoren für generische Typen zu deklarieren, die für bestimmte Typargumente Konvertierungen angeben, die bereits als vordefinierte Konvertierungen vorhanden sind.However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. Im BeispielIn the example

struct Convertible<T>
{
    public static implicit operator Convertible<T>(T value) {...}
    public static explicit operator T(Convertible<T> value) {...}
}

Wenn Type object als Typargument für Tangegeben wird, deklariert der zweite Operator eine Konvertierung, die bereits vorhanden ist (ein implizites und somit auch eine explizite Konvertierung von einem beliebigen objectTyp in den Typ).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 Fällen, in denen eine vordefinierte Konvertierung zwischen zwei Typen vorhanden ist, werden alle benutzerdefinierten Konvertierungen zwischen diesen Typen ignoriert.In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. Dies gilt insbesondere in folgenden Fällen:Specifically:

  • Wenn eine vordefinierte implizite Konvertierung (implizite Konvertierungen S ) vom Typ in den Typ Tvorhanden ist, werden alle benutzerdefinierten Konvertierungen (implizit oder explizit T ) von S zu ignoriert.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.
  • Wenn eine vordefinierte explizite Konvertierung (explizite Konvertierungen S ) vom Typ in den Typ Tvorhanden ist, werden alle benutzerdefinierten expliziten T Konvertierungen von S in ignoriert.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. WeiterFurthermore:

Wenn T ein Schnittstellentyp ist, werden benutzerdefinierte implizite Konvertierungen T von S in ignoriert.If T is an interface type, user-defined implicit conversions from S to T are ignored.

Andernfalls werden benutzerdefinierte implizite Konvertierungen von S in T immer noch berücksichtigt.Otherwise, user-defined implicit conversions from S to T are still considered.

Für alle Typen object, jedoch verursachen die Convertible<T> vom oben genannten Typ deklarierten Operatoren keinen Konflikt mit vordefinierten Konvertierungen.For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. Zum Beispiel: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
}

Für den Typ objectverbergen vordefinierte Konvertierungen jedoch die benutzerdefinierten Konvertierungen in allen Fällen, aber eine: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
}

Benutzerdefinierte Konvertierungen dürfen nicht von oder in INTERFACE_TYPEs konvertiert werden.User-defined conversions are not allowed to convert from or to interface_types. Diese Einschränkung stellt insbesondere sicher, dass keine benutzerdefinierten Transformationen beim Konvertieren in ein INTERFACE_TYPEauftreten und dass eine Konvertierung in ein INTERFACE_TYPE nur erfolgreich ist, wenn das konvertierte Objekt tatsächlich das angegebene INTERFACE_TYPE.In particular, this restriction ensures that no user-defined transformations occur when converting to an interface_type, and that a conversion to an interface_type succeeds only if the object being converted actually implements the specified interface_type.

Die Signatur eines Konvertierungs Operators besteht aus dem Quelltyp und dem Zieltyp.The signature of a conversion operator consists of the source type and the target type. (Beachten Sie, dass dies die einzige Form der Member ist, für die der Rückgabetyp an der Signatur teilnimmt.) Die implicit - explicit oder-Klassifizierung eines Konvertierungs Operators ist nicht Teil der Signatur des Operators.(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. Daher kann eine Klasse oder Struktur nicht sowohl einen implicit -als auch einen explicit -Konvertierungs Operator mit denselben Quell-und Zieltypen deklarieren.Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

Im Allgemeinen sollten benutzerdefinierte implizite Konvertierungen so entworfen werden, dass Sie niemals Ausnahmen auslösen und nie Informationen verlieren.In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. Wenn eine benutzerdefinierte Konvertierung Ausnahmen auslösen kann (z. b. weil das Quell Argument außerhalb des gültigen Bereichs liegt) oder Informationen verloren geht (z. b. das Verwerfen von großen Bits), sollte diese Konvertierung als explizite Konvertierung definiert werden.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.

Im BeispielIn 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);
    }
}

die Konvertierung von Digit in byte ist implizit, weil Sie niemals Ausnahmen auslöst oder Informationen verliert, aber die Konvertierung byte von Digit in ist explizit Digit , da nur eine Teilmenge der möglichen Werte einer byte.the 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.

InstanzkonstruktorenInstance constructors

Ein Instanzkonstruktor ist ein Member, der die erforderlichen Aktionen zum Initialisieren einer Instanz einer Klasse implementiert.An instance constructor is a member that implements the actions required to initialize an instance of a class. Instanzkonstruktoren werden mit constructor_declarations deklariert: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
    | ';'
    ;

Ein constructor_declaration kann einen Satz von Attributen (Attribute), eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer) und einen extern-Modifizierer (Externe Methoden) enthalten.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. Eine Konstruktordeklaration darf nicht denselben Modifizierer mehrmals einschließen.A constructor declaration is not permitted to include the same modifier multiple times.

Der Bezeichner eines constructor_declarator muss der Klasse, in der der Instanzkonstruktor deklariert ist, einen Namen benennen.The identifier of a constructor_declarator must name the class in which the instance constructor is declared. Wenn ein anderer Name angegeben wird, tritt ein Kompilierzeitfehler auf.If any other name is specified, a compile-time error occurs.

Der optionale formal_parameter_list eines Instanzkonstruktors unterliegt den gleichen Regeln wie das formal_parameter_list einer Methode (Methoden).The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (Methods). Die Liste formaler Parameter definiert die Signatur (Signaturen und überladen) eines Instanzkonstruktors und steuert den Prozess, bei dem die Überladungs Auflösung (Typrückschluss) einen bestimmten Instanzkonstruktor in einem Aufruf auswählt.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.

Auf jeden der Typen, auf die in der formal_parameter_list eines Instanzkonstruktors verwiesen wird, muss mindestens der Zugriff auf den Konstruktor selbst (BarrierefreiheitsEinschränkungen) möglich sein.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).

Der optionale constructor_initializer -Konstruktor gibt einen anderen Instanzkonstruktor an, der vor der Ausführung der Anweisungen im constructor_body dieses Instanzkonstruktors aufgerufen werden soll.The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. Dies wird in konstruktorinitialisierernausführlicher beschrieben.This is described further in Constructor initializers.

Wenn eine Konstruktordeklaration einen extern Modifizierer enthält, wird der Konstruktor als externer Konstruktorbezeichnet.When a constructor declaration includes an extern modifier, the constructor is said to be an external constructor. Da eine externe Konstruktordeklaration keine tatsächliche Implementierung bereitstellt, besteht deren constructor_body aus einem Semikolon.Because an external constructor declaration provides no actual implementation, its constructor_body consists of a semicolon. Für alle anderen Konstruktoren besteht der constructor_body aus einem- Block , der die Anweisungen zum Initialisieren einer neuen Instanz der-Klasse angibt.For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. Dies entspricht exakt dem Block einer Instanzmethode mit einem void Rückgabetyp (Methoden Text).This corresponds exactly to the block of an instance method with a void return type (Method body).

Instanzkonstruktoren werden nicht geerbt.Instance constructors are not inherited. Folglich hat eine Klasse keine Instanzkonstruktoren, die nicht tatsächlich in der Klasse deklariert sind.Thus, a class has no instance constructors other than those actually declared in the class. Wenn eine Klasse keine Instanzkonstruktordeklarationen enthält, wird automatisch ein Standardinstanzkonstruktor bereitgestellt (Standardkonstruktoren).If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

Instanzkonstruktoren werden von object_creation_expressions (Objekt Erstellungs Ausdrücke) und bis constructor_initializers aufgerufen.Instance constructors are invoked by object_creation_expressions (Object creation expressions) and through constructor_initializers.

KonstruktorinitialisiererConstructor initializers

Alle Instanzkonstruktoren (außer den Klassen object) enthalten implizit einen Aufruf eines anderen Instanzkonstruktors direkt vor dem constructor_body-Element.All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. Der implizit aufzurufende Konstruktor wird durch die constructor_initializerbestimmt:The constructor to implicitly invoke is determined by the constructor_initializer:

  • Ein instanzkonstruktorinitialisierer des Formulars base(argument_list) oder base() bewirkt, dass ein Instanzkonstruktor von der direkten Basisklasse aufgerufen wird.An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. Dieser Konstruktor wird mit argument_list , sofern vorhanden, und den Regeln zur Überladungs Auflösung der Überladungs Auflösungausgewählt.That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. Der Satz von Kandidaten Instanzkonstruktoren besteht aus allen zugänglichen Instanzkonstruktoren, die in der direkten Basisklasse enthalten sind, oder dem Standardkonstruktor (Standardkonstruktoren), wenn in der direkten Basisklasse keine Instanzkonstruktoren deklariert werden.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. Wenn dieser Satz leer ist oder ein einzelner Konstruktor mit der besten Instanz nicht identifiziert werden kann, tritt ein Kompilierzeitfehler auf.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • Ein instanzkonstruktorinitialisierer des Formulars this(argument-list) oder this() bewirkt, dass ein Instanzkonstruktor von der Klasse selbst aufgerufen wird.An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. Der Konstruktor wird mit argument_list , sofern vorhanden, und den Regeln zur Überladungs Auflösung der Überladungs Auflösungausgewählt.The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. Der Satz von Kandidaten Instanzkonstruktoren besteht aus allen zugänglichen Instanzkonstruktoren, die in der Klasse selbst deklariert sind.The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. Wenn dieser Satz leer ist oder ein einzelner Konstruktor mit der besten Instanz nicht identifiziert werden kann, tritt ein Kompilierzeitfehler auf.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. Wenn eine Instanzkonstruktordeklaration einen Konstruktorinitialisierer enthält, der den Konstruktor selbst aufruft, tritt ein Kompilierzeitfehler auf.If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

Wenn ein Instanzkonstruktor über keinen Konstruktorinitialisierer verfügt, wird ein Konstruktorinitialisierer base() des Formulars implizit bereitgestellt.If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. Folglich ist eine Instanzkonstruktordeklaration der FormThus, an instance constructor declaration of the form

C(...) {...}

ist genau Äquivalent zuis exactly equivalent to

C(...): base() {...}

Der Gültigkeitsbereich der Parameter, die vom formal_parameter_list einer Instanzkonstruktordeklaration angegeben werden, beinhaltet den Konstruktorinitialisierer dieser Deklaration.The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. Daher ist es einem Konstruktorinitialisierer gestattet, auf die Parameter des Konstruktors zuzugreifen.Thus, a constructor initializer is permitted to access the parameters of the constructor. Zum Beispiel:For example:

class A
{
    public A(int x, int y) {}
}

class B: A
{
    public B(int x, int y): base(x + y, x - y) {}
}

Ein instanzkonstruktorinitialisierer kann nicht auf die Instanz zugreifen, die erstellt wird.An instance constructor initializer cannot access the instance being created. Daher ist es ein Kompilierzeitfehler, der auf this in einem Argument Ausdruck des konstruktorinitialisierers verweist, ebenso wie es ein Kompilierzeitfehler für einen Argument Ausdruck ist, der auf ein Instanzmember über eine Simple_nameverweist.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.

InstanzvariableninitialisiererInstance variable initializers

Wenn ein Instanzkonstruktor über keinen Konstruktorinitialisierer oder einen Konstruktorinitialisierer der Form base(...) verfügt, führt dieser Konstruktor implizit die Initialisierungen aus, die von den variable_initializers der Instanzfelder angegeben werden, die in deklariert werden. die Klasse.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. Dies entspricht einer Sequenz von Zuweisungen, die unmittelbar nach dem Einstieg in den Konstruktor und vor dem impliziten Aufruf des direkten Basisklassenkonstruktors ausgeführt werden.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. Die Variableninitialisierer werden in der Text Reihenfolge ausgeführt, in der Sie in der Klassen Deklaration angezeigt werden.The variable initializers are executed in the textual order in which they appear in the class declaration.

KonstruktorausführungConstructor execution

Variableninitialisierer werden in Zuweisungs Anweisungen transformiert, und diese Zuweisungs Anweisungen werden vor dem Aufruf des Basisklasseninstanzkonstruktors ausgeführt.Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. Diese Reihenfolge stellt sicher, dass alle Instanzfelder durch ihre Variableninitialisierer initialisiert werden, bevor Anweisungen ausgeführt werden, die auf diese Instanz zugreifen können.This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

Im BeispielGiven 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);
    }
}

Wenn new B() verwendet wird, um eine Instanz von Bzu erstellen, wird die folgende Ausgabe erzeugt:when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

Der Wert von x ist 1, da der Variableninitialisierer ausgeführt wird, bevor der basisklasseninstanzkonstruktor aufgerufen wird.The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. Der Wert von y ist jedoch 0 (der Standardwert intvon), da die Zuweisung zu y erst ausgeführt wird, nachdem der Basisklassenkonstruktor zurückgegeben wurde.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.

Es ist hilfreich, instanzvariableninitialisierer und Konstruktorinitialisierer als Anweisungen zu betrachten, die vor dem constructor_bodyautomatisch eingefügt werden.It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. Das BeispielThe 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;
    }
}

enthält mehrere Variableninitialisierer. Sie enthält auch Konstruktorinitialisierer von beiden Formularen (base und this).contains several variable initializers; it also contains constructor initializers of both forms (base and this). Das Beispiel entspricht dem unten gezeigten Code, wobei jeder Kommentar eine automatisch eingefügte Anweisung angibt (die Syntax, die für die automatisch eingefügten Konstruktoraufrufe verwendet wird, ist nicht gültig, dient lediglich zur Veranschaulichung des Mechanismus).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;
    }
}

StandardkonstruktorenDefault constructors

Wenn eine Klasse keine Instanzkonstruktordeklarationen enthält, wird automatisch ein Standardinstanzkonstruktor bereitgestellt.If a class contains no instance constructor declarations, a default instance constructor is automatically provided. Dieser Standardkonstruktor ruft einfach den Parameter losen Konstruktor der direkten Basisklasse auf.That default constructor simply invokes the parameterless constructor of the direct base class. Wenn die Klasse abstrakt ist, wird die deklarierte Barrierefreiheit für den Standardkonstruktor geschützt.If the class is abstract then the declared accessibility for the default constructor is protected. Andernfalls ist die deklarierte Barrierefreiheit für den Standardkonstruktor öffentlich.Otherwise, the declared accessibility for the default constructor is public. Daher ist der Standardkonstruktor immer das Formular.Thus, the default constructor is always of the form

protected C(): base() {}

oderor

public C(): base() {}

dabei C ist der Name der Klasse.where C is the name of the class. Wenn die Überladungs Auflösung keinen eindeutigen besten Kandidaten für den basisklassenkonstruktorinitialisierer ermitteln kann, tritt ein Kompilierzeitfehler auf.If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

Im BeispielIn the example

class Message
{
    object sender;
    string text;
}

ein Standardkonstruktor wird bereitgestellt, da die-Klasse keine Instanzkonstruktordeklarationen enthält.a default constructor is provided because the class contains no instance constructor declarations. Folglich entspricht das Beispiel genau demThus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

Private KonstruktorenPrivate constructors

Wenn eine Klasse T nur private Instanzkonstruktoren deklariert, ist es nicht möglich, dass Klassen außerhalb des Programm T Texts von T oder direkt Instanzen von Terstellen.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. Wenn eine Klasse nur statische Member enthält und nicht instanziiert werden soll, wird durch das Hinzufügen eines leeren privaten Instanzkonstruktors eine Instanziierung verhindert.Thus, if a class contains only static members and isn't intended to be instantiated, adding an empty private instance constructor will prevent instantiation. Zum Beispiel: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) {...}
}

Die Trig -Klasse gruppiert verwandte Methoden und Konstanten, sollte jedoch nicht instanziiert werden.The Trig class groups related methods and constants, but is not intended to be instantiated. Daher wird ein einzelner leerer privater Instanzkonstruktor deklariert.Therefore it declares a single empty private instance constructor. Mindestens ein Instanzkonstruktor muss deklariert werden, um die automatische Generierung eines Standardkonstruktors zu unterdrücken.At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

Optionale instanzkonstruktorparameterOptional instance constructor parameters

Die this(...) Form des konstruktorinitialisierers wird häufig in Verbindung mit überladen verwendet, um optionale instanzkonstruktorparameter zu implementieren.The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. Im BeispielIn 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
    }
}

die ersten beiden Instanzkonstruktoren stellen lediglich die Standardwerte für die fehlenden Argumente bereit.the first two instance constructors merely provide the default values for the missing arguments. Beide verwenden einen this(...) Konstruktorinitialisierer, um den dritten Instanzkonstruktor aufzurufen, der tatsächlich die Initialisierung der neuen Instanz bewirkt.Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. Der Effekt besteht aus den optionalen Konstruktorparametern: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");

Statische KonstruktorenStatic constructors

Ein statischer Konstruktor ist ein Member, der die erforderlichen Aktionen zum Initialisieren eines geschlossenen Klassen Typs implementiert.A static constructor is a member that implements the actions required to initialize a closed class type. Statische Konstruktoren werden mit static_constructor_declarations deklariert: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
    | ';'
    ;

Ein static_constructor_declaration kann einen Satz von Attributen (Attribute) und einen extern-Modifizierer (Externe Methoden) enthalten.A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

Der Bezeichner eines static_constructor_declaration muss der Klasse, in der der statische Konstruktor deklariert ist, einen Namen benennen.The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. Wenn ein anderer Name angegeben wird, tritt ein Kompilierzeitfehler auf.If any other name is specified, a compile-time error occurs.

Wenn eine statische Konstruktordeklaration einen extern Modifizierer enthält, wird als statischer Konstruktor ein externer statischer Konstruktorbezeichnet.When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Da eine externe statische Konstruktordeklaration keine tatsächliche Implementierung bereitstellt, besteht deren static_constructor_body aus einem Semikolon.Because an external static constructor declaration provides no actual implementation, its static_constructor_body consists of a semicolon. Für alle anderen statischen Konstruktordeklarationen besteht der static_constructor_body aus einem- Block , der die auszuführenden Anweisungen angibt, um die-Klasse zu initialisieren.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. Dies entspricht exakt dem method_body einer statischen Methode mit einem void-Rückgabetyp (Methoden Text).This corresponds exactly to the method_body of a static method with a void return type (Method body).

Statische Konstruktoren werden nicht geerbt und können nicht direkt aufgerufen werden.Static constructors are not inherited, and cannot be called directly.

Der statische Konstruktor für einen geschlossenen Klassentyp wird höchstens einmal in einer bestimmten Anwendungsdomäne ausgeführt.The static constructor for a closed class type executes at most once in a given application domain. Die Ausführung eines statischen Konstruktors wird ausgelöst, wenn das erste der folgenden Ereignisse in einer Anwendungsdomäne auftritt:The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • Eine Instanz des-Klassen Typs wird erstellt.An instance of the class type is created.
  • Auf alle statischen Member des Klassen Typs wird verwiesen.Any of the static members of the class type are referenced.

Wenn eine Klasse die Main Methode (Anwendungsstart) enthält, in der die Ausführung beginnt, wird der statische Konstruktor für diese Klasse Main ausgeführt, bevor die-Methode aufgerufen wird.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.

Um einen neuen geschlossenen Klassentyp zu initialisieren, wird zuerst ein neuer Satz statischer Felder (statische Felder und Instanzfelder) für diesen bestimmten geschlossenen Typ erstellt.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. Jedes der statischen Felder wird mit dem Standardwert initialisiert (Standardwerte).Each of the static fields is initialized to its default value (Default values). Als nächstes werden die statischen Feldinitialisierer (statische Feld Initialisierung) für diese statischen Felder ausgeführt.Next, the static field initializers (Static field initialization) are executed for those static fields. Schließlich wird der statische Konstruktor ausgeführt.Finally, the static constructor is executed.

Das BeispielThe 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");
    }
}

die Ausgabe muss erzeugt werden:must produce the output:

Init A
A.F
Init B
B.F

, da der statische AKonstruktor der Ausführung durch den B- B.Faufzurufenden aufgerufen wird und die Ausführung des statischen Konstruktors durch den-Befehl ausgelöst wird. 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.

Es ist möglich, zirkuläre Abhängigkeiten zu erstellen, die es ermöglichen, dass statische Felder mit Variableninitialisierern im Standardwert Zustand beobachtet werden.It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

Das BeispielThe 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);
    }
}

erzeugt die Ausgabeproduces the output

X = 1, Y = 2

Zum Ausführen der Main -Methode führt das System zuerst den Initialisierer für B.Yaus, bevor der Bstatische Konstruktor der Klasse ausgeführt wird.To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Yder Initialisierer bewirkt A, dass der statische Konstruktor ausgeführt wird, da auf A.X den Wert von verwiesen wird.Y's initializer causes A's static constructor to be run because the value of A.X is referenced. Der statische Konstruktor von A führt wiederum den Wert von Xaus und ruft dabei den Standardwert von Yab, der 0 (null) ist.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.Xwird daher mit 1 initialisiert.A.X is thus initialized to 1. Der Prozess der Ausführung Ader statischen Feldinitialisierer und des statischen Konstruktors wird dann abgeschlossen, wobei die Berechnung des Anfangs YWerts von zurückgegeben wird. Dadurch wird das Ergebnis 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.

Da der statische Konstruktor für jeden geschlossenen konstruierten Klassentyp genau einmal ausgeführt wird, können Sie Laufzeitüberprüfungen für den Typparameter erzwingen, der zur Kompilierzeit nicht über Einschränkungen (Typparameter Einschränkungen) überprüft werden kann. .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). Der folgende Typ verwendet beispielsweise einen statischen Konstruktor, um zu erzwingen, dass das Typargument eine-Enum ist: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");
        }
    }
}

DestruktorenDestructors

Ein destrukturtor ist ein Member, der die erforderlichen Aktionen zum Zerstörung einer Instanz einer Klasse implementiert.A destructor is a member that implements the actions required to destruct an instance of a class. Ein Dekonstruktor wird mit einem destructor_declarationdeklariert:A destructor is declared using a destructor_declaration:

destructor_declaration
    : attributes? 'extern'? '~' identifier '(' ')' destructor_body
    | destructor_declaration_unsafe
    ;

destructor_body
    : block
    | ';'
    ;

Ein destructor_declaration kann einen Satz von Attributen (Attribute) enthalten.A destructor_declaration may include a set of attributes (Attributes).

Der Bezeichner eines destructor_declaration muss der Klasse, in der der Dekonstruktor deklariert ist, einen Namen benennen.The identifier of a destructor_declaration must name the class in which the destructor is declared. Wenn ein anderer Name angegeben wird, tritt ein Kompilierzeitfehler auf.If any other name is specified, a compile-time error occurs.

Wenn eine dekonstruktordeklaration extern einen Modifizierer enthält, wird der Dekonstruktor als externer Dekonstruktorbezeichnet.When a destructor declaration includes an extern modifier, the destructor is said to be an external destructor. Da eine externe dekonstruktordeklaration keine tatsächliche Implementierung bereitstellt, besteht deren destructor_body aus einem Semikolon.Because an external destructor declaration provides no actual implementation, its destructor_body consists of a semicolon. Für alle anderen destrukturtoren besteht der destructor_body aus einem- Block , der die auszuführenden Anweisungen angibt, um eine Instanz der-Klasse zu Zerstörung.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. Ein destructor_body entspricht exakt dem method_body einer Instanzmethode mit einem void-Rückgabetyp (Methoden Text).A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

Deerdektoren werden nicht geerbt.Destructors are not inherited. Folglich hat eine Klasse keine Dekonstruktoren, die nicht die Dekonstruktoren aufweisen, die in dieser Klasse deklariert werden können.Thus, a class has no destructors other than the one which may be declared in that class.

Da für einen Dekonstruktor keine Parameter erforderlich sind, kann er nicht überladen werden, sodass eine Klasse höchstens einen Dekonstruktor aufweisen kann.Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

Dededektoren werden automatisch aufgerufen und können nicht explizit aufgerufen werden.Destructors are invoked automatically, and cannot be invoked explicitly. Eine Instanz ist für die Zerstörung infrage, wenn es für keinen Code mehr möglich ist, diese Instanz zu verwenden.An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. Die Ausführung des Dekonstruktors für die Instanz kann zu einem beliebigen Zeitpunkt erfolgen, nachdem die Instanz für eine Zerstörung berechtigt ist.Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. Wenn eine Instanz von zerstört wird, werden die Dekonstruktoren in der Vererbungs Kette dieser Instanz in der richtigen Reihenfolge von der am wenigsten abgeleiteten zum geringsten abgeleiteten aufgerufen.When an instance is destructed, the destructors in that instance's inheritance chain are called, in order, from most derived to least derived. Ein Dekonstruktor kann in jedem Thread ausgeführt werden.A destructor may be executed on any thread. Weitere Informationen zu den Regeln, die bestimmen, wann und wie ein Dekonstruktor ausgeführt wird, finden Sie unter Automatische Speicherverwaltung.For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

Die Ausgabe des BeispielsThe 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();
   }
}

echtis

B's destructor
A's destructor

Da deerdektoren in einer Vererbungs Kette in der Reihenfolge von den meisten abgeleiteten zu den am wenigsten abgeleitetensince destructors in an inheritance chain are called in order, from most derived to least derived.

Dededektoren werden implementiert, indem die Finalize virtuelle System.ObjectMethode für überschrieben wird.Destructors are implemented by overriding the virtual method Finalize on System.Object. C#Es ist nicht zulässig, dass Programme diese Methode außer Kraft setzen oder Sie direkt (bzw. über schreibungen) direkt aufzurufen.C# programs are not permitted to override this method or call it (or overrides of it) directly. Beispielsweise wird das ProgrammFor instance, the program

class A 
{
    override protected void Finalize() {}    // error

    public void F() {
        this.Finalize();                     // error
    }
}

enthält zwei Fehler.contains two errors.

Der Compiler verhält sich so, als ob diese Methode und über schreibungen davon überhaupt nicht vorhanden sind.The compiler behaves as if this method, and overrides of it, do not exist at all. Dieses Programm ist also:Thus, this program:

class A 
{
    void Finalize() {}                            // permitted
}

ist gültig, und die System.Object-Methode hat die- Finalize Methode ausgeblendet.is valid, and the method shown hides System.Object's Finalize method.

Eine Erläuterung des Verhaltens, wenn eine Ausnahme von einem debugtor ausgelöst wird, finden Sie unter so werden Ausnahmen behandelt.For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

IteratorsIterators

Ein Funktionsmember (Funktionsmember), der mit einem Iteratorblock (Blocks) implementiert wird, wird als Iteratorbezeichnet.A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

Ein Iteratorblock kann als Text eines Funktionsmembers verwendet werden, solange der Rückgabetyp des entsprechenden Funktionsmembers eine der Enumeratorschnittstellen (Enumeratorschnittstellen) oder eine der Aufzähl Bare-Schnittstellen (Aufzähl Bare Schnittstellen) ist. .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). Sie kann als method_body, operator_body oder accessor_bodyauftreten, wohingegen Ereignisse, Instanzkonstruktoren, statische Konstruktoren und Dekonstruktoren nicht als Iteratoren implementiert werden können.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.

Wenn ein Funktionsmember mit einem Iteratorblock implementiert wird, ist dies ein Kompilierzeitfehler für die Liste formaler Parameter des Funktionsmembers, um ref beliebige out -oder-Parameter anzugeben.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.

EnumeratorschnittstellenEnumerator interfaces

Die Enumeratorschnittstellen sind die nicht generische Schnittstelle System.Collections.IEnumerator und alle Instanziierungen der generischen- System.Collections.Generic.IEnumerator<T>Schnittstelle.The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. Aus Gründen der Kürze werden diese Schnittstellen in diesem Kapitel als IEnumerator IEnumerator<T>bzw. referenziert.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

Enumerable-SchnittstellenEnumerable interfaces

Die Aufzähl Bare-Schnittstellen sind die nicht generische System.Collections.IEnumerable Schnittstelle und alle Instanziierungen der generischen-Schnittstelle. 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>. Aus Gründen der Kürze werden diese Schnittstellen in diesem Kapitel als IEnumerable IEnumerable<T>bzw. referenziert.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Yield-TypYield type

Ein Iterator erzeugt eine Sequenz von Werten, die alle denselben Typ haben.An iterator produces a sequence of values, all of the same type. Dieser Typ wird als Yield-Typ des Iterators bezeichnet.This type is called the yield type of the iterator.

  • Der Yield-Typ eines Iterators, der IEnumerator oder IEnumerable zurück objectgibt, ist.The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • Der Yield-Typ eines Iterators, der IEnumerator<T> oder IEnumerable<T> zurück Tgibt, ist.The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

EnumeratorobjekteEnumerator objects

Wenn ein Funktionsmember, der einen enumeratorschnittstellentyp zurückgibt, mit einem Iteratorblock implementiert wird, führt der Aufruf des Funktionsmembers den Code nicht sofort im Iteratorblock aus.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. Stattdessen wird ein Enumeratorobjekt erstellt und zurückgegeben.Instead, an enumerator object is created and returned. Dieses Objekt kapselt den im Iteratorblock angegebenen Code, und die Ausführung des Codes im Iteratorblock tritt auf, wenn die- MoveNext Methode des Enumeratorobjekts aufgerufen wird.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. Ein Enumeratorobjekt weist die folgenden Eigenschaften auf:An enumerator object has the following characteristics:

  • Es implementiert IEnumerator und IEnumerator<T>, wobei T der Yield-Typ des Iterators ist.It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • Sie implementiert System.IDisposable.It implements System.IDisposable.
  • Sie wird mit einer Kopie der Argument Werte (sofern vorhanden) und dem Instanzwert initialisiert, der an das Funktionsmember übermittelt wurde.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • Sie verfügt über vier mögliche Zustände: vor, wird ausgeführt , angehalten und nach, und befindet sich anfänglich im Zustand " vor ".It has four potential states, before, running, suspended, and after, and is initially in the before state.

Bei einem Enumeratorobjekt handelt es sich in der Regel um eine Instanz einer vom Compiler generierten Enumeratorklasse, die den Code im Iteratorblock kapselt und die Enumeratorschnittstellen implementiert, aber andere Implementierungs Methoden sind möglich.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. Wenn eine Enumeratorklasse vom Compiler generiert wird, wird diese Klasse direkt oder indirekt in der Klasse, die den Funktionsmember enthält, in die-Klasse eingefügt, Sie verfügt über private zugreif barkeit und hat einen Namen, der fürdie Verwendung durch den Compiler reserviert ist (Identifier).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).

Ein Enumeratorobjekt kann mehr Schnittstellen implementieren, als die oben genannten.An enumerator object may implement more interfaces than those specified above.

In den folgenden MoveNextAbschnitten wird das genaue Verhalten der IEnumerable - Current,- Dispose und-Member der IEnumerable<T> -und-Schnittstellen Implementierungen beschrieben, die von einem Enumeratorobjekt bereitgestellt werden.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.

Beachten Sie, dass Enumeratorobjekte die IEnumerator.Reset -Methode nicht unterstützen.Note that enumerator objects do not support the IEnumerator.Reset method. Das Aufrufen dieser Methode bewirkt System.NotSupportedException , dass eine ausgelöst wird.Invoking this method causes a System.NotSupportedException to be thrown.

Die Methode "Design ext"The MoveNext method

Die MoveNext -Methode eines Enumeratorobjekts kapselt den Code eines Iteratorblocks.The MoveNext method of an enumerator object encapsulates the code of an iterator block. Durch Aufrufen MoveNext der-Methode wird Code im Iteratorblock ausgeführt, Current und die-Eigenschaft des Enumeratorobjekts wird entsprechend festgelegt.Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. Die genaue Aktion, die MoveNext von ausgeführt wird, hängt vom Status des Enumeratorobjekts ab, wenn MoveNext aufgerufen wird:The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • Wenn der Status des Enumeratorobjekts vorist, wird aufgerufen MoveNext:If the state of the enumerator object is before, invoking MoveNext:
    • Ändert den Status in wird ausgeführt.Changes the state to running.
    • Initialisiert die Parameter (einschließlich this) des Iteratorblocks mit den Argument Werten und dem Instanzwert, die beim Initialisieren des Enumeratorobjekts gespeichert wurden.Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • Führt den Iteratorblock von dem Anfang aus, bis die Ausführung unterbrochen wird (wie unten beschrieben).Executes the iterator block from the beginning until execution is interrupted (as described below).
  • Wenn der Status des Enumeratorobjekts ausgeführtwird, MoveNext ist das Ergebnis des Aufrufs nicht angegeben.If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • Wenn der Status des Enumeratorobjekts angehalten wird, wird MoveNextaufgerufen:If the state of the enumerator object is suspended, invoking MoveNext:
    • Ändert den Status in wird ausgeführt.Changes the state to running.
    • Stellt die Werte aller lokalen Variablen und Parameter (einschließlich dieser) für die Werte wieder her, die bei der letzten Ausführung des Iteratorblocks gespeichert wurden.Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Beachten Sie, dass sich der Inhalt aller Objekte, auf die von diesen Variablen verwiesen wird, seit dem vorherigen-Befehl von "muvenext" geändert hat.Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • Nimmt die Ausführung des Iteratorblocks unmittelbar nach der yield return Anweisung an, die die Unterbrechung der Ausführung verursacht hat, und wird fortgesetzt, bis die Ausführung unterbrochen wurde (wie unten beschrieben).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).
  • Wenn der Zustand des Enumeratorobjekts nachist, wird der MoveNext Aufruf falsevon zurückgegeben.If the state of the enumerator object is after, invoking MoveNext returns false.

Wenn MoveNext den Iteratorblock ausführt, kann die Ausführung auf vier Arten unterbrochen werden: Durch eine yield return -Anweisung durch eine yield break -Anweisung, durch die das Ende des Iteratorblocks und durch eine Ausnahme ausgelöst und aus dem Iteratorblock weitergegeben wurde.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.

  • Wenn eine yield return -Anweisung gefunden wird (die yield-Anweisung):When a yield return statement is encountered (The yield statement):
    • Der in der-Anweisung angegebene Ausdruck wird ausgewertet, implizit in den Yield-Typ konvertiert und der Current -Eigenschaft des Enumeratorobjekts zugewiesen.The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • Die Ausführung des iteratortexts wurde angehalten.Execution of the iterator body is suspended. Die Werte aller lokalen Variablen und Parameter (einschließlich this) werden gespeichert, ebenso wie der Speicherort dieser yield return Anweisung.The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. Wenn sich yield return die-Anweisung innerhalb eines oder try mehrerer Blöcke befindet, finally werden die zugeordneten Blöcke zurzeit nicht ausgeführt.If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • Der Status des Enumeratorobjekts wird in " angehalten" geändert.The state of the enumerator object is changed to suspended.
    • Die MoveNext -Methode true kehrt an ihren Aufrufer zurück und gibt an, dass die Iterationen erfolgreich auf den nächsten Wert erweitert wurden.The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • Wenn eine yield break -Anweisung gefunden wird (die yield-Anweisung):When a yield break statement is encountered (The yield statement):
    • Wenn die yield break Anweisung innerhalb eines oder mehrerer try Blöcke liegt, werden die finally zugeordneten Blöcke ausgeführt.If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • Der Status des Enumeratorobjekts wird in nachgeändert.The state of the enumerator object is changed to after.
    • Die MoveNext Methode kehrt false an ihren Aufrufer zurück und gibt an, dass die Iterations Funktion vollständig ist.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Wenn das Ende des iteratortexts erreicht ist:When the end of the iterator body is encountered:
    • Der Status des Enumeratorobjekts wird in nachgeändert.The state of the enumerator object is changed to after.
    • Die MoveNext Methode kehrt false an ihren Aufrufer zurück und gibt an, dass die Iterations Funktion vollständig ist.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Wenn eine Ausnahme ausgelöst und aus dem Iteratorblock weitergegeben wird:When an exception is thrown and propagated out of the iterator block:
    • Die finally entsprechenden Blöcke im iteratortext werden von der Ausnahme Weitergabe ausgeführt.Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • Der Status des Enumeratorobjekts wird in nachgeändert.The state of the enumerator object is changed to after.
    • Die Ausnahme Weitergabe wird an den Aufrufer der MoveNext -Methode weitergegeben.The exception propagation continues to the caller of the MoveNext method.

Die aktuelle EigenschaftThe Current property

Die-Eigenschaft eines Enumeratorobjekts wirkt sich yield return auf- Current Anweisungen im Iteratorblock aus.An enumerator object's Current property is affected by yield return statements in the iterator block.

Wenn sich ein Enumeratorobjekt im angehaltenen Zustand befindet, ist Current der Wert von der Wert, der durch MoveNextden vorherigen-Befehl festgelegt wurde.When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. Wenn sich ein Enumeratorobjekt in den Zuständen " before", " Running" oder " after " Current befindet, ist das Ergebnis des Zugriffs nicht angegeben.When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified.

Bei einem Iterator mit einem anderen Yield-Typ objectals entspricht das Ergebnis des Current Zugriffs auf die- IEnumerable Implementierung des Enumeratorobjekts dem Current Zugriff über das- IEnumerator<T> Enumeratorobjekt. Implementierung und Umwandeln des Ergebnisses in object.For 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.

Die verwerfen-MethodeThe Dispose method

Die Dispose -Methode wird verwendet, um die Iterationen zu bereinigen, indem das Enumeratorobjekt in den after -Zustand versetzt wird.The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • Wenn der Status des Enumeratorobjekts vorherist, ändert der Dispose Aufruf von den Zustand in nach.If the state of the enumerator object is before, invoking Dispose changes the state to after.
  • Wenn der Status des Enumeratorobjekts ausgeführtwird, Dispose ist das Ergebnis des Aufrufs nicht angegeben.If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • Wenn der Status des Enumeratorobjekts angehalten wird, wird Disposeaufgerufen:If the state of the enumerator object is suspended, invoking Dispose:
    • Ändert den Status in wird ausgeführt.Changes the state to running.
    • Führt beliebige letzte Blöcke aus, als wäre die yield return Letzte ausgeführte yield break Anweisung eine-Anweisung.Executes any finally blocks as if the last executed yield return statement were a yield break statement. Wenn dies bewirkt, dass eine Ausnahme ausgelöst und aus dem iteratortext weitergegeben wird, wird der Status des Enumeratorobjekts auf after festgelegt, und die Ausnahme wird an den Aufrufer der Dispose Methode weitergegeben.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.
    • Ändert den Zustand in nach.Changes the state to after.
  • Wenn der Zustand des Enumeratorobjekts nachist, hat der Dispose Aufruf von keine Auswirkungen.If the state of the enumerator object is after, invoking Dispose has no affect.

Enumerable-ObjekteEnumerable objects

Wenn ein Funktionsmember, der einen Aufzähl baren Schnittstellentyp zurückgibt, mit einem Iteratorblock implementiert wird, führt der Aufruf des Funktionsmembers den Code nicht sofort im Iteratorblock aus.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. Stattdessen wird ein Aufzähl bares Objekt erstellt und zurückgegeben.Instead, an enumerable object is created and returned. Die- GetEnumerator Methode des Aufzähl Bare-Objekts gibt ein Enumeratorobjekt zurück, das den im Iteratorblock angegebenen Code kapselt, und die Ausführung des Codes im Iteratorblock tritt auf, wenn die- MoveNext Methode des Enumeratorobjekts aufgerufen wird.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. Ein Aufzähl Bare-Objekt hat die folgenden Eigenschaften:An enumerable object has the following characteristics:

  • Es implementiert IEnumerable und IEnumerable<T>, wobei T der Yield-Typ des Iterators ist.It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • Sie wird mit einer Kopie der Argument Werte (sofern vorhanden) und dem Instanzwert initialisiert, der an das Funktionsmember übermittelt wurde.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

Ein Aufzähl Bare-Objekt ist in der Regel eine Instanz einer vom Compiler generierten Aufzähl Bare-Klasse, die den Code im Iteratorblock kapselt und die Aufzähl Bare-Schnittstellen implementiert, aber andere Implementierungs Methoden sind möglich.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. Wenn eine Aufzähl Bare-Klasse vom Compiler generiert wird, wird diese Klasse direkt oder indirekt in der-Klasse, die den Funktionsmember enthält, in eine private Barrierefreiheit eingefügt, und Sie erhält einen Namen, der für die Verwendung durch denCompiler (Bezeichner) reserviert ist.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).

Ein Aufzähl Bare-Objekt kann mehr Schnittstellen implementieren, als die oben genannten.An enumerable object may implement more interfaces than those specified above. Insbesondere kann ein Aufzähl Bare-Objekt auch und IEnumerator IEnumerator<T>implementieren, sodass es sowohl als Aufzähl Bare-als auch als Enumerator fungieren kann.In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. Bei diesem Implementierungstyp wird das Aufzähl Bare Objekt selbst zurückgegeben GetEnumerator , wenn die-Methode eines Aufzähl Bare-Objekts zum ersten Mal aufgerufen wird.In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. Nachfolgende Aufrufe des Aufzähl baren Objekts GetEnumeratorgeben, falls vorhanden, eine Kopie des Aufzähl Bare-Objekts zurück.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Folglich hat jeder zurückgegebene Enumerator seinen eigenen Zustand, und Änderungen in einem Enumerator haben keine Auswirkung auf einen anderen Enumerator.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

Die getenreerator-MethodeThe GetEnumerator method

Ein Aufzähl Bare IEnumerable -Objekt stellt eine Implementierung der GetEnumerator Methoden der-Schnitt IEnumerable<T> Stelle und der-Schnittstelle bereit.An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. Die beiden GetEnumerator Methoden verwenden eine gemeinsame-Implementierung, die ein verfügbares Enumeratorobjekt abruft und zurückgibt.The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. Das Enumeratorobjekt wird mit den Argument Werten und dem Instanzwert initialisiert, die gespeichert wurden, als das Aufzähl Bare Objekt initialisiert wurde. andernfalls ist das Enumeratorobjekt wie in enumeratorobjektenbeschrieben funktionsfähig.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.

Implementierungs BeispielImplementation example

In diesem Abschnitt wird eine mögliche Implementierung von Iteratoren in Bezug auf C# standardkonstrukte beschrieben.This section describes a possible implementation of iterators in terms of standard C# constructs. Die hier beschriebene Implementierung basiert auf denselben Prinzipien, die vom Microsoft C# -Compiler verwendet werden. Dies bedeutet jedoch nicht, dass es sich um eine vorgeschriebene Implementierung oder die einzige Möglichkeit handelt.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.

Die folgende Stack<T> Klasse GetEnumerator implementiert die-Methode mit einem Iterator.The following Stack<T> class implements its GetEnumerator method using an iterator. Der Iterator listet die Elemente des Stapels in der Reihenfolge von oben nach unten auf.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];
    }
}

Die GetEnumerator -Methode kann in eine Instanziierung einer vom Compiler generierten Enumeratorklasse übersetzt werden, die den Code im Iteratorblock kapselt, wie im folgenden gezeigt.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();
        }
    }
}

In der obigen Übersetzung wird der Code im Iteratorblock in einen Zustands Automat umgewandelt und in die MoveNext -Methode der Enumeratorklasse eingefügt.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. Außerdem wird die lokale Variable i in ein Feld im Enumeratorobjekt umgewandelt, sodass Sie weiterhin über Aufrufe von MoveNextvorhanden sein kann.Furthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext.

Im folgenden Beispiel wird eine einfache Multiplikationstabelle der ganzen Zahlen 1 bis 10 gedruckt.The following example prints a simple multiplication table of the integers 1 through 10. Die FromTo -Methode im Beispiel gibt ein Aufzähl bares Objekt zurück und wird mit einem Iterator implementiert.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();
        }
    }
}

Die FromTo -Methode kann in eine Instanziierung einer vom Compiler generierten Aufzähl Bare-Klasse übersetzt werden, die den Code im Iteratorblock kapselt, wie im folgenden gezeigt.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();
        }
    }
}

Die Aufzähl Bare-Klasse implementiert sowohl die Aufzähl Bare-Schnittstelle als auch die Enumeratorschnittstellen, sodass Sie sowohl als Aufzähl Bare-als auch als Enumerator fungieren kann.The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. Wenn die- GetEnumerator Methode zum ersten Mal aufgerufen wird, wird das Aufzähl Bare-Objekt selbst zurückgegeben.The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Nachfolgende Aufrufe des Aufzähl baren Objekts GetEnumeratorgeben, falls vorhanden, eine Kopie des Aufzähl Bare-Objekts zurück.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Folglich hat jeder zurückgegebene Enumerator seinen eigenen Zustand, und Änderungen in einem Enumerator haben keine Auswirkung auf einen anderen Enumerator.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. Die Interlocked.CompareExchange -Methode wird verwendet, um einen Thread sicheren Vorgang sicherzustellen.The Interlocked.CompareExchange method is used to ensure thread-safe operation.

Der from - to Parameter und der-Parameter werden in Felder in der Aufzähl Bare-Klasse umgewandelt.The from and to parameters are turned into fields in the enumerable class. Da from im Iteratorblock geändert wird, wird ein zusätzliches __from Feld eingeführt, das den Anfangswert enthält, der from für jeden Enumerator angegeben wird.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.

Die MoveNext Methode löst eine InvalidOperationException aus, wenn 0Sie aufgerufen __state wird, wenn den Wert hat.The MoveNext method throws an InvalidOperationException if it is called when __state is 0. Dadurch wird verhindert, dass das Aufzähl Bare Objekt als Enumeratorobjekt verwendet wird, ohne GetEnumeratordass zuerst aufgerufen wird.This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

Das folgende Beispiel zeigt eine einfache Strukturklasse.The following example shows a simple tree class. Die Tree<T> -Klasse GetEnumerator implementiert die-Methode mit einem Iterator.The Tree<T> class implements its GetEnumerator method using an iterator. Der Iterator listet die Elemente der Struktur in der Infix-Reihenfolge auf.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();
    }
}

Die GetEnumerator -Methode kann in eine Instanziierung einer vom Compiler generierten Enumeratorklasse übersetzt werden, die den Code im Iteratorblock kapselt, wie im folgenden gezeigt.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();
        }
    }
}

Die vom Compiler generierten temporare, die foreach in den-Anweisungen verwendet __left werden __right , werden in die Felder und des Enumeratorobjekts gehoben.The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. Das __state -Feld des Enumeratorobjekts wird sorgfältig aktualisiert, sodass die richtige Dispose() Methode ordnungsgemäß aufgerufen wird, wenn eine Ausnahme ausgelöst wird.The __state field of the enumerator object is carefully updated so that the correct Dispose() method will be called correctly if an exception is thrown. Beachten Sie, dass es nicht möglich ist, den übersetzten Code foreach mit einfachen Anweisungen zu schreiben.Note that it is not possible to write the translated code with simple foreach statements.

Async-FunktionenAsync functions

Eine Methode (Methoden) oder eine anonyme Funktion (Anonyme Funktions Ausdrücke) mit async dem-Modifizierer wird als Async-Funktionbezeichnet.A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an async function. Im Allgemeinen wird der Begriff Async verwendet, um jede Art von Funktion zu beschreiben, die async über den-Modifizierer verfügt.In general, the term async is used to describe any kind of function that has the async modifier.

Es handelt sich um einen Kompilierzeitfehler für die Liste formaler Parameter einer Async-Funktion, ref um out beliebige-oder-Parameter anzugeben.It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

Der return_type einer Async-Methode muss entweder void oder ein Tasktypsein.The return_type of an async method must be either void or a task type. Die Aufgaben Typen sind System.Threading.Tasks.Task -und-Typen System.Threading.Tasks.Task<T>, die aus erstellt werden.The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. Der Kürze halber wird in diesem Kapitel auf diese Typen als Task Task<T>bzw. verwiesen.For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. Eine Async-Methode, die einen Tasktyp zurückgibt, wird als Aufgaben Rückgabe bezeichnet.An async method returning a task type is said to be task-returning.

Die genaue Definition der Aufgaben Typen ist implementiert, aber aus der Sicht der Sprache befindet sich ein Aufgabentyp in einem der Zustände unvollständig, erfolgreich oder fehlerhaft.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. Eine fehlerhafte Aufgabe zeichnet eine relevante Ausnahme auf.A faulted task records a pertinent exception. Ein erfolgreicher Datensatz T zeichneteinErgebnisTask<T> des Typs auf.A succeeded Task<T> records a result of type T. Aufgaben Typen sind möglich und können daher die Operanden von Erwartungs Ausdrücken (ErwartungsAusdrücke) sein.Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

Ein Async-Funktionsaufruf bietet die Möglichkeit zum Aussetzen der Auswertung mithilfe von Erwartungs Ausdrücken (ErwartungsAusdrücke) im Textkörper.An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. Die Auswertung kann später an dem Punkt fortgesetzt werden, an dem der aufrufende Ausdruck mithilfe eines WiederaufnahmeDelegaten fortgesetzt wird.Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. Der Wiederaufnahme Delegat ist System.Actionvom Typ, und wenn er aufgerufen wird, wird die Auswertung des asynchronen Funktions aufrutens aus dem Erwartungs Ausdruck fortgesetzt, an dem er unterbrochen wurde.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. Der aktuelle Aufrufer eines Async-Funktions Aufrufers ist der ursprüngliche Aufrufer, wenn der Funktionsaufruf nie angehalten wurde, andernfalls der letzte Aufrufer des Wiederaufnahme Delegaten.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.

Auswertung einer Aufgaben Rückgabe Async-FunktionEvaluation of a task-returning async function

Durch den Aufruf einer Aufgaben Rückgabe Async-Funktion wird eine Instanz des zurückgegebenen Aufgaben Typs generiert.Invocation of a task-returning async function causes an instance of the returned task type to be generated. Dies wird als Rückgabe Task der Async-Funktion bezeichnet.This is called the return task of the async function. Der Task befindet sich anfänglich in einem unvollständigen Zustand.The task is initially in an incomplete state.

Der asynchrone Funktions Text wird dann ausgewertet, bis er entweder angehalten wird (indem ein Erwartungs Ausdruck erreicht wird) oder beendet wird, an dem die Punkt Steuerung zusammen mit der Rückgabe Aufgabe an den Aufrufer zurückgegeben wird.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.

Wenn der Text der Async-Funktion beendet wird, wird die Rückgabe Aufgabe aus dem unvollständigen Zustand verschoben:When the body of the async function terminates, the return task is moved out of the incomplete state:

  • Wenn der Funktions Rumpf durch das Erreichen einer Return-Anweisung oder des Endes des Texts beendet wird, werden alle Ergebnis Werte in der Rückgabe Aufgabe aufgezeichnet, die in den Status "erfolgreich" versetzt wird.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.
  • Wenn der Funktions Text als Ergebnis einer nicht abgefangenen Ausnahme (der throw-Anweisung) beendet wird, wird die Ausnahme in der Rückgabe Aufgabe aufgezeichnet, die in einen fehlerhaften Zustand versetzt wird.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.

Auswertung einer "void"-Rückgabe Async-FunktionEvaluation of a void-returning async function

Wenn der Rückgabetyp der Async- voidFunktion ist, weicht die Auswertung von der obigen auf folgende Weise ab: Da keine Aufgabe zurückgegeben wird, kommuniziert die Funktion stattdessen mit Abschluss und Ausnahmen mit dem Synchronisierungs Kontextdes aktuellen Threads.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. Die genaue Definition des Synchronisierungs Kontexts ist implementierungsabhängig, ist jedoch eine Darstellung von "Where", in der der aktuelle Thread ausgeführt wird.The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. Der Synchronisierungs Kontext wird benachrichtigt, wenn die Auswertung einer Async-Funktion mit void-Rückgabe beginnt, erfolgreich abgeschlossen wird oder eine nicht abgefangene Ausnahme ausgelöst wird.The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

Dies ermöglicht es dem Kontext nachzuverfolgen, wie viele void-zurückgegebene asynchrone Funktionen darunter ausgeführt werden, und um zu entscheiden, wie Ausnahmen weitergegeben werden sollen, die aus ihnen stammen.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.