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.
Class declarations (Klassendeklarationen)Class declarations
Eine class_declaration ist eine type_declaration (Typdeklarationen), die 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 ';'?
;
Eine class_declaration besteht aus einem optionalen Satz von Attributen (Attributen), gefolgt von einer optionalen Gruppe von class_modifier s (Klassenmodifizierer). gefolgt von einem optionalen partial
-Modifizierer, gefolgt vom-Schlüsselwort class
und einem Bezeichner , der die-Klasse benennt, gefolgt von einer optionalen type_parameter_list (Typparameter), gefolgt von einer optionalen class_base -Spezifikation (Klassenbasis Spezifikation), gefolgt von einem optionalen Satz von type_parameter_constraints_clause s (Typparameter Einschränkungen), gefolgt von einer 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_modifier s (Class modifiers), followed by an optional partial
modifier, followed by the keyword class
and an identifier that names the class, followed by an optional type_parameter_list (Type parameters), followed by an optional class_base specification (Class base specification) , followed by an optional set of type_parameter_constraints_clause s (Type parameter constraints), followed by a class_body (Class body), optionally followed by a semicolon.
Eine Klassen Deklaration kann nur type_parameter_constraints_clause s bereitstellen, wenn Sie auch eine type_parameter_list bereitstellt.A class declaration cannot supply type_parameter_constraints_clause s unless it also supplies a type_parameter_list.
Eine Klassen Deklaration, die einen 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
Eine 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 der- new
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 public
protected
internal
private
Modifizierer,, und 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 abstract
sealed
static
modifiziererer, und 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, wenn der
new
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 thenew
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 notwendigerweise entweder einnull
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 benull
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-Klasse A
führt eine abstrakte Methode ein F
.the abstract class A
introduces an abstract method F
. Die-Klasse B
führt eine zusätzliche-Methode ein G
, aber da Sie keine Implementierung von bereitstellt F
, B
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. C
Die Klasse überschreibt F
und stellt eine tatsächliche Implementierung bereit.Class C
overrides F
and provides an actual implementation. Da keine abstrakten Member in vorhanden sind C
, 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 Klasse deklariert 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-
sealed
abstract
Modifizierer oder-Modifizierer enthalten.A static class may not include asealed
orabstract
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 Spezifikation (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 typeobject
. - 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
oderprotected internal
deklarierter Barrierefreiheit haben.A static class cannot have members withprotected
orprotected 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, Sie enthält explizit einen static
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
Eine 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
- Der namespace_or_type_name ist
T
ein namespace_or_type_name des Formulars.T.I
The namespace_or_type_name is theT
in a namespace_or_type_name of the formT.I
, or - Der namespace_or_type_name ist das
T
in einem typeof_expression (Argument Listen1) im Formulartypeof(T)
.The namespace_or_type_name is theT
in a typeof_expression (Argument lists1) of the formtypeof(T)
.
Eine primary_expression (Funktionsmember) darf auf eine statische Klasse verweisen, wennA primary_expression (Function members) is permitted to reference a static class if
- Der primary_expression ist das
E
in einer member_access (Überprüfung der Auflösung der dynamischen Überlastung) des FormularsE.I
.The primary_expression is theE
in a member_access (Compile-time checking of dynamic overload resolution) of the formE.I
.
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, einem is
Ausdruck, einem Ausdruck, as
einem Ausdruck sizeof
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 diese 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 im class_base enthalten 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_base hat oder wenn die class_base nur Schnittstellentypen auflistet, wird angenommen, dass die direkte Basisklasse ist object
.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 {}
A
die Klasse ist die direkte Basisklasse von B
, und wird als B
abgeleitet bezeichnet A
.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 generischen Klassen Deklaration 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 ersetzt wird.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>
wäre B<string,int[]>
.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 oder-Klasse abgeleitet werden soll internal
.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.Enum
oder 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 nicht System.Attribute
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 B
wird die direkte Basisklasse von vorübergehend als festgelegt B
object
.While determining the meaning of the direct base class specification A
of a class B
, the direct base class of B
is temporarily assumed to be object
. 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. Beispiel:The example:
class A<T> {
public class B {}
}
class C : A<C.B> {}
ist fehlerhaft, da in der Basisklassen Spezifikation A<C.B>
die direkte Basisklasse von C
als betrachtet wird object
und daher (gemäß den Regeln von Namespace-und Typnamen) C
nicht als Member angesehen wird B
.is in error since in the base class specification A<C.B>
the direct base class of C
is considered to be object
, and hence (by the rules of Namespace and type names) C
is not considered to have a member B
.
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 die Basisklassen von B
A
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[]>>
, A
und object
.the base classes of D<int>
are C<int[]>
, B<IComparable<int[]>>
, A
, and object
.
Mit Ausnahme von Class object
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 abgeleitet ist A
, ist dies ein Kompilierzeitfehler, von A
dem abhängig ist B
.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, das reflexive und transitiv Schließen 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 {}
}
führt zu einem Kompilierzeitfehler, da von A
B.C
(seiner direkten Basisklasse) abhängt, das von B
(seiner unmittelbar einschließenden Klasse) abhängt, von dem zirkulär abhängig ist A
.results in a compile-time error because A
depends on B.C
(its direct base class), which depends on B
(its immediately enclosing class), which circularly depends on A
.
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
hängt von ab A
(da A
sowohl die direkte Basisklasse als auch die unmittelbar einschließende Klasse ist), aber nicht von abhängt A
B
(da B
weder eine Basisklasse noch eine einschließende Klasse von ist 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
B
die Klasse ist fehlerhaft, da Sie versucht, von der- sealed
Klasse abzuleiten A
.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 Typen-und Methoden Deklarationen können optional Typparameter Einschränkungen angeben, indem Sie type_parameter_constraints_clause s einschließen.Generic type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clause s.
type_parameter_constraints_clause
: 'where' type_parameter ':' type_parameter_constraints
;
type_parameter_constraints
: primary_constraint
| secondary_constraints
| constructor_constraint
| primary_constraint ',' secondary_constraints
| primary_constraint ',' constructor_constraint
| secondary_constraints ',' constructor_constraint
| primary_constraint ',' secondary_constraints ',' constructor_constraint
;
primary_constraint
: class_type
| 'class'
| 'struct'
;
secondary_constraints
: interface_type
| type_parameter
| secondary_constraints ',' interface_type
| secondary_constraints ',' type_parameter
;
constructor_constraint
: 'new' '(' ')'
;
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. where
Für jeden Typparameter kann höchstens 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
-Token und das- set
Token in einem Eigenschaften Accessor where
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 einer- where
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 Konstruktoreinschränkung new()
.The list of constraints given in a where
clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, new()
.
Eine primäre Einschränkung kann ein Klassentyp oder der Verweistyp Einschränkung _ class
oder die _Werttyp Einschränkung*_ sein struct
.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_TYPE sein.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 die constructor_constraint verfü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 besealed
. - Der Typ darf keinem der folgenden Typen sein:
System.Array
,System.Delegate
,System.Enum
oderSystem.ValueType
.The type must not be one of the following types:System.Array
,System.Delegate
,System.Enum
, orSystem.ValueType
. - Der Typ darf nicht sein
object
.The type must not beobject
. Da alle Typen von abgeleitetobject
sind, hätte eine solche Einschränkung keine Auswirkung, wenn Sie zulässig wäre.Because all types derive fromobject
, 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 als INTERFACE_TYPE Einschränkung angegebener Typ 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 Klausel nicht mehrmals angegeben werden
where
.A type must not be specified more than once in a givenwhere
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 als type_parameter Einschränkung angegebener Typ 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 Klausel nicht mehrmals angegeben werden
where
.A type must not be specified more than once in a givenwhere
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 Typparameter verwendet wirdS
,S
hängt von abT
.If a type parameterT
is used as a constraint for type parameterS
thenS
depends onT
. - Wenn ein Typparameter von
S
einem Typparameter abhängtT
und vonT
einem Typparameter abhängt,U
hängt vonS
abU
.If a type parameterS
depends on a type parameterT
andT
depends on a type parameterU
thenS
depends onU
.
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 vom S
Typparameter abhängt, T
dann:If type parameter S
depends on type parameter T
then:
T
darf nicht über die Werttyp Einschränkung verfügen.T
must not have the value type constraint. AndernfallsT
ist tatsächlich versiegelt, sodassS
gezwungen wird, denselben Typ wie zuT
haben, sodass zwei Typparameter nicht mehr benötigt werden.Otherwise,T
is effectively sealed soS
would be forced to be the same type asT
, eliminating the need for two type parameters.- Wenn
S
die Werttyp Einschränkung hat,T
darf keine class_type -Einschränkung aufweisen.IfS
has the value type constraint thenT
must not have a class_type constraint. - Wenn
S
über eine class_type -Einschränkung verfügtA
undT
eine class_type -Einschränkung aufweist,B
muss eine Identitäts Konvertierung oder eine implizite Verweis Konvertierung vonA
inB
oder eine implizite Verweis Konvertierung von in vorhanden seinB
A
.IfS
has a class_type constraintA
andT
has a class_type constraintB
then there must be an identity conversion or implicit reference conversion fromA
toB
or an implicit reference conversion fromB
toA
. - Wenn
S
auch vom Typparameter abhängtU
und überU
eine class_type -Einschränkung verfügtA
undT
eine class_type -Einschränkung aufweist,B
muss eine Identitäts Konvertierung oder eine implizite Verweis Konvertierung vonA
inB
oder eine implizite Verweis Konvertierung von in vorhanden seinB
A
.IfS
also depends on type parameterU
andU
has a class_type constraintA
andT
has a class_type constraintB
then there must be an identity conversion or implicit reference conversion fromA
toB
or an implicit reference conversion fromB
toA
.
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 praktisch T
die Typen System.Object
, System.ValueType
, System.Enum
und 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 das-Format aufweist new()
) enthält, kann der-Operator verwendet werden, new
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 für jeden Werttyp implizit vorhanden) oder ein Typparameter mit der Werttyp Einschränkung oder der Konstruktoreinschränkung (Weitere Informationen finden Sie unter Typparameter Einschränkungen ).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 die effektive Basisklasseobject
.IfT
has no primary constraints or type parameter constraints, its effective base class isobject
. - Wenn
T
die Werttyp Einschränkung aufweist, ist die effektive BasisklasseSystem.ValueType
.IfT
has the value type constraint, its effective base class isSystem.ValueType
. - Wenn
T
eine class_type EinschränkungC
, aber keine type_parameter Einschränkungen hat, ist die effektive BasisklasseC
.IfT
has a class_type constraintC
but no type_parameter constraints, its effective base class isC
. - Wenn
T
keine class_type Einschränkung hat, aber über eine oder mehrere type_parameter -Einschränkungen verfügt, ist die effektive Basisklasse der am häufigsten in der Reihe der in der Reihe von gültigen Basisklassen der type_parameter Einschränkungen enthaltenen Typen.IfT
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 häufigsten durch zufügungs Operator (gesteigerte Konvertierungs Operator) in der Menge, die aus der class_type -Einschränkung vonT
und den effektiven Basisklassen der type_parameter Einschränkungen besteht.IfT
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 ofT
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 Basisklasseobject
.IfT
has the reference type constraint but no class_type constraints, its effective base class isobject
.
Verwenden Sie für diese Regeln V
stattdessen den spezifischsten Basistyp von, bei dem es sich um eine value_type-Einschränkung handelt, bei der es sich um eine V
class_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_type ist.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
keine secondary_constraints hat, ist der effektive Schnittstellen Satz leer.IfT
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.IfT
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 seiner type_parameter Einschränkungen.IfT
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 Vereinigung seines Satzes von INTERFACE_TYPE Einschränkungen und die effektiven Schnittstellen Sätze der type_parameter Einschränkungen.IfT
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 ist System.ValueType
.A type parameter is known to be a reference type if it has the reference type constraint or its effective base class is not object
or System.ValueType
.
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 von IPrintable
können direkt auf aufgerufen x
werden T
, da eingeschränkt ist, um immer zu implementieren IPrintable
.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 Typdeklarationen hinweg 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. partial
ist kein Schlüsselwort und fungiert nur als Modifizierer, wenn er unmittelbar vor einem der Schlüsselwörter class
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 einzelnen Deklaration gültig, den partial
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-Modifizierer in mehreren Teilen deklarierte Typen deklariert werden partial
.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 (die public
protected
internal
Modifizierer,, und) enthält, private
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änkungen ( where
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 zu System.Object
(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
-Klasse ist IA
, IB
und IC
.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 Member ist ein Typ mit dem- partial
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 selten für c#-Code wichtig, 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 muss sein void
, und ihre Parameter dürfen nicht den- out
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 er direkt vor dem Typ angezeigt wird void
. 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 *definierende partielle Methoden Deklaration _ bezeichnet.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 _block * angegeben wird, wird die Deklaration als eine implementierende partielle Methoden Deklaration bezeichnet.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 zurückgibt void
, 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 ist private
, 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.Thepartial
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:
- Es handelt sich um einen Kompilierzeitfehler zum Erstellen eines Delegaten für die Methode (delegaterstellungs-Ausdrücke).It is a compile-time error to create a delegate to method (Delegate creation expressions).
- Es handelt sich um einen Kompilierzeitfehler, auf den in
M
einer anonymen Funktion verwiesen wird, die in einen Ausdrucks Strukturtyp konvertiert wird (Auswertung anonymer Funktions Konvertierungen in Ausdrucks Baumstruktur Typen).It is a compile-time error to refer toM
inside an anonymous function that is converted to an expression tree type (Evaluation of anonymous function conversions to expression tree types). - Ausdrücke, die als Teil des aufzurufenden von auftreten
M
, wirken sich nicht auf den eindeutigen Zuweisungs Zustand (definitive Zuweisung) aus, der potenziell zu Kompilier Zeitfehlern führen kann.Expressions occurring as part of an invocation ofM
do not affect the definite assignment state (Definite assignment), which can potentially lead to compile-time errors. M
kann nicht der Einstiegspunkt für eine Anwendung (Anwendungsstart) sein.M
cannot be the entry point for an application (Application Startup).
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 using
können für 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_declaration en und den von der direkten Basisklasse geerbten Membern eingeführt wurden.The members of a class consist of the members introduced by its class_member_declaration s and the members inherited from the direct base class.
class_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| destructor_declaration
| static_constructor_declaration
| type_declaration
;
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 s, die sofort im 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_declaration s immediately contained by the class_declaration introduce new members into this declaration space. Die folgenden Regeln gelten für class_member_declaration s:The following rules apply to class_member_declaration s:
- 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, die sich ausschließlich durch und unterscheiden
ref
out
.In addition, the signature (Signatures and overloading) of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely byref
andout
. - 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 über Signaturen, die sich ausschließlich durch und unterscheiden
ref
out
.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 byref
andout
. - 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 von this
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 Members a
in der generischen Klassen Deklaration Gen
ist das zweidimensionale Array von T
, sodass der Typ des Members a
im konstruierten Typ oben "zweidimensionales Array eines eindimensionalen Arrays von int
", oder ist 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 ist der Typ von this
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. 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 abgeleitet istB
undB
von abgeleitet istA
,C
erbt die in deklarierten MemberB
sowie die in deklarierten MemberA
.IfC
is derived fromB
, andB
is derived fromA
, thenC
inherits the members declared inB
as well as the members declared inA
. - 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 in der class_base Spezifikation ersetzt werden.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 für jede type_parameter in der Element Deklaration der entsprechende type_argument der class_base Spezifikation ersetzt wird.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 Typ D<int>
einen nicht geerbten Member, der public int G(string s)
durch das Typargument int
für den Typparameter abgerufen wurde T
.In the above example, the constructed type D<int>
has a non-inherited member public int G(string s)
obtained by substituting the type argument int
for the type parameter T
. D<int>
verfügt auch über einen geerbten Member aus der Klassen Deklaration B
.D<int>
also has an inherited member from the class declaration B
. Dieser geerbte Member wird bestimmt, indem zuerst der Basis Klassentyp bestimmt wird, B<int[]>
D<int>
indem int
T
in der Basisklassen Spezifikation ersetzt wird B<T[]>
.This inherited member is determined by first determining the base class type B<int[]>
of D<int>
by substituting int
for T
in the base class specification B<T[]>
. Anschließend wird als Typargument B
int[]
für in durch ersetzt U
public U F(long index)
, wodurch der geerbte Member bereitstellt wird public int[] F(long index)
.Then, as a type argument to B
, int[]
is substituted for U
in public U F(long index)
, yielding the inherited member public int[] F(long index)
.
Der New-Modifizierer.The new modifier
Ein class_member_declaration 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.
ZugriffsmodifiziererAccess modifiers
Eine class_member_declaration kann eine der fünf möglichen Arten von deklarierten zugreif barkeit (deklariert als Barrierefreiheit) aufweisen: 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 eine class_member_declaration keine Zugriffsmodifizierer einschließt, private
wird 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 einer member_access (Element Zugriff) des Formulars verwiesen wirdE.M
,E
muss einen Typ angeben, der enthältM
.When a static memberM
is referenced in a member_access (Member access) of the formE.M
,E
must denote a type containingM
. Es handelt sich um einen KompilierzeitfehlerE
, mit dem eine Instanz bezeichnet wird.It is a compile-time error forE
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, auf den
this
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 tothis
in such a function member.
Wenn ein Feld, eine Methode, eine Eigenschaft, ein Ereignis, ein Indexer, ein Konstruktor oder eine destrukturerdeklaration keinen static
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 (Element Zugriff) des Formulars verwiesen wirdE.M
,E
muss eine Instanz eines Typs angeben, der enthältM
.When an instance memberM
is referenced in a member_access (Member access) of the formE.M
,E
must denote an instance of a type containingM
. Es handelt sich um einen Bindungs Zeit FehlerE
, mit dem ein Typ bezeichnet wird.It is a binding-time error forE
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 als
this
(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 asthis
(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 eine Simple_name (einfache Namen) verwendet werden kann, um auf Instanzmember und statische Member zuzugreifen.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_name auf 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 (Element Zugriff) auf Instanzmemberüber-Instanzen zugegriffen werden muss, und auf statische Member muss über-Typen zugegriffen werden.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 in einer Klassen-oder Struktur Deklaration deklarierter Typ wird als * geschachtelter Typ _ bezeichnet.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 Typ * bezeichnet.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 Klasse ist ein geschachtelter B
Typ, da Sie innerhalb der Klasse deklariert wird A
und die Klasse A
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.
Vollqualifizierter 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 Typ N
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-------typtypen können public
Barrierefreiheit haben oder internal
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 deklarierter, in einer Klasse deklarierter Typ kann über eine beliebige von fünf Formen der deklarierten Barrierefreiheit (
public
,,protected internal
protected
,internal
oder) verfügen,private
und wie bei anderen Klassenmembern wird standardmäßig dieprivate
deklarierte Barrierefreiheit verwendet.A nested type that is declared in a class can have any of five forms of declared accessibility (public
,protected internal
,protected
,internal
, orprivate
) and, like other class members, defaults toprivate
declared accessibility. - Ein in einer Struktur deklarierter, in einer Struktur deklarierter Typ kann über eine von drei Formen der deklarierten Barrierefreiheit (
public
, oder) verfügen, und wie bei anderen Strukturmemberninternal
private
werden standardmäßigprivate
deklarierte Zugriffsmöglichkeiten verwendet.A nested type that is declared in a struct can have any of three forms of declared accessibility (public
,internal
, orprivate
) and, like other struct members, defaults toprivate
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 Klasse Node
.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 M
in der Liste definierte Klasse, die die M
in definierte Methode verbirgt Base
.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). Insbesondere innerhalb eines geschachtelten this
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 Zugriff bereitgestellt werden, indem der this
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. Beispiel:The following example
using System;
class C
{
int i = 123;
public void F() {
Nested n = new Nested(this);
n.G();
}
public class Nested
{
C this_c;
public Nested(C c) {
this_c = c;
}
public void G() {
Console.WriteLine(this_c.i);
}
}
}
class Test
{
static void Main() {
C c = new C();
c.F();
}
}
zeigt diese Methode.shows this technique. Eine Instanz von C
erstellt eine Instanz von Nested
und übergibt ihren eigenen this
an Nested
den-Konstruktor, um den nachfolgenden Zugriff auf C
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 enthaltenden Typs, die über die Berechtigung "Barrierefreiheit" verfügen private
protected
.A nested type has access to all of the members that are accessible to its containing type, including members of the containing type that have private
and protected
declared accessibility. 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 an C
, die eine-Klasse enthält Nested
.shows a class C
that contains a nested class Nested
. In Nested
G
Ruft die-Methode die statische Methode F
auf, die in definiert C
ist, und F
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 Derived.Nested
schsted Class greift auf die geschützte Methode zu, die F
in Derived
der Basisklasse von definiert ist, Base
indem Sie durch eine Instanz von aufruft Derived
.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, der aus erstellt wurde Inner
. 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, die eine Eigenschaft, ein Ereignis oder einen Indexer ist, 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 dem new
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 für den Get-oder Set-Zugriff 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 in allen c#-Implementierungen einheitlich sind.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 T
sind 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 P
und 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-Klasse wird B
von abgeleitet A
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 T
sind 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 L
sind 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 Elementname Item
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
Ein *Constant _ 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. Eine _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
;
Eine 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 vom constant_declaration deklarierten Member.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 noch einen static
Modifizierer erforderlich.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 einer constant_declaration der den Typ der Member angibt, 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_declarator en, von denen jeder einen neuen Member einführt.The type is followed by a list of constant_declarator s, 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), der den Wert des Members übergibt.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
, float
, double
, decimal
, bool
, string
, ein enum_type oder eine reference_type sein.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 (Barrierefreiheits Einschrä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 (einfache Namen) 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 an einer constant_expression teilnehmen.A constant can itself participate in a constant_expression. Daher kann eine Konstante in jedem Konstrukt verwendet werden, das eine constant_expression erfordert.Thus, a constant may be used in any construct that requires a constant_expression. Beispiele für derartige Konstrukte sind case
Bezeichnungen, goto case
Anweisungen, Element enum
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 reference_type außer string
ist, den new
-Operator anzuwenden, und da der new
Operator in einem constant_expression nicht zulässig ist, ist der einzige mögliche Wert für Konstanten von reference_type s, der nicht 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_type s 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_expression berechnet werden kann, readonly
wird stattdessen ein Feld (schreibgeschützte Felder) verwendet.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 wertet zunächst A.Y
B.Z
A.X
die Werte 10
, 11
und 12
aus und wertet Sie anschließend aus.the 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. Wenn A
und in separaten Programmen in Bezug auf das obige Beispiel B
deklariert wurden, kann es möglich sein, von abhängig zu sein A.X
B.Z
, aber es B.Z
kann nicht gleichzeitig von abhängen A.Y
.Referring to the example above, if A
and B
were declared in separate programs, it would be possible for A.X
to depend on B.Z
, but B.Z
could then not simultaneously depend on A.Y
.
FelderFields
Ein *Field _ 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
;
Eine 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) enthalten.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 eine field_declaration einen readonly
Modifizierer (schreibgeschützte Felder) 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 vom field_declaration deklarierten Member.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 einer field_declaration der den Typ der Member angibt, 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_declarator en, von denen jeder einen neuen Member einführt.The type is followed by a list of variable_declarator s, 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 (Barrierefreiheits Einschrä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 (einfache Namen) 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 Dekrementoperatoren) abgerufen und geändert werden.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 von c# unterstützt werden. manchmal werden Sie als statische Variablen bzw. _ Instanzvariablen * bezeichnet.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.
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 verwiesen wird (Member-Zugriff) E.M
, wenn M
ein statisches Feld ist, E
muss einen Typ angeben M
, der enthält, und 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 eine 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 einer 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 als-out
oder-Parameter zu übergebenref
.These are also the only contexts in which it is valid to pass areadonly
field as anout
orref
parameter.
Der Versuch, einem readonly
Feld zuzuweisen oder es als- out
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 Member Black
, White
, Red
, Green
und Blue
können nicht als Member deklariert werden, const
da ihre Werte zur Kompilierzeit nicht berechnet werden können.the 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
Program2
Namespaces und 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 von der-Anweisung ausgegeben wird Console.WriteLine
, 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 der Wert von X
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. War jedoch X
eine Konstante. der Wert von wurde zum X
Zeitpunkt der Program2
Kompilierung abgerufen und bleibt von Änderungen in Program1
bis zur Program2
erneuten Kompilierung nicht beeinträchtigt.However, had X
been a constant, the value of X
would have been obtained at the time Program2
was compiled, and would remain unaffected by changes in Program1
until Program2
is recompiled.
Flüchtige FelderVolatile fields
Wenn eine 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 vom lock_statement (lock-Anweisung) bereitgestellt werden.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 Lese Vorgang 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 schreiben bezeichnet.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:
- Eine reference_type.A reference_type.
- Der Typ
byte
,sbyte
,short
,,,ushort
int
uint
,char
,float
,bool
,System.IntPtr
oderSystem.UIntPtr
.The typebyte
,sbyte
,short
,ushort
,int
,uint
,char
,float
,bool
,System.IntPtr
, orSystem.UIntPtr
. - Ein enum_type mit dem Aufzählungs Basistyp
byte
,sbyte
,short
,ushort
,int
oderuint
.An enum_type having an enum base type ofbyte
,sbyte
,short
,ushort
,int
, oruint
.
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-Methode Main
einen neuen Thread, der die-Methode ausführt Thread2
.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 result
mit dem Namen und speichert dann true
im flüchtigen Feld finished
.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 das Feld finished
auf festgelegt wird true
, und liest dann das Feld result
.The main thread waits for the field finished
to be set to true
, then reads the field result
. Da finished
deklariert wurde volatile
, muss der Haupt Thread den Wert 143
aus dem Feld lesen result
.Since finished
has been declared volatile
, the main thread must read the value 143
from the field result
. Wenn das Feld finished
nicht deklariert wurde volatile
, ist es zulässig, dass der Speicher für result
den Haupt Thread nach dem Speichern in sichtbar finished
ist, sodass der Haupt Thread den Wert 0
aus dem Feld liest 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 deklarieren finished
als volatile
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 und i
automatisch mit den Standardwerten initialisiert werden.because b
and i
are both automatically initialized to default values.
VariableninitialisiererVariable initializers
Feld Deklarationen können variable_initializer s enthalten.Field declarations may include variable_initializer s. 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 x
erfolgt, wenn statische Feldinitialisierer ausgeführt werden und Zuweisungen zu i
und auftreten, s
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 0
(den Standardwert für) initialisiert werden, int
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 ausgeführt wird a
, ist der Wert von b
0 (null) und a
wird daher mit initialisiert 1
.When the initializer for a
runs, the value of b
is zero, and so a
is initialized to 1
. Wenn der Initialisierer für ausgeführt wird b
, ist der Wert von a
bereits 1
, und b
wird daher mit initialisiert 2
.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 X
Initialisierers und Y
des 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 B
statischen Konstruktor (und somit B
die statischen Feldinitialisierer) ausführen müssen, müssen vor A
dem statischen Konstruktor und den feldinitialisierern ausgeführt werden.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, this
der in einem Variableninitialisierer referenziert werden kann, da es sich um einen Kompilierzeitfehler für einen Variableninitialisierer handelt, der über eine Simple_name auf ein Instanzmember verweist.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 für y
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
Ein *method _ ist ein Member, der eine Berechnung oder eine Aktion implementiert, die von einem Objekt oder einer Klasse ausgefü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_declaration * s deklariert:Methods are declared using _method_declaration*s:
method_declaration
: method_header method_body
;
method_header
: attributes? method_modifier* 'partial'? return_type member_name type_parameter_list?
'(' formal_parameter_list? ')' type_parameter_constraints_clause*
;
method_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'static'
| 'virtual'
| 'sealed'
| 'override'
| 'abstract'
| 'extern'
| 'async'
| method_modifier_unsafe
;
return_type
: type
| 'void'
;
member_name
: identifier
| interface_type '.' identifier
;
method_body
: block
| '=>' expression ';'
| ';'
;
Eine method_declaration kann eine Reihe von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), ( new
der neuen Modifizierer), static
(statische und Instanzmethoden), virtual
(virtuelle Methoden), override
(Überschreibungs Methoden), sealed
(versiegelte Methoden), abstract
(abstrakte Methoden) und extern
(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
undoverride
.The declaration includes at most one of the following modifiers:static
,virtual
, andoverride
. - Die-Deklaration enthält höchstens einen der folgenden Modifizierer:
new
undoverride
.The declaration includes at most one of the following modifiers:new
andoverride
. - Wenn die Deklaration den-
abstract
Modifizierer enthält, enthält die Deklaration keinen der folgenden Modifiziererstatic
:virtual
,sealed
oderextern
.If the declaration includes theabstract
modifier, then the declaration does not include any of the following modifiers:static
,virtual
,sealed
orextern
. - Wenn die Deklaration den-
private
Modifizierer enthält, enthält die Deklaration keinen der folgenden Modifizierer:virtual
,override
oderabstract
.If the declaration includes theprivate
modifier, then the declaration does not include any of the following modifiers:virtual
,override
, orabstract
. - Wenn die Deklaration den-
sealed
Modifizierer enthält, enthält die Deklaration auch den-override
Modifizierer.If the declaration includes thesealed
modifier, then the declaration also includes theoverride
modifier. - Wenn die Deklaration den-
partial
Modifizierer enthält, enthält Sie keinen der folgenden Modifizierer:new
,public
,protected
,internal
,private
,virtual
,sealed
,override
,abstract
oderextern
.If the declaration includes thepartial
modifier, then it does not include any of the following modifiers:new
,public
,protected
,internal
,private
,virtual
,sealed
,override
,abstract
, orextern
.
Eine Methode, die über den async
-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 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 void
.If the declaration includes the partial
modifier, then the return type must be void
.
Der MEMBER_NAME der den Namen der Methode angibt.The member_name specifies the name of the method. Wenn die Methode keine explizite Schnittstellenmember-Implementierung (explizite Schnittstellenmember-Implementierungen) ist, ist die 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 die MEMBER_NAME aus einer INTERFACE_TYPE gefolgt von " .
" 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 gibt die Typparameter der Methode (Typparameter) an.The optional type_parameter_list specifies the type parameters of the method (Type parameters). Wenn ein type_parameter_list angegeben wird, ist die Methode eine *generische Methode _.If a type_parameter_list is specified the method is a *generic method _. Wenn die Methode über einen extern
Modifizierer verfügt, kann kein _type_parameter_list * angegeben werden.If the method has an extern
modifier, a _type_parameter_list* cannot be specified.
Der optionale formal_parameter_list gibt die Parameter der Methode (Methoden Parameter) an.The optional formal_parameter_list specifies the parameters of the method (Method parameters).
Die optionalen type_parameter_constraints_clause s 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 verfügt über keinen- override
Modifizierer.The optional type_parameter_constraints_clause s specify constraints on individual type parameters (Type parameter constraints) and may only be specified if a type_parameter_list is also supplied, and the method does not have an override
modifier.
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 einer _block *, die die Anweisungen angibt, die beim Aufrufen der Methode ausgeführt werden sollen.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.
Für abstract
extern
die-Methode und die-Methode besteht die method_body einfach aus einem Semikolon.For abstract
and extern
methods, the method_body consists simply of a semicolon. Für partial
Methoden kann die method_body entweder aus einem Semikolon, einem Block Text oder einem Ausdrucks Text bestehen.For partial
methods the method_body may consist of either a semicolon, a block body or an expression body. Bei allen anderen Methoden ist die method_body entweder ein Block Körper oder ein Ausdrucks Körper.For all other methods, the method_body is either a block body or an expression body.
Wenn das method_body aus einem Semikolon besteht, kann die Deklaration den- async
Modifizierer nicht enthalten.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, die sich ausschließlich durch und unterscheiden ref
out
.In addition, the signature of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref
and out
.
Die type_parameter s der Methode befinden sich im Gültigkeitsbereich des gesamten method_declaration und können verwendet werden, um Typen in diesem Bereich in return_type, method_body und type_parameter_constraints_clause s, jedoch nicht in Attributen zu bilden.The method's type_parameter s are in scope throughout the method_declaration, and can be used to form types throughout that scope in return_type, method_body, and type_parameter_constraints_clause s but not in attributes.
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 durch den formal_parameter_list der 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 formaler Parameter besteht aus mindestens einem durch Trennzeichen getrennten Parameter, bei dem nur der letzte eine parameter_array sein 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
this
-Modifizierer oder-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 wird als *optionaler Parameter _ bezeichnet, wohingegen ein fixed_parameter * ohne default_argument ein *erforderlicher Parameter ist.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_list * angezeigt werden.A required parameter may not appear after an optional parameter in a _formal_parameter_list*.
Ein- ref
oder- out
Parameter kann keine default_argument haben.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 derS
ein Werttyp ist.an expression of the formnew S()
whereS
is a value type - ein Ausdruck der Form,
default(S)
in derS
ein Werttyp ist.an expression of the formdefault(S)
whereS
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), einer expliziten Schnittstellenmember-Implementierung (explizite Schnittstellenmember-Implementierungen) oder in einer Indexer-Deklaration mit einem einzelnen Parameter (Indexer) auftreten, sollte der Compiler eine Warnung angeben, da diese Member niemals so aufgerufen werden können, dass Argumente ausgelassen werden.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_type und 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.
Eine 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
) { }
Im formal_parameter_list für M
i
ist 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 den neu erstellten formalen Parametern Werte oder Variablen Verweise zu.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 (einfache 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 theref
modifier. - Ausgabeparameter, die mit dem-
out
Modifizierer deklariert werden.Output parameters, which are declared with theout
modifier. - Parameter Arrays, die mit dem-
params
Modifizierer deklariert werden.Parameter arrays, which are declared with theparams
modifier.
Wie in Signaturen und überladenbeschrieben, ref
sind 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 einem- ref
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 bestehen, ref
gefolgt von einem variable_reference (genaue Regeln zum Bestimmen der eindeutigen Zuweisung) desselben Typs 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 Aufruf von Swap
in Main
x
stellt i
und y
dar j
.For the invocation of Swap
in Main
, x
represents i
and y
represents j
. Folglich hat der Aufruf die Auswirkungen, die Werte von i
und auszutauschen j
.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 Aufruf von F
in G
übergibt einen Verweis auf s
sowohl für a
als auch für b
.the invocation of F
in G
passes a reference to s
for both a
and b
. Daher beziehen sich bei diesem Aufruf die Namen, s
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 bestehen, out
gefolgt von einem variable_reference (präzise Regeln zum Bestimmen der eindeutigen Zuweisung) desselben Typs wie der formale 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. 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, dass die dir
Zuweisung der-Variable und der-Variablen aufgehoben name
werden kann, bevor Sie an übermittelt werden SplitPath
, 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 einem- params
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 string[]
können die-Typen und string[][]
als Typ eines Parameter Arrays verwendet werden, aber der-Typ ist string[,]
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 und zu kombinieren ref
out
.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 tatsächliches Argument.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 wird F
das Array einfach a
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, dass eine object[]
selbst implizit in den Typ konvertiert werden kann object
.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 F
ist die normale Form von anwendbar, F
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 von aus F
, 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 konvertiert werden object[]
).In the second and third invocations, the normal form of F
is not applicable because no implicit conversion exists from the argument type to the parameter type (type object
cannot be implicitly converted to type object[]
). Allerdings ist die erweiterte Form von F
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 ist object[]
).As a result, a one-element object[]
is created by the invocation, and the single element of the array is initialized with the given argument value (which itself is a reference to an object[]
).
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 einer member_access verwiesen wird (Member-Zugriff) E.M
, wenn M
eine statische Methode ist, E
muss einen Typ angeben, der enthält M
, und wenn M
eine Instanzmethode ist, muss eine E
Instanz eines Typs angeben, der enthält 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. Wenn eine Methode mit dem Namen mit einer N
Argumentliste A
für eine Instanz mit einem Kompilier Zeittyp C
und einem Lauf Zeittyp R
(wobei R
entweder C
oder eine von abgeleitete Klasse ist) aufgerufen wird 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
,N
und angewendetA
, um eine bestimmte MethodeM
aus dem Satz von Methoden auszuwählen, der in deklariert und von geerbt wurdeC
.First, overload resolution is applied toC
,N
, andA
, to select a specific methodM
from the set of methods declared in and inherited byC
. Dies wird unter Methodenaufrufebeschrieben.This is described in Method invocations. - Wenn dann
M
eine nicht virtuelle Methode ist,M
wird aufgerufen.Then, ifM
is a non-virtual method,M
is invoked. - Andernfalls
M
ist eine virtuelle Methode, und die am meisten abgeleitete Implementierung vonM
in Bezug aufR
wird aufgerufen.Otherwise,M
is a virtual method, and the most derived implementation ofM
with respect toR
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 Methode in M
Bezug auf eine Klasse R
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 von enthältM
, dann handelt es sich hierbei um die am meisten abgeleitete Implementierung vonM
.IfR
contains the introducingvirtual
declaration ofM
, then this is the most derived implementation ofM
. - Wenn andernfalls
R
einen von enthältoverride
M
, ist dies die am meisten abgeleitete Implementierung vonM
.Otherwise, ifR
contains anoverride
ofM
, then this is the most derived implementation ofM
. - Andernfalls ist die am meisten abgeleitete Implementierung von
M
in Bezug aufR
die gleiche wie die am meisten abgeleitete Implementierung von inM
Bezug auf die direkte Basisklasse vonR
.Otherwise, the most derived implementation ofM
with respect toR
is the same as the most derived implementation ofM
with respect to the direct base class ofR
.
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 eingeführt G
.In the example, A
introduces a non-virtual method F
and a virtual method G
. Die-Klasse B
führt eine neue nicht virtuelle Methode ein F
, wodurch die geerbte-Methode ausgeblendet F
wird, und überschreibt auch die geerbte-Methode G
.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, dass die-Anweisung aufruft a.G()
B.G
, nicht A.G
.Notice that the statement a.G()
invokes B.G
, not A.G
. Dies liegt daran, dass der Lauf Zeittyp der-Instanz (d. h B
.), nicht der Kompilier Zeittyp der-Instanz (d A
. 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
D
Klassen und enthalten zwei virtuelle Methoden mit der gleichen Signatur: die, die von eingeführt wurde, A
und die, die von eingeführt wird C
.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 eingeführte Methode C
blendet die von geerbte Methode aus A
.The method introduced by C
hides the method inherited from A
. Folglich überschreibt die Überschreibungs Deklaration in D
die von eingeführte Methode C
, und es ist nicht möglich, D
die durch eingeführte Methode zu überschreiben A
.Thus, the override declaration in D
overrides the method introduced by C
, and it is not possible for D
to override the method introduced by A
. 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 Sie auf eine Instanz von D
ü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 Methode bezeichnet.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 Methode bezeichnet.The method overridden by an override
declaration is known as the overridden base method. Bei einer M
in einer Klasse deklarierten Überschreibungs Methode C
wird die überschriebene Basis Methode bestimmt, indem jeder Basis Klassentyp von untersucht wird C
, beginnend mit dem direkten Basis Klassentyp von C
und mit jedem aufeinander folgenden direkten Basis Klassentyp, bis in einem gegebenen Basis Klassentyp mindestens eine barrierefreie Methode gefunden wird, die dieselbe Signatur wie M
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 als verfügbar betrachtet, wenn dies der Fall ist, wenn dies der Fall ist, public
protected
protected internal
oder wenn Sie internal
in demselben Programm wie deklariert und deklariert wird C
.For the purposes of locating the overridden base method, a method is considered accessible if it is public
, if it is protected
, if it is protected internal
, or if it is internal
and declared in the same program as C
.
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 Zugriff) 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 deklarierte Methode auf A
.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 der Aufruf von B
geschrieben wurde ((A)this).PrintFields()
, würde er die in deklarierte Methode rekursiv aufrufen, PrintFields
B
nicht die in deklarierte Methode A
, da PrintFields
virtuell ist und der Lauf Zeittyp von ((A)this)
ist B
.Had 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 eines- override
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 in B
enthält keinen override
-Modifizierer und überschreibt daher nicht die- F
Methode in A
.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 in B
die-Methode in A
, und es wird eine Warnung ausgegeben, da die Deklaration keinen new
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 in B
F
blendet 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 erweitert C
.Since the new F
in B
has private access, its scope only includes the class body of B
and does not extend to C
. Daher ist es zulässig, die Deklaration von F
in C
zu überschreiben, das F
von geerbt wurde A
.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 Methode bezeichnet.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-Klasse B
stellt zwei Überschreibungs Methoden bereit: eine F
Methode, die über den sealed
-Modifizierer und eine- G
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. B
die Verwendung des versiegelten verhindert, dass weiter überschrieben wird modifier
C
F
.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 Methode bezeichnet.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 aufweisen virtual
.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 die 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
Box
Klassen und sind konkrete Shape
Implementierungen.The Ellipse
and Box
classes are concrete Shape
implementations. Da diese Klassen nicht abstrakt sind, müssen Sie die-Methode überschreiben Paint
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 einen base_access (Basis Zugriff), 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 Aufruf wird ein Kompilierzeitfehler gemeldet, base.F()
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-Klasse A
deklariert eine virtuelle Methode, die-Klasse B
überschreibt diese Methode mit einer abstrakten-Methode, und die-Klasse C
ü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 Methode _ bezeichnet.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 Sprache als c#.External methods are implemented externally, typically using a language other than C#. Da eine externe Methoden Deklaration keine tatsächliche Implementierung bereitstellt, besteht die _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 Verbindung mit einem- DllImport
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 Methode bezeichnet.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 Erweiterungsmethode bezeichnet.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 aufweisen this
, 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 in verfügbar string[]
, und die- ToInt32
Methode ist in verfügbar string
, 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 Rückgabetyp ist System.Threading.Tasks.Task
.The result type of a method is void
if the return type is void
, or if the method is async and the return type is System.Threading.Tasks.Task
. Andernfalls ist der Ergebnistyp einer nicht Async-Methode der Rückgabetyp, und der Ergebnistyp einer Async-Methode mit dem Rückgabetyp System.Threading.Tasks.Task<T>
ist T
.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 verfügt, ist return
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
eine statement_expression sein, und der Text entspricht genau einem Block Text des Formulars { 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, return
muss 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
-Methode und die- H
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 *Property _ 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 _ *Accessoren**, die die auszuführenden Anweisungen angeben, wenn ihre Werte gelesen oder geschrieben werden.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_declaration s deklariert:Properties are declared using property_declaration s:
property_declaration
: attributes? property_modifier* type member_name property_body
;
property_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'static'
| 'virtual'
| 'sealed'
| 'override'
| 'abstract'
| 'extern'
| property_modifier_unsafe
;
property_body
: '{' accessor_declarations '}' property_initializer?
| '=>' expression ';'
;
property_initializer
: '=' variable_initializer ';'
;
Eine property_declaration kann eine Reihe von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), ( new
der neuen Modifizierer), static
(statische und Instanzmethoden), virtual
(virtuelle Methoden), override
(Überschreibungs Methoden), sealed
(versiegelte Methoden), abstract
(abstrakte Methoden) und extern
(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 der 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 Implementierung des Schnittstellenmembers ist, ist die 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 die MEMBER_NAME aus einer INTERFACE_TYPE gefolgt von " .
" 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 (Barrierefreiheits Einschränkungen).The type of a property must be at least as accessible as the property itself (Accessibility constraints).
Eine property_body kann entweder aus einem -**Accessor Body* _ oder einem Ausdrucks Körper bestehen.A property_body may either consist of an accessor body _ or an _expression body*_. In einem Accessor-Text _accessor_declarations *, der in die {
Token "" und "" eingeschlossen werden muss }
, die Accessoren (Accessoren) der Eigenschaft.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 Anweisungs Text { get { return E; } }
und kann daher nur zum Angeben von nur-Getter-Eigenschaften verwendet werden, bei denen das Ergebnis des Getters von einem einzelnen Ausdruck angegeben wird.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.
Eine 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-oder- ref
Argument zu übergeben out
.Thus, it is not possible to pass a property as a ref
or out
argument.
Wenn eine Eigenschafts Deklaration einen extern
Modifizierer enthält, wird die Eigenschaft als *externe Eigenschaft _ bezeichnet.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 einen static
Modifizierer enthält, wird die Eigenschaft als *statische Eigenschaft _ bezeichnet.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 _ -Instanzeigenschaft * bezeichnet.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 this
den 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 verwiesen wird (Member-Zugriff) E.M
, wenn M
eine statische Eigenschaft ist, E
muss einen Typ angeben M
, der enthält, und wenn M
eine Instanzeigenschaft ist, muss E eine Instanz eines Typs angeben, der enthält 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 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 einer get_accessor_declaration, einem set_accessor_declaration oder beidem.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Jede Accessordeklaration 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_modifier s unterliegt den folgenden Einschränkungen:The use of accessor_modifier s is governed by the following restrictions:
- Eine 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 einenget
-Accessor als auch einen-set
Accessor aufweist und dann nur für einen dieser Accessoren zulässig ist.For a property or indexer that has nooverride
modifier, an accessor_modifier is permitted only if the property or indexer has both aget
andset
accessor, and then is permitted only on one of those accessors. - Für eine Eigenschaft oder einen Indexer, der einen-
override
Modifizierer enthält, muss ein-Accessor mit dem accessor_modifier(sofern vorhanden) des Accessors, der überschrieben wird, abgleichen.For a property or indexer that includes anoverride
modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden. - Der 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 von verfügt
public
, kann die accessor_modifier entwederprotected internal
,internal
,protected
oder seinprivate
.If the property or indexer has a declared accessibility ofpublic
, the accessor_modifier may be eitherprotected internal
,internal
,protected
, orprivate
. - Wenn die Eigenschaft oder der Indexer über eine deklarierte Zugriffsmöglichkeit von verfügt
protected internal
, kann die accessor_modifier entwederinternal
,protected
oder seinprivate
.If the property or indexer has a declared accessibility ofprotected internal
, the accessor_modifier may be eitherinternal
,protected
, orprivate
. - Wenn die Eigenschaft oder der Indexer eine deklarierte Zugriffsmöglichkeit von
internal
oder aufweistprotected
, muss die accessor_modifier seinprivate
.If the property or indexer has a declared accessibility ofinternal
orprotected
, the accessor_modifier must beprivate
. - Wenn die Eigenschaft oder der Indexer über eine deklarierte Zugriffsmöglichkeit von verfügt
private
, kann keine accessor_modifier verwendet werden.If the property or indexer has a declared accessibility ofprivate
, no accessor_modifier may be used.
- Wenn die Eigenschaft oder der Indexer über eine deklarierte Zugriffsmöglichkeit von verfügt
Für 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 der _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 auf eine Eigenschaft verwiesen wird, wird der- get
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 return
müssen alle 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 und einem void
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 value
.The implicit parameter of a set
accessor is always named value
. Wenn auf eine Eigenschaft als Ziel einer Zuweisung (Zuweisungs Operatoren) verwiesen wird, oder als Operand von ++
oder --
(postfix Inkrement-und Dekrementoperatoren, Präfix Inkrement-und Dekrementoperatoren), wird der- set
Accessor mit einem Argument aufgerufen (dessen Wert der rechten Seite der Zuweisung oder dem Operanden des OR-Operators entspricht), der ++
--
den neuen Wert (einfache Zuweisung) bereitstellt.When a property is referenced as the target of an assignment (Assignment operators), or as the operand of ++
or --
(Postfix increment and decrement operators, Prefix increment and decrement operators), the set
accessor is invoked with an argument (whose value is that of the right-hand side of the assignment or the operand of the ++
or --
operator) that provides the new value (Simple assignment). Der Text einer- set
Zugriffsmethode muss den Regeln für Methoden entsprechen, die void
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 im set
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 dem Namen aufweist value
, handelt es sich um einen Kompilierzeitfehler für eine lokale Variable oder eine Konstante Deklaration in einem- set
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 der get
-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 auch einen-set
Accessor enthält, wird als Lese-/Schreib-Eigenschaft bezeichnet.A property that includes both aget
accessor and aset
accessor is said to be a read-write property. - Eine Eigenschaft, die nur einen-
get
Accessor aufweist, wird als schreibgeschützte Eigenschaft bezeichnet.A property that has only aget
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 aset
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 eine öffentliche Caption
Eigenschaft.the Button
control declares a public Caption
property. Der- get
Accessor der- Caption
Eigenschaft gibt die Zeichenfolge zurück, die im privaten Feld gespeichert ist caption
.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, und der set
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 Verwendung der- Caption
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, und der- get
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
-und- set
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 in blendet B
die- P
Eigenschaft in in A
Bezug auf das 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 schreibgeschützte P
Eigenschaft in B
die schreibgeschützte P
Eigenschaft in A
ausblendet.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 Hidden- P
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. Beachten Sie folgendes Beispiel: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. Der Speicherort wird sowohl als als X
auch als Eigenschaft Y
Location
des Typs öffentlich verfügbar gemacht Point
.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 Label
bequemer wird, den Speicherort als intern zu speichern Point
, 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 public readonly
Felder, wäre es unmöglich, eine solche Änderung an der- Label
Klasse vorzunehmen.Had 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 Inlining bezeichnet 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 eines get
Accessoren konzeptionell Äquivalent zum Lesen des Werts eines Felds ist, gilt es als ungültiges Programmier Format für get
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 der- Next
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 für get
Accessoren bedeutet nicht, dass get
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. get
Accessoren 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. 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 Eigenschaften,, In
Out
und Error
, 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, kann die- Console
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 liegende TextWriter
für das Ausgabegerät wird erstellt.the underlying TextWriter
for the output device is created. Wenn die Anwendung jedoch keinen Verweis auf die-Eigenschaft und die-Eigenschaft durchführt In
Error
, 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 als (schreibgeschützte readonly
Felder) 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 eine property_initializer verfügen, die direkt auf das dahinter liegende 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_modifier verfügt, wird die Zugriffs Domäne (Barrierefreiheits Domänen) der Zugriffsmethode mithilfe der deklarierten Barrierefreiheit des accessor_modifier bestimmt.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 keine accessor_modifier hat, 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,
get
muss der Accessor vorhanden und zugänglich sein.If the usage is as a value (Values of expressions), theget
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), theset
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,get
müssen die Accessoren und derset
Accessor 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 theget
accessors and theset
accessor must exist and be accessible.
Im folgenden Beispiel wird die-Eigenschaft A.Text
durch die-Eigenschaft ausgeblendet B.Text
, auch in Kontexten, in denen nur der- set
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-Eigenschaft B.Count
nicht von der-Klasse aufgerufen M
werden, sodass stattdessen die barrierefreie Eigenschaft A.Count
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_modifier deklariert 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 die 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 den abstract
- 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 einschließen, die eine- override
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 Deklaration bezeichnet.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 kann den- sealed
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.Aget
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, einemvoid
Rückgabetyp und denselben Modifiziererwerten wie die enthaltende Eigenschaft.Aset
accessor corresponds to a method with a single value parameter of the property type, avoid
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; }
}
X
ist 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 abgeleitet A
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 von X
, Y
und Z
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 von X
und der- set
Accessor von Y
verwenden das- base
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 von Z
überschreibt beide abstrakten Accessoren – folglich gibt es keine ausstehenden abstrakten Funktionsmember in B
, und es darf sich B
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 deklariert wird override
, 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. 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, der es einem Objekt oder einer Klasse ermöglicht, Benachrichtigungen bereitzustellen.An *event _ is a member that enables an object or class to provide notifications. Clients können ausführbaren Code für Ereignisse anfügen, indem Sie die _ -Ereignishandler * bereitstellen.Clients can attach executable code for events by supplying _*event handlers**.
Ereignisse werden mithilfe von event_declaration s deklariert:Events are declared using event_declaration s:
event_declaration
: attributes? event_modifier* 'event' type variable_declarators ';'
| attributes? event_modifier* 'event' type member_name '{' event_accessor_declarations '}'
;
event_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'static'
| 'virtual'
| 'sealed'
| 'override'
| 'abstract'
| 'extern'
| event_modifier_unsafe
;
event_accessor_declarations
: add_accessor_declaration remove_accessor_declaration
| remove_accessor_declaration add_accessor_declaration
;
add_accessor_declaration
: attributes? 'add' block
;
remove_accessor_declaration
: attributes? 'remove' block
;
Eine event_declaration kann eine Reihe von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), ( new
der neuen Modifizierer), static
(statische und Instanzmethoden), virtual
(virtuelle Methoden), override
(Überschreibungs Methoden), sealed
(versiegelte Methoden), abstract
(abstrakte Methoden) und extern
(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 der 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_declarations enthalten.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.
In einer Ereignis Deklaration, die event_accessor_declarations auslässt, wird mindestens ein Ereignis definiert – eines für jede variable_declarator s.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarator s. Die Attribute und Modifizierer gelten für alle Member, die von einem solchen event_declaration deklariert werden.The attributes and modifiers apply to all of the members declared by such an event_declaration.
Es handelt sich hierbei um einen Kompilierzeitfehler für eine event_declaration , die sowohl den abstract
Modifizierer als auch das mit geschweiften Klammern getrennte event_accessor_declarations enthalten soll.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 Ereignis _ bezeichnet.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 das _event_accessor_declarations * einschließ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, der eine variable_initializer einschließt.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 Liste von Ereignis Handlern abrufen oder ändern.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 ist und der Verweis außerhalb des Typs stattfindet, der die Deklaration von enthält x
, hat das Ergebnis des Vorgangs den Typ void
(im Gegensatz zum Typ von x
, mit dem Wert 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-Klasse angefügt werden Button
:The following example shows how event handlers are attached to instances of the Button
class:
public delegate void EventHandler(object sender, EventArgs e);
public class Button: Control
{
public event EventHandler Click;
}
public class LoginDialog: Form
{
Button OkButton;
Button CancelButton;
public LoginDialog() {
OkButton = new Button(...);
OkButton.Click += new EventHandler(OkButtonClick);
CancelButton = new Button(...);
CancelButton.Click += new EventHandler(CancelButtonClick);
}
void OkButtonClick(object sender, EventArgs e) {
// Handle OkButton.Click event
}
void CancelButtonClick(object sender, EventArgs e) {
// Handle CancelButton.Click event
}
}
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 sein extern
und darf nicht explizit event_accessor_declarations 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 das Feld null
.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;
}
}
Click
wird 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 der- Button
Klasse löst das-Ereignis aus Click
.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 der Button
Click
-Klasse kann 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 Ereignisses anfügt Click
, 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 (Ausdrücke zum Erstellen anonymer Objekte) für ein statisches Ereignis aufrechterhalten werden.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 X
-Klasse bewirken Verweise auf Ev
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 ausgeblendete Feld zu verweisen __Ev
(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 in der Regel aus, wie im Button
obigen Beispiel gezeigt.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 enthalten 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.
Die event_accessor_declarations eines Ereignisses geben 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 einer add_accessor_declaration und einer remove_accessor_declaration.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Jede Accessordeklaration besteht aus dem Token add
oder remove
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 einem remove_accessor_declaration zugeordnete Block gibt die Anweisungen an, die beim Entfernen eines Ereignis Handlers ausgeführt werden sollen.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 einer add_accessor_declaration oder einer remove_accessor_declaration muss den Regeln für Methoden entsprechen, die void
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 Namen aufweist value
, 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-Methode ordnet einem AddEventHandler
Schlüssel einen Delegatwert zu, die GetEventHandler
Methode gibt den derzeit einem Schlüssel zugeordneten Delegaten zurück, und die RemoveEventHandler
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 Ereignis _ bezeichnet.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 _ -Instanzereignis * bezeichnet.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 this
in den Accessoren dieses Ereignisses als (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 verwiesen wird (Member-Zugriff) E.M
, wenn M
ein statisches Ereignis ist, E
muss einen Typ angeben M
, der 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_declarations bereitstellen.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 Modifizierer angibt, in einer abgeleiteten Klasse überschrieben werden override
.The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override
modifier. Dies wird als über schreibende Ereignis Deklaration bezeichnet.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 kann den- sealed
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_declaration * s deklariert:Indexers are declared using _indexer_declaration*s:
indexer_declaration
: attributes? indexer_modifier* indexer_declarator indexer_body
;
indexer_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'virtual'
| 'sealed'
| 'override'
| 'abstract'
| 'extern'
| indexer_modifier_unsafe
;
indexer_declarator
: type 'this' '[' formal_parameter_list ']'
| type interface_type '.' 'this' '[' formal_parameter_list ']'
;
indexer_body
: '{' accessor_declarations '}'
| '=>' expression ';'
;
Eine indexer_declaration kann einen Satz von Attributen (Attribute) und eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer), ( new
der neuen Modifizierer), virtual
(virtuelle Methoden), override
(Überschreibungs Methoden), sealed
(versiegelteMethoden), abstract
(abstrakte Methoden) und extern
(Externe Methoden)-Modifizierer enthalten.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 modifiziererer virtual
, 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
override
Modifizierer und 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-Schlüsselwort this
.Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this
. Bei einer expliziten Schnittstellenmember-Implementierung folgt dem- Typ ein INTERFACE_TYPE, ein " .
" 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.
Der formal_parameter_list der die Parameter des Indexers angibt.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
-und- out
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 im 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).
Eine indexer_body kann entweder aus einem -**Accessor Body* _ oder einem Ausdrucks Körper bestehen.An indexer_body may either consist of an accessor body _ or an _expression body*_. In einem Accessor-Text _accessor_declarations *, der in die {
Token "" und "" eingeschlossen werden muss }
, die Accessoren (Accessoren) der Eigenschaft.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 Anweisungs Text { get { return E; } }
und kann daher nur zum Angeben von nur-Getter-indexatoren verwendet werden, bei denen das Ergebnis des Getters durch einen einzelnen Ausdruck angegeben wird.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-oder-Argument zu übergeben ref
out
.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 (einfache Namen) oder über 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 astatic
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.Aget
accessor of a property corresponds to a method with no parameters, whereas aget
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 mitvalue
dem Namen, während ein-set
Accessor eines Indexers einer Methode mit derselben formalen Parameterliste wie der Indexer und einem zusätzlichen Parameter mit dem Namen entsprichtvalue
.Aset
accessor of a property corresponds to a method with a single parameter namedvalue
, whereas aset
accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter namedvalue
. - 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 mithilfe der-Syntax zugegriffen
base.P
, wobeiP
der Eigenschaftsname ist.In an overriding property declaration, the inherited property is accessed using the syntaxbase.P
, whereP
is the property name. In einer über schreibenden Indexer-Deklaration wird auf den geerbten Indexer mithilfe der-Syntax zugegriffenbase[E]
, wobeiE
eine durch Kommas getrennte Liste von Ausdrücken ist.In an overriding indexer declaration, the inherited indexer is accessed using the syntaxbase[E]
, whereE
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 Indexerdeklaration einen extern
Modifizierer enthält, wird der Indexer als *externer Indexer _ bezeichnet.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 entsprechende bool[]
(da jeder Wert des ersten-Werts nur ein Bit und nicht das zweite Byte) beansprucht, aber die gleichen Vorgänge wie eine zulässt bool[]
.An instance of the BitArray
class consumes substantially less memory than a corresponding bool[]
(since each value of the former occupies only one bit instead of the latter's one byte), but it permits the same operations as a bool[]
.
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 auf Elemente von BitArray
exakt der gleiche ist wie für ein bool[]
.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_declaration * s deklariert:Operators are declared using _operator_declaration*s:
operator_declaration
: attributes? operator_modifier+ operator_declarator operator_body
;
operator_modifier
: 'public'
| 'static'
| 'extern'
| operator_modifier_unsafe
;
operator_declarator
: unary_operator_declarator
| binary_operator_declarator
| conversion_operator_declarator
;
unary_operator_declarator
: type 'operator' overloadable_unary_operator '(' type identifier ')'
;
overloadable_unary_operator
: '+' | '-' | '!' | '~' | '++' | '--' | 'true' | 'false'
;
binary_operator_declarator
: type 'operator' overloadable_binary_operator '(' type identifier ',' type identifier ')'
;
overloadable_binary_operator
: '+' | '-' | '*' | '/' | '%' | '&' | '|' | '^' | '<<'
| right_shift | '==' | '!=' | '>' | '<' | '>=' | '<='
;
conversion_operator_declarator
: 'implicit' 'operator' type '(' type identifier ')'
| 'explicit' 'operator' type '(' type identifier ')'
;
operator_body
: block
| '=>' expression ';'
| ';'
;
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 einer _block *, die 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.
Für extern
Operatoren besteht die operator_body einfach aus einem Semikolon.For extern
operators, the operator_body consists simply of a semicolon. Für alle anderen Operatoren ist die 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 auch einen-static
Modifizierer enthalten.An operator declaration must include both apublic
and astatic
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 zum Angeben von-
ref
oder-out
Parametern.It is a compile-time error for an operator declaration to specifyref
orout
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, wobei T
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 oder annehmenT
T?
und kann jeden beliebigen Typ zurückgeben.A unary+
,-
,!
, or~
operator must take a single parameter of typeT
orT?
and can return any type. - Ein unärer
++
or--
-Operator muss einen einzelnen Parameter vom TypT
oder annehmenT?
, und er muss denselben Typ oder einen von ihm abgeleiteten Typ zurückgeben.A unary++
or--
operator must take a single parameter of typeT
orT?
and must return that same type or a type derived from it. - Ein unärer
true
orfalse
-Operator muss einen einzelnen Parameter vom TypT
oder annehmenT?
und muss den Typ zurückgebenbool
.A unarytrue
orfalse
operator must take a single parameter of typeT
orT?
and must return typebool
.
Die Signatur eines unären Operators besteht aus dem Operator Token ( +
, -
, !
, ~
, ++
, --
, true
oder 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
false
unären Operatoren und 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
false
Operatoren und 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 nachfolgende Verwendung von operator ++
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-undDekrementoperatorenNote 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 in C++ 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, wobei T
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 oder aufweisen muss
T
T?
und jeden beliebigen Typ zurückgeben kann.A binary non-shift operator must take two parameters, at least one of which must have typeT
orT?
, and can return any type. - Ein binärer
<<
or>>
-Operator muss zwei Parameter annehmen. der erste muss den-Typ aufweisen,T
T?
und der zweite muss den-Typint
oder aufweisenint?
und jeden beliebigen Typ zurückgeben.A binary<<
or>>
operator must take two parameters, the first of which must have typeT
orT?
and the second of which must have typeint
orint?
, and can return any type.
Die Signatur eines binären Operators besteht aus dem Operator Token ( +
, -
, *
, /
, %
, &
, |
, ^
, <<
, >>
, ==
, !=
, >
, <
, >=
oder <=
) und den Typen der beiden formalen Parameter.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 ==
undoperator !=
operator ==
andoperator !=
operator >
undoperator <
operator >
andoperator <
operator >=
undoperator <=
operator >=
andoperator <=
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, die das- implicit
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, die das- explicit
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.
Geben Sie für einen angegebenen Quelltyp S
und Zieltyp T
, wenn S
oder T
NULL-Werte zulassen, die S0
T0
zugrunde liegenden Typen an, und verweisen Sie andernfalls S0
T0
gleich S
T
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 deklarieren, T
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:
S0
undT0
sind unterschiedliche Typen.S0
andT0
are different types.- Entweder
S0
oderT0
ist der Klassen-oder Strukturtyp, in dem die Operator Deklaration stattfindet.EitherS0
orT0
is the class or struct type in which the operator declaration takes place. - Weder
S0
nochT0
ist eine INTERFACE_TYPE.NeitherS0
norT0
is an interface_type. - Ohne benutzerdefinierte Konvertierungen ist eine Konvertierung von
S
zuT
oder von zu nicht vorhandenT
S
.Excluding user-defined conversions, a conversion does not exist fromS
toT
or fromT
toS
.
Bei diesen Regeln werden alle Typparameter, die mit oder verknüpft sind, S
T
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 Indexer-Indexer, T
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 ist D<T>
.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-oder Strukturtyp C
eine Konvertierung von C
in int
und von int
in C
, aber nicht von in definiert int
bool
.For example, it is possible for a class or struct type C
to define a conversion from C
to int
and from int
to C
, but not from int
to bool
.
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 in zu konvertieren, object
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 angegeben wird T
, deklariert der zweite Operator eine Konvertierung, die bereits vorhanden ist (ein implizites und somit auch eine explizite Konvertierung von einem beliebigen Typ in den Typ object
).when type object
is specified as a type argument for T
, the second operator declares a conversion that already exists (an implicit, and therefore also an explicit, conversion exists from any type to type object
).
In 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 betrifft insbesondere:Specifically:
- Wenn eine vordefinierte implizite Konvertierung (implizite Konvertierungen) vom Typ
S
in den Typ vorhandenT
ist, werden alle benutzerdefinierten Konvertierungen (implizit oder explizit) vonS
zuT
ignoriert.If a pre-defined implicit conversion (Implicit conversions) exists from typeS
to typeT
, all user-defined conversions (implicit or explicit) fromS
toT
are ignored. - Wenn eine vordefinierte explizite Konvertierung (explizite Konvertierungen) vom Typ
S
in den Typ vorhandenT
ist, werden alle benutzerdefinierten expliziten Konvertierungen vonS
inT
ignoriert.If a pre-defined explicit conversion (Explicit conversions) exists from typeS
to typeT
, any user-defined explicit conversions fromS
toT
are ignored. Außerdem gilt Folgendes:Furthermore:
Wenn T
ein Schnittstellentyp ist, werden benutzerdefinierte implizite Konvertierungen von S
in T
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, jedoch verursachen object
die vom oben genannten Typ deklarierten Operatoren Convertible<T>
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. 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 object
den Typ verbergen 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_TYPE s konvertiert werden.User-defined conversions are not allowed to convert from or to interface_type s. Diese Einschränkung stellt insbesondere sicher, dass keine benutzerdefinierten Transformationen beim Konvertieren in eine INTERFACE_TYPE auftreten und dass eine Konvertierung in eine INTERFACE_TYPE nur erfolgreich ist, wenn das Objekt, das konvertiert wird, tatsächlich die angegebene INTERFACE_TYPE implementiert.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 von byte
in Digit
ist explizit, da Digit
nur eine Teilmenge der möglichen Werte eines darstellen kann 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_declaration * s deklariert:Instance constructors are declared using _constructor_declaration*s:
constructor_declaration
: attributes? constructor_modifier* constructor_declarator constructor_body
;
constructor_modifier
: 'public'
| 'protected'
| 'internal'
| 'private'
| 'extern'
| constructor_modifier_unsafe
;
constructor_declarator
: identifier '(' formal_parameter_list? ')' constructor_initializer?
;
constructor_initializer
: ':' 'base' '(' argument_list? ')'
| ':' 'this' '(' argument_list? ')'
;
constructor_body
: block
| ';'
;
Eine constructor_declaration kann einen Satz von Attributen (Attribute), eine gültige Kombination der vier Zugriffsmodifizierer (Zugriffsmodifizierer) und einen- extern
Modifizierer (externe Methode) 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 der 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 im formal_parameter_list eines Instanzkonstruktors verwiesen wird, muss mindestens der Zugriff auf den Konstruktor selbst (Barrierefreiheits Einschrä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 gibt einen anderen Instanzkonstruktor an, der vor dem Ausführen der Anweisungen in der 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 Konstruktor _ bezeichnet.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 die _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 die constructor_body aus einem- Block , der die Anweisungen angibt, um eine neue Instanz der-Klasse zu initialisieren.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_expression s (Objekt Erstellungs Ausdrücke) und durch constructor_initializer s aufgerufen.Instance constructors are invoked by object_creation_expression s (Object creation expressions) and through constructor_initializer s.
KonstruktorinitialisiererConstructor initializers
Alle Instanzkonstruktoren (außer den Klassen object
) enthalten implizit einen Aufruf eines anderen Instanzkonstruktors direkt vor dem constructor_body.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 vom constructor_initializer bestimmt:The constructor to implicitly invoke is determined by the constructor_initializer:
- Ein instanzkonstruktorinitialisierer des Formulars
base(argument_list)
oderbase()
bewirkt, dass ein Instanzkonstruktor von der direkten Basisklasse aufgerufen wird.An instance constructor initializer of the formbase(argument_list)
orbase()
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)
oderthis()
bewirkt, dass ein Instanzkonstruktor von der Klasse selbst aufgerufen wird.An instance constructor initializer of the formthis(argument-list)
orthis()
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 des Formulars base()
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, umfasst 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. 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, this
der in einem Argument Ausdruck des konstruktorinitialisierers referenziert werden kann, ebenso wie ein Kompilierzeitfehler für einen Argument Ausdruck, der auf ein Instanzmember durch eine Simple_name verweist.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 des Formulars verfügt base(...)
, führt dieser Konstruktor implizit die Initialisierungen aus, die von den variable_initializer s der Instanzfelder angegeben werden, die in der Klasse deklariert sind.When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...)
, that constructor implicitly performs the initializations specified by the variable_initializer s of the instance fields declared in its class. 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 zu erstellen B
, 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 ist jedoch y
0 (der Standardwert von), int
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 der constructor_body automatisch 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 Texts von T
T
oder direkt Instanzen von erstellen T
.When a class T
declares only private instance constructors, it is not possible for classes outside the program text of T
to derive from T
or to directly create instances of T
. 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. 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 zum Initialisieren eines geschlossenen Klassen Typs erforderlichen Aktionen implementiert.A *static constructor _ is a member that implements the actions required to initialize a closed class type. Statische Konstruktoren werden mit _static_constructor_declaration * s deklariert:Static constructors are declared using _static_constructor_declaration*s:
static_constructor_declaration
: attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body
;
static_constructor_modifiers
: 'extern'? 'static'
| 'static' 'extern'?
| static_constructor_modifiers_unsafe
;
static_constructor_body
: block
| ';'
;
Eine 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 der statische Konstruktor als *externer statischer Konstruktor _ bezeichnet.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 die _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 die 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 ausgeführt, bevor die- Main
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 A
statische Konstruktor der Ausführung durch den-aufzurufenden aufgerufen wird A.F
und die Ausführung des B
statischen Konstruktors durch den-Befehl ausgelöst wird B.F
.because 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 aus B.Y
, bevor der B
statische 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. Y
der Initialisierer bewirkt A
, dass der statische Konstruktor ausgeführt wird, da auf den Wert von A.X
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 aus und ruft dabei X
den Standardwert von ab, der 0 (null) Y
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.X
wird daher mit 1 initialisiert.A.X
is thus initialized to 1. Der Prozess der Ausführung der A
statischen Feldinitialisierer und des statischen Konstruktors wird dann abgeschlossen, wobei die Berechnung des Anfangs Werts von zurückgegeben Y
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_declaration * deklariert:A destructor is declared using a _destructor_declaration*:
destructor_declaration
: attributes? 'extern'? '~' identifier '(' ')' destructor_body
| destructor_declaration_unsafe
;
destructor_body
: block
| ';'
;
Eine 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 einen extern
Modifizierer enthält, wird der Dekonstruktor als *externer Dekonstruktor _ bezeichnet.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 die _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 die 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();
}
}
isis
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 virtuelle Methode Finalize
für überschrieben wird System.Object
.Destructors are implemented by overriding the virtual method Finalize
on System.Object
. C#-Programme dürfen diese Methode nicht überschreiben oder Sie direkt (oder überschreiben) (oder überschreiben).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-Methode hat die System.Object
- 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 Iterator bezeichnet.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 einer der Enumeratorschnittstellen (Enumeratorschnittstellen) oder einer 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_body auftreten, 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 beliebige- ref
oder- out
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-Schnittstelle System.Collections.Generic.IEnumerator<T>
.The enumerator interfaces are the non-generic interface System.Collections.IEnumerator
and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>
. Aus Gründen der Kürze werden diese Schnittstellen in diesem Kapitel als IEnumerator
bzw. referenziert IEnumerator<T>
.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 Schnittstelle System.Collections.IEnumerable
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
bzw. referenziert IEnumerable<T>
.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 oder zurückgibt,
IEnumerator
IEnumerable
istobject
.The yield type of an iterator that returnsIEnumerator
orIEnumerable
isobject
. - Der Yield-Typ eines Iterators, der oder zurückgibt,
IEnumerator<T>
IEnumerable<T>
istT
.The yield type of an iterator that returnsIEnumerator<T>
orIEnumerable<T>
isT
.
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-Methode des Enumeratorobjekts MoveNext
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
undIEnumerator<T>
, wobeiT
der Yield-Typ des Iterators ist.It implementsIEnumerator
andIEnumerator<T>
, whereT
is the yield type of the iterator. - Sie implementiert
System.IDisposable
.It implementsSystem.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 hat vier mögliche Zustände: vor _, ** wird ausgeführt _, angeh_ alten und _nach*, und befindet sich anfänglich im _ *before**-Zustand.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.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 Abschnitten wird das genaue Verhalten der MoveNext
Current
-,-und-Member Dispose
der IEnumerable
-und-Schnittstellen Implementierungen beschrieben, die IEnumerable<T>
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-Methode nicht unterstützen IEnumerator.Reset
.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 der- MoveNext
Methode wird Code im Iteratorblock ausgeführt, und die- Current
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 von ausgeführt MoveNext
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 vor ist, wird aufgerufen
MoveNext
:If the state of the enumerator object is before, invokingMoveNext
:- Ä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 (includingthis
) 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ührt wird, ist das Ergebnis des Aufrufs
MoveNext
nicht angegeben.If the state of the enumerator object is running, the result of invokingMoveNext
is unspecified. - Wenn der Status des Enumeratorobjekts angehalten wird, wird aufgerufen
MoveNext
:If the state of the enumerator object is suspended, invokingMoveNext
:- Ä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 theyield return
statement that caused the suspension of execution and continues until execution is interrupted (as described below).
- Wenn der Zustand des Enumeratorobjekts nach ist, wird der Aufruf von
MoveNext
zurückgegebenfalse
.If the state of the enumerator object is after, invokingMoveNext
returnsfalse
.
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 das Ende des Iteratorblocks und durch eine Ausnahme, die ausgelöst und aus dem Iteratorblock weitergegeben wird.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 ayield 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 theCurrent
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 dieseryield return
Anweisung.The values of all local variables and parameters (includingthis
) are saved, as is the location of thisyield return
statement. Wenn sich die-yield return
Anweisung innerhalb eines oder mehrerertry
Blöcke befindet, werden die zugeordnetenfinally
Blöcke zurzeit nicht ausgeführt.If theyield return
statement is within one or moretry
blocks, the associatedfinally
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 kehrt an ihren Aufrufer zurücktrue
und gibt an, dass die Iterationen erfolgreich auf den nächsten Wert erweitert wurden.TheMoveNext
method returnstrue
to its caller, indicating that the iteration successfully advanced to the next value.
- Der in der-Anweisung angegebene Ausdruck wird ausgewertet, implizit in den Yield-Typ konvertiert und der-
- Wenn eine-
yield break
Anweisung gefunden wird (die yield-Anweisung):When ayield break
statement is encountered (The yield statement):- Wenn die
yield break
Anweisung innerhalb eines oder mehrerertry
Blöcke liegt, werden die zugeordnetenfinally
Blöcke ausgeführt.If theyield break
statement is within one or moretry
blocks, the associatedfinally
blocks are executed. - Der Status des Enumeratorobjekts wird in nach geändert.The state of the enumerator object is changed to after.
- Die
MoveNext
Methode kehrt an ihren Aufrufer zurückfalse
und gibt an, dass die Iterations Funktion vollständig ist.TheMoveNext
method returnsfalse
to its caller, indicating that the iteration is complete.
- Wenn die
- Wenn das Ende des iteratortexts erreicht ist:When the end of the iterator body is encountered:
- Der Status des Enumeratorobjekts wird in nach geändert.The state of the enumerator object is changed to after.
- Die
MoveNext
Methode kehrt an ihren Aufrufer zurückfalse
und gibt an, dass die Iterations Funktion vollständig ist.TheMoveNext
method returnsfalse
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:
finally
Die entsprechenden Blöcke im iteratortext werden von der Ausnahme Weitergabe ausgeführt.Appropriatefinally
blocks in the iterator body will have been executed by the exception propagation.- Der Status des Enumeratorobjekts wird in nach geändert.The state of the enumerator object is changed to after.
- Die Ausnahme Weitergabe wird an den Aufrufer der-Methode weitergegeben
MoveNext
.The exception propagation continues to the caller of theMoveNext
method.
Die aktuelle EigenschaftThe Current property
Die-Eigenschaft eines Enumeratorobjekts wirkt Current
sich yield return
auf-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 der Wert von Current
der Wert, der durch den vorherigen-Befehl festgelegt wurde MoveNext
.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**" befindet, ist das Ergebnis des Zugriffs Current
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 als object
entspricht das Ergebnis des Zugriffs auf Current
die-Implementierung des Enumeratorobjekts dem IEnumerable
Zugriff Current
über die-Implementierung des Enumeratorobjekts IEnumerator<T>
und das 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 *vor _ ist, wird beim Aufrufen von
Dispose
der Zustand in _ after * geändert.If the state of the enumerator object is before _, invokingDispose
changes the state to _after**. - Wenn der Status des Enumeratorobjekts ausgeführt wird, ist das Ergebnis des Aufrufs
Dispose
nicht angegeben.If the state of the enumerator object is running, the result of invokingDispose
is unspecified. - Wenn der Status des Enumeratorobjekts angehalten wird, wird aufgerufen
Dispose
:If the state of the enumerator object is suspended, invokingDispose
:- Ändert den Status in wird ausgeführt.Changes the state to running.
- Führt beliebige letzte Blöcke aus, als wäre die letzte ausgeführte
yield return
Anweisung eine-yield break
Anweisung.Executes any finally blocks as if the last executedyield return
statement were ayield 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 Methode weitergegebenDispose
.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 theDispose
method. - Ändert den Zustand in nach.Changes the state to after.
- Wenn der Zustand des Enumeratorobjekts nach ist, hat der Aufruf von
Dispose
keine Auswirkungen.If the state of the enumerator object is after, invokingDispose
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-Methode des Aufzähl Bare-Objekts GetEnumerator
gibt ein Enumeratorobjekt zurück, das den im Iteratorblock angegebenen Code kapselt, und die Ausführung des Codes im Iteratorblock tritt auf, wenn die-Methode des Enumeratorobjekts MoveNext
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
undIEnumerable<T>
, wobeiT
der Yield-Typ des Iterators ist.It implementsIEnumerable
andIEnumerable<T>
, whereT
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 implementieren IEnumerator
IEnumerator<T>
, 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 GetEnumerator
wird das Aufzähl Bare Objekt selbst zurückgegeben, 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 GetEnumerator
geben, 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-Objekt stellt eine Implementierung der GetEnumerator
Methoden der IEnumerable
-Schnittstelle und der-Schnittstelle bereit IEnumerable<T>
.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.
Beispiel für die ImplementierungImplementation example
In diesem Abschnitt wird eine mögliche Implementierung von Iteratoren in Bezug auf Standard-c#-Konstrukte 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 implementiert die- GetEnumerator
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 vorhanden sein kann MoveNext
.Furthermore, the local variable i
is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext
.
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-Methode zum ersten Mal GetEnumerator
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 GetEnumerator
geben, 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
-Parameter und der- to
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 für from
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 aus, InvalidOperationException
Wenn Sie aufgerufen wird, wenn den Wert hat __state
0
.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 dass zuerst aufgerufen wird GetEnumerator
.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 implementiert die- GetEnumerator
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 in den foreach
-Anweisungen verwendet werden, werden in die __left
__right
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 mit einfachen Anweisungen zu schreiben foreach
.Note that it is not possible to write the translated code with simple foreach
statements.
Asynchrone FunktionenAsync functions
Eine Methode (Methoden) oder eine anonyme Funktion (Anonyme Funktions Ausdrücke) mit dem- async
Modifizierer wird als *Async-Funktion _ bezeichnet.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 alle Arten von Funktionen zu beschreiben, die den- async
Modifizierer haben.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, um beliebige- ref
oder- out
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 Tasktyp sein.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, die aus erstellt werden System.Threading.Tasks.Task<T>
.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 Task<T>
zeichnet ein Ergebnis des Typs auf T
.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 Erwartungs Ausdruck über einen -**Wiederaufnahme* Delegaten _ durchführt.Evaluation may later be resumed at the point of the suspending await expression by means of a *resumption delegate _. Der Wiederaufnahme Delegat ist vom Typ System.Action
, 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 _ Current-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-Funktion ist void
, unterscheidet sich die Auswertung auf folgende Weise von der obigen: da keine Aufgabe zurückgegeben wird, kommuniziert die Funktion stattdessen mit Abschluss und Ausnahmen mit dem Synchronisierungs Kontext des 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.