KlasyClasses

Klasa jest strukturą danych, która może zawierać składowe danych (stałe i pola), składowe funkcji (metody, właściwości, zdarzenia, indeksatory, operatory, konstruktory wystąpień, destruktory i konstruktory statyczne) i typy zagnieżdżone.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. Typy klas obsługują dziedziczenie, mechanizm, za pomocą którego Klasa pochodna może zwiększyć i specjalizację klasy bazowej.Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

Deklaracje klasClass declarations

Class_declaration jest type_declaration (deklaracje typu), które deklaruje nową klasę.A class_declaration is a type_declaration (Type declarations) that declares a new class.

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

Class_declaration składa się z opcjonalnego zestawu atrybutów (atrybutów), po którym następuje opcjonalny zestaw class_modifiers (Modyfikatory klas), po którym następuje opcjonalny modyfikator partial, po którym następuje class słowa kluczowego i Identyfikator , który nadaje nazwę klasy, a po nim opcjonalne type_parameter_list (parametry typu), a po nim opcjonalne specyfikacje class_base (Specyfikacja bazowa klasy), po których następuje przez opcjonalny zestaw type_parameter_constraints_clauses (ograniczenia parametru typu), po którym następuje class_body (Treść klasy), opcjonalnie następuje średnik.A class_declaration consists of an optional set of attributes (Attributes), followed by an optional set of class_modifiers (Class modifiers), followed by an optional partial modifier, followed by the keyword class and an identifier that names the class, followed by an optional type_parameter_list (Type parameters), followed by an optional class_base specification (Class base specification) , followed by an optional set of type_parameter_constraints_clauses (Type parameter constraints), followed by a class_body (Class body), optionally followed by a semicolon.

Deklaracja klasy nie może podawać type_parameter_constraints_clauses, chyba że zawiera również type_parameter_list.A class declaration cannot supply type_parameter_constraints_clauses unless it also supplies a type_parameter_list.

Deklaracja klasy dostarczająca type_parameter_list jest deklaracją klasy ogólnej.A class declaration that supplies a type_parameter_list is a generic class declaration. Ponadto każda klasa zagnieżdżona wewnątrz deklaracji klasy generycznej lub ogólnej deklaracji struktury jest sama deklaracją klasy ogólnej, ponieważ parametry typu dla typu zawierającego muszą zostać dostarczone, aby utworzyć typ skonstruowany.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.

Modyfikatory klasClass modifiers

Class_declaration może opcjonalnie zawierać sekwencję modyfikatorów klasy:A class_declaration may optionally include a sequence of class modifiers:

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

Jest to błąd czasu kompilacji dla tego samego modyfikatora do wyświetlenia wiele razy w deklaracji klasy.It is a compile-time error for the same modifier to appear multiple times in a class declaration.

Modyfikator new jest dozwolony dla zagnieżdżonych klas.The new modifier is permitted on nested classes. Określa, że Klasa ukrywa dziedziczonego elementu członkowskiego o tej samej nazwie, zgodnie z opisem w nowym modyfikatorze.It specifies that the class hides an inherited member by the same name, as described in The new modifier. Jest to błąd czasu kompilacji dla modyfikatora new, który ma być wyświetlany w deklaracji klasy, która nie jest deklaracją klasy zagnieżdżonej.It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

Modyfikatory public, protected, internali private kontrolują ułatwienia dostępu dla klasy.The public, protected, internal, and private modifiers control the accessibility of the class. W zależności od kontekstu, w którym występuje deklaracja klasy, niektóre z tych modyfikatorów mogą nie być dozwolone (zadeklarowane ułatwienia dostępu).Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

Modyfikatory abstract, sealed i static zostały omówione w poniższych sekcjach.The abstract, sealed and static modifiers are discussed in the following sections.

Klasy abstrakcyjneAbstract classes

Modyfikator abstract jest używany do wskazania, że Klasa jest niekompletna i że jest przeznaczona do użycia tylko jako klasa bazowa.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. Klasa abstrakcyjna różni się od klasy nieabstrakcyjnej w następujący sposób:An abstract class differs from a non-abstract class in the following ways:

  • Nie można bezpośrednio utworzyć wystąpienia klasy abstrakcyjnej i jest to błąd czasu kompilacji, aby użyć operatora new w klasie abstrakcyjnej.An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. Chociaż możliwe jest posiadanie zmiennych i wartości, których typy czasu kompilacji są abstrakcyjne, takie zmienne i wartości muszą być null lub zawierają odwołania do wystąpień klas nieabstrakcyjnych pochodzących od typów abstrakcyjnych.While it is possible to have variables and values whose compile-time types are abstract, such variables and values will necessarily either be null or contain references to instances of non-abstract classes derived from the abstract types.
  • Klasa abstrakcyjna jest dozwolona (ale nie jest wymagana), aby zawierała abstrakcyjne elementy członkowskie.An abstract class is permitted (but not required) to contain abstract members.
  • Klasa abstrakcyjna nie może być zapieczętowana.An abstract class cannot be sealed.

Gdy Klasa nieabstrakcyjna jest pochodną klasy abstrakcyjnej, Klasa nieabstrakcyjna musi zawierać rzeczywiste implementacje wszystkich dziedziczonych abstrakcyjnych elementów członkowskich, co zastępuje te abstrakcyjne elementy członkowskie.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. w przykładzieIn 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
    }
}

Klasa abstrakcyjna A wprowadza metodę abstrakcyjną F.the abstract class A introduces an abstract method F. Klasa B wprowadza dodatkową metodę G, ale ponieważ nie zapewnia implementacji F, B musi być zadeklarowana jako abstract.Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. Klasa C przesłania F i zapewnia rzeczywistą implementację.Class C overrides F and provides an actual implementation. Ponieważ w Cnie ma abstrakcyjnych elementów członkowskich, C jest dozwolony (ale nie jest wymagany) jako nieabstrakcyjny.Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

Zapieczętowane klasySealed classes

Modyfikator sealed służy do zapobiegania wyprowadzaniu z klasy.The sealed modifier is used to prevent derivation from a class. Błąd czasu kompilacji występuje, jeśli Klasa zapieczętowana została określona jako klasa bazowa innej klasy.A compile-time error occurs if a sealed class is specified as the base class of another class.

Klasa zapieczętowana nie może być również klasą abstrakcyjną.A sealed class cannot also be an abstract class.

Modyfikator sealed jest używany głównie do zapobiegania niezamierzonemu wyznaczeniu, ale również zapewnia pewne optymalizacje w czasie wykonywania.The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. W szczególności ze względu na to, że Klasa zapieczętowana nie ma żadnych klas pochodnych, istnieje możliwość przekształcenia wywołań elementów członkowskich funkcji wirtualnych w wystąpieniach klasy zapieczętowanej na wywołania niewirtualne.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.

Klasy statyczneStatic classes

Modyfikator static służy do oznaczania klasy zadeklarowanej jako Klasa statyczna.The static modifier is used to mark the class being declared as a static class. Nie można utworzyć wystąpienia klasy statycznej, nie można jej użyć jako typu i może zawierać tylko statyczne elementy członkowskie.A static class cannot be instantiated, cannot be used as a type and can contain only static members. Tylko Klasa statyczna może zawierać deklaracje metod rozszerzających (metody rozszerzające).Only a static class can contain declarations of extension methods (Extension methods).

Deklaracja klasy statycznej podlega następującym ograniczeniom:A static class declaration is subject to the following restrictions:

  • Klasa statyczna nie może zawierać modyfikatora sealed ani abstract.A static class may not include a sealed or abstract modifier. Należy jednak pamiętać, że ponieważ od klasy statycznej nie można utworzyć wystąpienia ani pochodnej, zachowuje się tak, jakby było zapieczętowane i abstrakcyjne.Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • Klasa statyczna nie może zawierać specyfikacji class_base (specyfikacji klasy podstawowej) i nie może jawnie określić klasy bazowej lub listy zaimplementowanych interfejsów.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. Klasa statyczna niejawnie dziedziczy po typie object.A static class implicitly inherits from type object.
  • Klasa statyczna może zawierać tylko statyczne elementy członkowskie (statyczne i składowe wystąpienia).A static class can only contain static members (Static and instance members). Należy zauważyć, że stałe i zagnieżdżone typy są klasyfikowane jako statyczne elementy członkowskie.Note that constants and nested types are classified as static members.
  • Klasa statyczna nie może mieć elementów członkowskich z zazadeklarowaną dostępnością protected lub protected internal.A static class cannot have members with protected or protected internal declared accessibility.

Jest to błąd czasu kompilacji, który narusza którekolwiek z tych ograniczeń.It is a compile-time error to violate any of these restrictions.

Klasa statyczna nie ma konstruktorów wystąpień.A static class has no instance constructors. Nie można zadeklarować konstruktora wystąpienia w klasie statycznej i nie podano domyślnego konstruktora wystąpień (konstruktorów domyślnych) dla klasy statycznej.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.

Elementy członkowskie klasy statycznej nie są automatycznie statyczne i deklaracje składowych muszą jawnie zawierać modyfikator static (z wyjątkiem stałych i zagnieżdżonych typów).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). Gdy Klasa jest zagnieżdżona w obrębie statycznej klasy zewnętrznej, Klasa zagnieżdżona nie jest klasą statyczną, chyba że jawnie zawiera modyfikator static.When a class is nested within a static outer class, the nested class is not a static class unless it explicitly includes a static modifier.

Odwołujące się do typów klas statycznychReferencing static class types

Namespace_or_type_name (przestrzeń nazw i nazwy typów) mogą odwoływać się do klasy statycznej, jeśliA namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • Namespace_or_type_name jest T w namespace_or_type_name T.Iformularz, lubThe namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • Namespace_or_type_name jest T w typeof_expression (Argument Lists1) typeof(T)formularza.The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

Primary_expression (Członkowie funkcji) mogą odwoływać się do klasy statycznej, jeśliA primary_expression (Function members) is permitted to reference a static class if

W każdym innym kontekście jest to błąd czasu kompilacji, który odwołuje się do klasy statycznej.In any other context it is a compile-time error to reference a static class. Na przykład jest to błąd dla klasy statycznej, która ma być używana jako klasa bazowa, typ składnika (typy zagnieżdżone) elementu członkowskiego, argument typu ogólnego lub ograniczenie parametru typu.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. Podobnie Klasa statyczna nie może być używana w typie tablicy, typ wskaźnika, wyrażenie new, wyrażenie rzutowania, wyrażenie is, wyrażenie as, wyrażenie sizeof lub wyrażenie wartości domyślnej.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.

Modyfikator częściowyPartial modifier

Modyfikator partial jest używany do wskazania, że ta class_declaration jest deklaracją typu częściowego.The partial modifier is used to indicate that this class_declaration is a partial type declaration. Wiele deklaracji typu częściowego o tej samej nazwie w otaczającej przestrzeni nazw lub deklaracji typu Połącz, aby utworzyć jedną deklarację typu, zgodnie z regułami określonymi w typach częściowych.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.

Posiadanie deklaracji klasy rozproszonej za pomocą oddzielnych segmentów tekstu programu może być przydatne, jeśli te segmenty są produkowane lub utrzymywane w różnych kontekstach.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. Na przykład jedna część deklaracji klasy może być wygenerowana maszyną, podczas gdy druga zostaje ręcznie utworzona.For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. Oddzielenie tekstu między nimi uniemożliwia aktualizację przez jeden z powodu konfliktu z aktualizacjami.Textual separation of the two prevents updates by one from conflicting with updates by the other.

Parametry typuType parameters

Parametr typu jest prostym identyfikatorem, który wskazuje symbol zastępczy argumentu typu dostarczonego do utworzenia konstruowanego typu.A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. Parametr typu jest formalnym symbolem zastępczym dla typu, który zostanie dostarczony później.A type parameter is a formal placeholder for a type that will be supplied later. Z kolei argument typu (argumenty typu) jest rzeczywistym typem, który jest zastępowany dla parametru typu podczas tworzenia konstruowanego typu.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
    ;

Każdy parametr typu w deklaracji klasy definiuje nazwę w obszarze deklaracji (deklaracji) tej klasy.Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. Dlatego nie może mieć takiej samej nazwy jak inny parametr typu lub element członkowski zadeklarowany w tej klasie.Thus, it cannot have the same name as another type parameter or a member declared in that class. Parametr typu nie może mieć takiej samej nazwy jak sam typ.A type parameter cannot have the same name as the type itself.

Specyfikacja bazowa klasyClass base specification

Deklaracja klasy może zawierać specyfikację class_base , która definiuje bezpośrednią klasę bazową klasy i interfejsy (interfejsy) bezpośrednio zaimplementowane przez klasę.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)*
    ;

Klasa bazowa określona w deklaracji klasy może być skonstruowanym typem klasy (skonstruowane typy).The base class specified in a class declaration can be a constructed class type (Constructed types). Klasa bazowa nie może być parametrem typu, ale może zawierać parametry typu, które znajdują się w zakresie.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

Klas podstawowychBase classes

Gdy class_type jest uwzględniona w class_base, określa bezpośrednią klasę bazową zadeklarowanej klasy.When a class_type is included in the class_base, it specifies the direct base class of the class being declared. Jeśli deklaracja klasy nie ma class_baselub class_base wyświetla tylko typy interfejsów, przyjmuje się, że bezpośrednia klasa bazowa jest 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. Klasa dziedziczy składowe z bezpośredniej klasy podstawowej, zgodnie z opisem w dziedziczeniu.A class inherits members from its direct base class, as described in Inheritance.

w przykładzieIn the example

class A {}

class B: A {}

Klasa A ma być bezpośrednią klasą bazową B, a B jest określana jako pochodna z A.class A is said to be the direct base class of B, and B is said to be derived from A. Ponieważ A nie określa jawnie bezpośredniej klasy bazowej, jej bezpośrednia klasa bazowa jest niejawnie object.Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

W przypadku konstruowanego typu klasy, jeśli klasa bazowa jest określona w deklaracji klasy generycznej, Klasa bazowa typu konstruowanego jest uzyskiwana przez podstawianie dla każdego type_parameter w deklaracji klasy bazowej, odpowiadającego type_argument typu złożonego.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. Podaną deklaracje klas ogólnychGiven the generic class declarations

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

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

Klasa bazowa złożonego typu G<int> zostałaby B<string,int[]>.the base class of the constructed type G<int> would be B<string,int[]>.

Bezpośrednia klasa bazowa typu klasy musi być co najmniej równa dostępności jako samego typu klasy (domeny dostępności).The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). Na przykład jest to błąd czasu kompilowania dla klasy public, która dziedziczy z klasy private lub internal.For example, it is a compile-time error for a public class to derive from a private or internal class.

Bezpośrednia klasa bazowa typu klasy nie może być żadnym z następujących typów: System.Array, System.Delegate, System.MulticastDelegate, System.Enumani 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. Ponadto deklaracja klasy generycznej nie może używać System.Attribute jako bezpośredniej lub pośredniej klasy bazowej.Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

Podczas określania znaczenia bezpośredniej specyfikacji klasy bazowej A klasy B, przyjmuje się, że bezpośrednia klasa bazowa B jest tymczasowo 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. Intuicyjnie gwarantuje to, że znaczenie specyfikacji klasy bazowej nie może rekursywnie zależeć od siebie.Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. Przykład:The example:

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

class C : A<C.B> {}

Wystąpił błąd, ponieważ w specyfikacji klasy bazowej A<C.B> bezpośrednia klasa bazowa C jest uznawana za object, a tym samym (zgodnie z regułami przestrzeni nazw i nazw typów) C nie jest uważana za element członkowski 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.

Klasy bazowe typu klasy są bezpośrednią klasą bazową i jej klasami podstawowymi.The base classes of a class type are the direct base class and its base classes. Innymi słowy, zestaw klas bazowych jest przechodnim zamknięciem bezpośredniej relacji między klasami podstawowymi.In other words, the set of base classes is the transitive closure of the direct base class relationship. W odniesieniu do powyższego przykładu klasy bazowe BA i object.Referring to the example above, the base classes of B are A and object. w przykładzieIn the example

class A {...}

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

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

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

klasy bazowe D<int>C<int[]>, B<IComparable<int[]>>, Ai object.the base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

Oprócz klasy objectkażdy typ klasy ma dokładnie jedną bezpośrednią klasę bazową.Except for class object, every class type has exactly one direct base class. Klasa object nie ma bezpośredniej klasy podstawowej i jest ostateczną klasą bazową wszystkich innych klas.The object class has no direct base class and is the ultimate base class of all other classes.

Gdy Klasa B dziedziczy z klasy A, jest to błąd czasu kompilacji dla A zależnie od B.When a class B derives from a class A, it is a compile-time error for A to depend on B. Klasa bezpośrednio zależy od jej bezpośredniej klasy podstawowej (jeśli istnieje) i bezpośrednio zależy od klasy, w której jest od razu zagnieżdżona (jeśli istnieje).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). W związku z tą definicją kompletny zestaw klas, od których zależy Klasa, jest bezpośrednie i przechodnie zamknięcie bezpośredniego zależy od relacji.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.

PrzykładThe example

class A: A {}

jest błędem, ponieważ Klasa zależy od siebie samej.is erroneous because the class depends on itself. Podobnie, przykładLikewise, the example

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

Wystąpił błąd, ponieważ klasy cyklicznie zależą od siebie.is in error because the classes circularly depend on themselves. Na koniec przykładFinally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

powoduje błąd czasu kompilacji, ponieważ A zależy od B.C (jego bezpośrednia klasa bazowa), która zależy od B (bezpośrednio otaczającej klasy), która cyklicznie zależy od 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.

Należy zauważyć, że Klasa nie zależy od klas, które są w niej zagnieżdżone.Note that a class does not depend on the classes that are nested within it. w przykładzieIn the example

class A
{
    class B: A {}
}

B zależy od A (ponieważ A jest zarówno bezpośrednią klasą bazową, jak i bezpośrednio otaczającą ją klasą), ale A nie zależy od B (ponieważ B nie jest klasą bazową ani otaczającą klasę 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). W tym przypadku przykład jest prawidłowy.Thus, the example is valid.

Nie można dziedziczyć z klasy sealed.It is not possible to derive from a sealed class. w przykładzieIn the example

sealed class A {}

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

Wystąpił błąd klasy B, ponieważ próbuje dziedziczyć z klasy sealed A.class B is in error because it attempts to derive from the sealed class A.

Implementacje interfejsuInterface implementations

Specyfikacja class_base może zawierać listę typów interfejsów, w takim przypadku Klasa jest określana do bezpośredniego zaimplementowania danego typu interfejsu.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. Implementacje interfejsów omówiono dokładniej w implementacjach interfejsu.Interface implementations are discussed further in Interface implementations.

Ograniczenia parametru typuType parameter constraints

Deklaracje typu ogólnego i metody mogą opcjonalnie określać ograniczenia parametrów typu przez uwzględnienie type_parameter_constraints_clauses.Generic type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clauses.

type_parameter_constraints_clause
    : 'where' type_parameter ':' type_parameter_constraints
    ;

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

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

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

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

Każdy type_parameter_constraints_clause składa się z wheretokena, po którym następuje nazwa parametru typu, po którym następuje dwukropek i lista ograniczeń dla tego parametru typu.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. Dla każdego parametru typu może istnieć co najwyżej jedna klauzula where, a klauzule where mogą być wyświetlane w dowolnej kolejności.There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Podobnie jak tokeny get i set w metodzie dostępu do właściwości, token where nie jest słowem kluczowym.Like the get and set tokens in a property accessor, the where token is not a keyword.

Lista ograniczeń podanych w klauzuli where może zawierać dowolny z następujących składników, w następującej kolejności: jedno ograniczenie podstawowe, jedno lub więcej ograniczeń pomocniczych i ograniczenie konstruktora, 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().

Ograniczenie podstawowe może być typem klasy lub ograniczeniem typu referencyjnego class lub structograniczenia typu wartości .A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. Ograniczenie pomocnicze może być type_parameter lub INTERFACE_TYPE.A secondary constraint can be a type_parameter or interface_type.

Ograniczenie typu odwołania określa, że argument typu użyty dla parametru typu musi być typem referencyjnym.The reference type constraint specifies that a type argument used for the type parameter must be a reference type. Wszystkie typy klas, typy interfejsów, typy delegatów, typy tablic i parametry typu znane jako typ referencyjny (zgodnie z definicją poniżej) spełniają to ograniczenie.All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

Ograniczenie typu wartości określa, że argument typu użyty dla parametru typu musi być typem wartości niedopuszczający wartości null.The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. Wszystkie typy struktur niedopuszczające wartości null, typy wyliczeniowe i parametry typu mające ograniczenie typu wartości spełniają to ograniczenie.All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Należy zauważyć, że chociaż sklasyfikowane jako typ wartości, typ dopuszczający wartość null (Typy dopuszczające wartości null) nie spełnia ograniczenia typu wartości.Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. Parametr typu z ograniczeniem typu wartości nie może mieć również constructor_constraint.A type parameter having the value type constraint cannot also have the constructor_constraint.

Typy wskaźników nigdy nie mogą być argumentami typu i nie są uważane za spełniające ograniczenia typu odwołania lub typu wartości.Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

Jeśli ograniczenie jest typem klasy, typem interfejsu lub parametrem typu, ten typ określa minimalny "typ podstawowy", który każdy argument typu użyty dla tego parametru typu musi obsługiwać.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. Za każdym razem, gdy jest używany typ skonstruowany lub metoda ogólna, argument typu jest sprawdzany względem ograniczeń w parametrze typu w czasie kompilacji.Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. Dostarczony argument typu musi spełniać warunki opisane w temacie spełnianie ograniczeń.The type argument supplied must satisfy the conditions described in Satisfying constraints.

Ograniczenie class_type musi spełniać następujące reguły:A class_type constraint must satisfy the following rules:

  • Typ musi być typem klasy.The type must be a class type.
  • Typ nie może być sealed.The type must not be sealed.
  • Typ nie może być jednym z następujących typów: System.Array, System.Delegate, System.Enumlub System.ValueType.The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • Typ nie może być object.The type must not be object. Ponieważ wszystkie typy pochodzą od object, takie ograniczenie nie będzie miało wpływu, jeśli było dozwolone.Because all types derive from object, such a constraint would have no effect if it were permitted.
  • Co najwyżej jedno ograniczenie dla danego parametru typu może być typem klasy.At most one constraint for a given type parameter can be a class type.

Typ określony jako ograniczenie INTERFACE_TYPE musi spełniać następujące reguły:A type specified as an interface_type constraint must satisfy the following rules:

  • Typ musi być typem interfejsu.The type must be an interface type.
  • Typ nie może być określony więcej niż raz w danej klauzuli where.A type must not be specified more than once in a given where clause.

W obu przypadkach ograniczenie może dotyczyć dowolnego z parametrów typu skojarzonej deklaracji typu lub metody w ramach typu złożonego i może dotyczyć zadeklarowanego typu.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.

Każdy typ klasy lub interfejsu określony jako ograniczenie parametru typu musi być co najmniej jako dostępny (ograniczenia dostępu) jako zadeklarowany typ ogólny lub metoda.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.

Typ określony jako ograniczenie type_parameter musi spełniać następujące reguły:A type specified as a type_parameter constraint must satisfy the following rules:

  • Typ musi być parametrem typu.The type must be a type parameter.
  • Typ nie może być określony więcej niż raz w danej klauzuli where.A type must not be specified more than once in a given where clause.

Ponadto nie może istnieć żadne cykle w grafie zależności parametrów typu, gdzie zależność jest relacją przechodnią zdefiniowaną przez:In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

  • Jeśli parametr typu T jest używany jako ograniczenie dla parametru typu S, S zależy od T.If a type parameter T is used as a constraint for type parameter S then S depends on T.
  • Jeśli parametr typu S zależy od parametru typu T i T zależy od parametru typu U, S zależy od U.If a type parameter S depends on a type parameter T and T depends on a type parameter U then S depends on U.

Ta relacja jest błędem czasu kompilowania dla parametru typu, aby zależała od samego siebie (bezpośrednio lub pośrednio).Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Wszystkie ograniczenia muszą być spójne między zależnymi parametrami typu.Any constraints must be consistent among dependent type parameters. Jeśli parametr typu S zależy od parametru typu T, wówczas:If type parameter S depends on type parameter T then:

  • T nie może mieć ograniczenia typu wartości.T must not have the value type constraint. W przeciwnym razie T jest skutecznie zapieczętowany, dlatego S zostanie wymuszone tego samego typu co T, eliminując konieczność stosowania dwóch parametrów typu.Otherwise, T is effectively sealed so S would be forced to be the same type as T, eliminating the need for two type parameters.
  • Jeśli S ma ograniczenie typu wartości, T nie może mieć ograniczenia class_type .If S has the value type constraint then T must not have a class_type constraint.
  • Jeśli S ma ograniczenie class_type A i T ma ograniczenie class_type B, należy wykonać konwersję tożsamości lub niejawną konwersję odwołań z A na B lub niejawną konwersję odwołań z B do A.If S has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.
  • Jeśli S również zależy od parametru typu U, a U ma ograniczenie class_type A i T ma ograniczenie class_type B, należy przeprowadzić konwersję tożsamości lub niejawną konwersję odwołań z A na B lub niejawną konwersję odwołania z B do A.If S also depends on type parameter U and U has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

Jest on prawidłowy dla S, aby miał ograniczenie typu wartości i T mieć ograniczenia typu odwołania.It is valid for S to have the value type constraint and T to have the reference type constraint. Efektywnie te limity T do typów System.Object, System.ValueType, System.Enumi dowolnego typu interfejsu.Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

Jeśli klauzula where parametru typu zawiera ograniczenie konstruktora (które ma postać new()), można użyć operatora new, aby utworzyć wystąpienia typu (wyrażenia tworzenia obiektów).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). Dowolny argument typu użyty dla parametru typu z ograniczeniem konstruktora musi mieć publiczny Konstruktor bez parametrów (ten Konstruktor niejawnie istnieje dla dowolnego typu wartości) lub być parametrem typu, który ma ograniczenie typu wartości lub ograniczenie konstruktora (zobacz ograniczenia parametru typu , aby uzyskać szczegółowe informacje).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).

Poniżej przedstawiono przykłady ograniczeń: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()
{
    ...
}

Poniższy przykład dotyczy błędu, ponieważ powoduje cykliczność w grafie zależności parametrów typu: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
{
    ...
}

W poniższych przykładach przedstawiono dodatkowe nieprawidłowe sytuacje: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
{
    ...
}

Obowiązująca Klasa bazowa parametru typu T jest zdefiniowana w następujący sposób:The effective base class of a type parameter T is defined as follows:

  • Jeśli T nie ma ograniczeń podstawowych ani ograniczeń parametrów typu, jego skuteczna Klasa bazowa jest object.If T has no primary constraints or type parameter constraints, its effective base class is object.
  • Jeśli T ma ograniczenie typu wartości, jego obowiązująca Klasa bazowa jest System.ValueType.If T has the value type constraint, its effective base class is System.ValueType.
  • Jeśli T ma ograniczenie class_type C ale nie ma żadnych ograniczeń type_parameter , jego obowiązująca Klasa bazowa jest C.If T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • Jeśli T nie ma ograniczenia class_type , ale ma co najmniej jedno ograniczenie type_parameter , jego skuteczna Klasa bazowa to najbardziej z nich typ (podniesione operatory konwersji) w zestawie skutecznych klas podstawowych ograniczeń type_parameter .If T has no class_type constraint but has one or more type_parameter constraints, its effective base class is the most encompassed type (Lifted conversion operators) in the set of effective base classes of its type_parameter constraints. Reguły spójności zapewniają, że ten typ jest najbardziej odnoszący się do tego typu.The consistency rules ensure that such a most encompassed type exists.
  • Jeśli T ma ograniczenie class_type i co najmniej jeden type_parameter ograniczenia, jego obowiązująca Klasa bazowa to najbardziej z nich typ (zniesione operatory konwersji) w zestawie składający się z ograniczenia class_type T i obowiązujących klas podstawowych jego ograniczeń type_parameter .If T has both a class_type constraint and one or more type_parameter constraints, its effective base class is the most encompassed type (Lifted conversion operators) in the set consisting of the class_type constraint of T and the effective base classes of its type_parameter constraints. Reguły spójności zapewniają, że ten typ jest najbardziej odnoszący się do tego typu.The consistency rules ensure that such a most encompassed type exists.
  • Jeśli T ma ograniczenie typu odwołania, ale nie ma żadnych ograniczeń class_type , jego obowiązująca Klasa bazowa jest object.If T has the reference type constraint but no class_type constraints, its effective base class is object.

Na potrzeby tych reguł, jeśli T ma ograniczenie V, które jest value_type, Użyj zamiast tego najbardziej konkretnego typu podstawowego V, który jest class_type.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. Może to nigdy potrwać jawnie określone ograniczenie, ale może wystąpić, gdy ograniczenia metody generycznej są niejawnie dziedziczone przez zastępowanie deklaracji metody lub jawną implementację metody interfejsu.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.

Te reguły zapewniają, że obowiązująca Klasa bazowa jest zawsze class_type.These rules ensure that the effective base class is always a class_type.

Efektywny zestaw interfejsów parametru typu T jest zdefiniowany w następujący sposób:The effective interface set of a type parameter T is defined as follows:

  • Jeśli T nie ma secondary_constraints, jego skuteczny zestaw interfejsów jest pusty.If T has no secondary_constraints, its effective interface set is empty.
  • Jeśli T ma ograniczenia INTERFACE_TYPE , ale nie ograniczenia type_parameter , jego obowiązujący zestaw interfejsów jest jego zestawem ograniczeń INTERFACE_TYPE .If T has interface_type constraints but no type_parameter constraints, its effective interface set is its set of interface_type constraints.
  • Jeśli T nie ma ograniczeń INTERFACE_TYPE , ale ma ograniczenia type_parameter , jego skuteczny zestaw interfejsów jest złożeniem efektywnych zestawów interfejsów jego ograniczeń type_parameter .If T has no interface_type constraints but has type_parameter constraints, its effective interface set is the union of the effective interface sets of its type_parameter constraints.
  • Jeśli T ma zarówno ograniczenia INTERFACE_TYPE , jak i ograniczenia type_parameter , jego skuteczny zestaw interfejsów jest Unią zestawu ograniczeń INTERFACE_TYPE i obowiązującymi zestawami interfejsów jego ograniczeń type_parameter .If T has both interface_type constraints and type_parameter constraints, its effective interface set is the union of its set of interface_type constraints and the effective interface sets of its type_parameter constraints.

Parametr typu jest znany jako typ referencyjny , jeśli ma ograniczenie typu odwołania lub jego obowiązująca Klasa bazowa nie jest object ani 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.

Wartości typu parametru typu ograniczonego mogą służyć do uzyskiwania dostępu do elementów członkowskich wystąpienia IMPLIKOWANYCH przez ograniczenia.Values of a constrained type parameter type can be used to access the instance members implied by the constraints. w przykładzieIn the example

interface IPrintable
{
    void Print();
}

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

Metody IPrintable mogą być wywoływane bezpośrednio na x, ponieważ T jest ograniczone do zawsze implementowania IPrintable.the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Treść klasyClass body

Class_body klasy definiuje elementy członkowskie tej klasy.The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

Typy częściowePartial types

Deklarację typu można podzielić na wiele deklaracji typu częściowego.A type declaration can be split across multiple partial type declarations. Deklaracja typu jest zbudowana z jego części, zgodnie z regułami w tej sekcji, whereupon jest traktowany jako jedna deklaracja w trakcie pozostałej części czasu kompilacji i przetwarzania w czasie wykonywania programu.The type declaration is constructed from its parts by following the rules in this section, whereupon it is treated as a single declaration during the remainder of the compile-time and run-time processing of the program.

Class_declaration, struct_declaration lub interface_declaration reprezentuje deklarację typu częściowego, jeśli zawiera modyfikator partial.A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partial nie jest słowem kluczowym i działa tylko jako modyfikator, jeśli występuje bezpośrednio przed jednym ze słów kluczowych class, struct lub interface w deklaracji typu lub przed typem void w deklaracji metody.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. W innych kontekstach można używać ich jako normalnego identyfikatora.In other contexts it can be used as a normal identifier.

Każda część deklaracji typu częściowego musi zawierać modyfikator partial.Each part of a partial type declaration must include a partial modifier. Musi mieć taką samą nazwę i być zadeklarowane w tej samej przestrzeni nazw lub deklaracji typu co pozostałe części.It must have the same name and be declared in the same namespace or type declaration as the other parts. Modyfikator partial wskazuje, że dodatkowe części deklaracji typu mogą istnieć w innym miejscu, ale istnienie takich dodatkowych części nie jest wymagane; jest ona prawidłowa dla typu z pojedynczą deklaracją, aby uwzględnić modyfikator partial.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.

Wszystkie części typu częściowego muszą zostać skompilowane w taki sposób, aby części mogły być scalane w czasie kompilacji w jednej deklaracji typu.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. Typy częściowe nie zezwalają na rozszerzone już skompilowane typy.Partial types specifically do not allow already compiled types to be extended.

Zagnieżdżone typy można zadeklarować w wielu częściach przy użyciu modyfikatora partial.Nested types may be declared in multiple parts by using the partial modifier. Zazwyczaj typ zawierający jest zadeklarowany przy użyciu partial, a każda część typu zagnieżdżonego jest zadeklarowana w innej części zawierającego go typu.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.

Modyfikator partial nie jest dozwolony w deklaracjach delegata ani wyliczeniowych.The partial modifier is not permitted on delegate or enum declarations.

AtrybutyAttributes

Atrybuty typu częściowego są określane przez połączenie, w nieokreślonej kolejności, atrybutów każdej części.The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. Jeśli atrybut jest umieszczany w wielu częściach, jest równoważne do określenia atrybutu wielokrotnie w typie.If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. Na przykład dwie części:For example, the two parts:

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

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

są równoważne deklaracji, takiej jak:are equivalent to a declaration such as:

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

Atrybuty parametrów typu łączą się w podobny sposób.Attributes on type parameters combine in a similar fashion.

ModyfikatoryModifiers

Gdy deklaracja typu częściowego zawiera specyfikację dostępności (Modyfikatory public, protected, internali private), musi on wyrazić zgodę na wszystkie inne części, które zawierają specyfikację dostępności.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. Jeśli żadna część typu częściowego nie zawiera specyfikacji dostępności, typ ma odpowiednią domyślną dostępność (zadeklarowany dostęp).If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

Jeśli co najmniej jedna deklaracja częściowa typu zagnieżdżonego zawiera modyfikator new, żadne ostrzeżenie nie zostanie zgłoszone, jeśli typ zagnieżdżony ukrywa dziedziczonego elementu członkowskiego (ukrywając przez dziedziczenie).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).

Jeśli co najmniej jedna deklaracja częściowa klasy zawiera modyfikator abstract, Klasa jest uznawana za abstrakcyjną (klasy abstrakcyjne).If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). W przeciwnym razie Klasa jest uznawana za nieabstrakcyjną.Otherwise, the class is considered non-abstract.

Jeśli co najmniej jedna deklaracja częściowa klasy zawiera modyfikator sealed, Klasa jest uznawana za zapieczętowany (klasy zapieczętowane).If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). W przeciwnym razie Klasa jest uznawana za niezapieczętowany.Otherwise, the class is considered unsealed.

Należy zauważyć, że Klasa nie może być abstrakcyjna i zapieczętowana.Note that a class cannot be both abstract and sealed.

Gdy modyfikator unsafe jest używany w deklaracji typu częściowego, tylko ta część jest traktowana jako niebezpieczny kontekst (niebezpieczne konteksty).When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (Unsafe contexts).

Parametry i ograniczenia typuType parameters and constraints

Jeśli typ ogólny jest zadeklarowany w wielu częściach, każda część musi określać parametry typu.If a generic type is declared in multiple parts, each part must state the type parameters. Każda część musi mieć taką samą liczbę parametrów typu i taką samą nazwę dla każdego parametru typu, w pożądanej kolejności.Each part must have the same number of type parameters, and the same name for each type parameter, in order.

Gdy częściowa deklaracja typu ogólnego zawiera ograniczenia (klauzulewhere), ograniczenia muszą zgadzać się ze wszystkimi innymi częściami, które zawierają ograniczenia.When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. W odniesieniu do każdej części, która zawiera ograniczenia muszą mieć ograniczenia dotyczące tego samego zestawu parametrów typu, a dla każdego parametru typu zestawy podstawowe, pomocnicze i konstruktory muszą być równoważne.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. Dwa zestawy ograniczeń są równoważne, jeśli zawierają te same elementy członkowskie.Two sets of constraints are equivalent if they contain the same members. Jeśli żadna część częściowego typu ogólnego określa ograniczenia parametru typu, parametry typu są uznawane za nieograniczone.If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

PrzykładThe 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>
{
    ...
}

jest poprawna, ponieważ te części, które zawierają ograniczenia (pierwsze dwa) skutecznie określają ten sam zestaw ograniczeń podstawowych, pomocniczych i konstruktorów dla tego samego zestawu parametrów typu.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.

Klasa bazowaBase class

Gdy deklaracja klasy częściowej zawiera specyfikację klasy bazowej, musi zgadzać się ze wszystkimi innymi częściami, które zawierają specyfikację klasy bazowej.When a partial class declaration includes a base class specification it must agree with all other parts that include a base class specification. Jeśli żadna część klasy częściowej nie zawiera specyfikacji klasy bazowej, Klasa bazowa stają się System.Object (klasy bazowe).If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

Interfejsy podstawoweBase interfaces

Zestaw interfejsów podstawowych dla typu zadeklarowanego w wielu częściach jest złożeniem interfejsów podstawowych określonych w każdej części.The set of base interfaces for a type declared in multiple parts is the union of the base interfaces specified on each part. Określony interfejs podstawowy może być nazwany tylko raz w każdej części, ale jest dozwolony dla wielu części, aby nazwać te same interfejsy podstawowe.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). Może istnieć tylko jedna implementacja elementów członkowskich dowolnego interfejsu podstawowego.There must only be one implementation of the members of any given base interface.

w przykładzieIn the example

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

partial class C: IC {...}

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

zestaw interfejsów podstawowych dla klasy C jest IA, IBi IC.the set of base interfaces for class C is IA, IB, and IC.

Zazwyczaj każda część zawiera implementację interfejsów zadeklarowanych w tej części; nie jest to jednak wymagane.Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. Część może zapewnić implementację interfejsu zadeklarowanego w innej części: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
{
    ...
}

MembersMembers

Z wyjątkiem metod częściowych (metod częściowych) zestaw elementów członkowskich typu zadeklarowanych w wielu częściach jest po prostu Unią zestawu elementów członkowskich zadeklarowanych w każdej części.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. Ciała wszystkich części deklaracji typu współdzielą ten sam obszar deklaracji (deklaracje), a zakres każdego elementu członkowskiego (zakresów) rozciąga się na treść wszystkich części.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. Domena dostępności dowolnego elementu członkowskiego zawsze zawiera wszystkie części typu otaczającego; członek private zadeklarowany w jednej części jest swobodnie dostępny z innej części.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. Jest to błąd czasu kompilacji, który deklaruje ten sam element członkowski w więcej niż jednej części typu, chyba że ten element członkowski jest typem z modyfikatorem partial.It is a compile-time error to declare the same member in more than one part of the type, unless that member is a type with the partial modifier.

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

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

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

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

Kolejność elementów członkowskich w ramach typu jest rzadko istotna dla C# kodu, ale może być istotna w przypadku współdziałania z innymi językami i środowiskami.The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. W takich przypadkach kolejność elementów członkowskich w typie zadeklarowanym w wielu częściach jest niezdefiniowana.In these cases, the ordering of members within a type declared in multiple parts is undefined.

Metody częściowePartial methods

Metody częściowe można definiować w jednej części deklaracji typu i zaimplementowane w innej.Partial methods can be defined in one part of a type declaration and implemented in another. Implementacja jest opcjonalna; Jeśli żadna część nie implementuje metody częściowej, deklaracja częściowej metody i wszystkie wywołania do niej są usuwane z deklaracji typu, która wynika z kombinacji części.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.

Metody częściowe nie mogą definiować modyfikatorów dostępu, ale są niejawnie private.Partial methods cannot define access modifiers, but are implicitly private. Typem zwracanym musi być void, a ich parametry nie mogą mieć modyfikatora out.Their return type must be void, and their parameters cannot have the out modifier. Identyfikator partial jest rozpoznawany jako specjalne słowo kluczowe w deklaracji metody tylko wtedy, gdy jest wyświetlany bezpośrednio przed typem void; w przeciwnym razie można go użyć jako normalnego identyfikatora.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. Metoda częściowa nie może jawnie implementować metod interfejsu.A partial method cannot explicitly implement interface methods.

Istnieją dwa rodzaje deklaracji metody częściowej: Jeśli treść deklaracji metody jest średnikiem, deklaracja jest określana jako definicje metody częściowej.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. Jeśli treść jest przyznany jako blok, deklaracja jest określana jako implementująca Deklaracja metody częściowej.If the body is given as a block, the declaration is said to be an implementing partial method declaration. Dla części deklaracji typu może istnieć tylko jedna definiująca deklarację metody częściowej z danym podpisem, a może istnieć tylko jedna implementująca Deklaracja metody częściowej z danym podpisem.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. W przypadku podanej deklaracji metody częściowej należy określić odpowiadającą jej definicje metodę częściową, a deklaracje muszą być zgodne z określonymi w następujących przypadkach: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:

  • Deklaracje muszą mieć takie same Modyfikatory (choć niekoniecznie w tej samej kolejności), nazwę metody, liczbę parametrów typu i liczbę parametrów.The declarations must have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters.
  • Odpowiednie parametry w deklaracjach muszą mieć takie same Modyfikatory (choć nie muszą być w tej samej kolejności) i te same typy (różnice modulo w nazwach parametrów typu).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).
  • Odpowiednie parametry typu w deklaracjach muszą mieć takie same ograniczenia (różnice modulo w nazwach parametrów typu).Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

Implementacja deklaracji metody częściowej może pojawić się w tej samej części co odpowiadająca jej definiująca Deklaracja metody częściowej.An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

Tylko Definiowanie metody częściowej uczestniczy w rozpoznaniu przeciążenia.Only a defining partial method participates in overload resolution. W tym przypadku, niezależnie od tego, czy jest określona deklaracja implementująca, wyrażenia wywołania mogą zostać rozwiązane z wywołaniami metody częściowej.Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Ponieważ metoda częściowa zawsze zwraca void, takie wyrażenia wywołania zawsze będą instrukcją wyrażenia.Because a partial method always returns void, such invocation expressions will always be expression statements. Ponadto ponieważ metoda częściowa jest niejawnie private, takie instrukcje są zawsze wykonywane w jednej z części deklaracji typu, w ramach której zadeklarowano metodę częściową.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.

Jeśli żadna część deklaracji typu częściowego zawiera deklarację implementującą dla danej metody częściowej, każda instrukcja wyrażenia wywołująca ją jest po prostu usunięta z złożonej deklaracji typu.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. W efekcie wyrażenie wywołania, łącznie ze wszystkimi wyrażeniami składowymi, nie ma wpływu na czas wykonywania.Thus the invocation expression, including any constituent expressions, has no effect at run-time. Sama metoda częściowa jest również usuwana i nie będzie składową deklaracji typu połączonego.The partial method itself is also removed and will not be a member of the combined type declaration.

Jeśli istnieje deklaracja implementująca dla danej metody częściowej, wywołania metod częściowych są zachowywane.If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. Metoda częściowa daje podstawę deklaracji metody podobnej do implementującej deklaracji metody częściowej z wyjątkiem następujących:The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • Modyfikator partial nie jest uwzględnionyThe partial modifier is not included
  • Atrybuty w deklaracji metody otrzymanej są połączonymi atrybutami definicji i implementowania deklaracji metody częściowej w nieokreślonej kolejności.The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. Duplikaty nie są usuwane.Duplicates are not removed.
  • Atrybuty w parametrach deklaracji metody będącej wynikiem są połączone atrybuty odpowiednich parametrów definiujących i implementujących deklaracji metody częściowej w nieokreślonej kolejności.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. Duplikaty nie są usuwane.Duplicates are not removed.

Jeśli zdefiniowanie deklaracji, ale nie deklaracji implementującej, jest podano dla metody częściowej M, obowiązują następujące ograniczenia:If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

Metody częściowe są przydatne do umożliwienia jednej części deklaracji typu, aby dostosować zachowanie innej części, np., która jest generowana przez narzędzie.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. Rozważmy następującą deklarację klasy częściowej: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();
}

Jeśli ta klasa jest skompilowana bez żadnych innych części, definicje deklaracji metody częściowej i ich wywołania zostaną usunięte, a wynikiem połączonej deklaracji klasy będzie odpowiednik następujących elementów: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; }
    }
}

Załóżmy, że inna część została określona, Jednakże, która zawiera deklaracje implementacji metod częściowych: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);
    }
}

Następnie wynikiem połączonej deklaracji klasy będzie odpowiednik następujących: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);
    }
}

Powiązanie nazwName binding

Chociaż każda część rozszerzalnego typu musi być zadeklarowana w obrębie tego samego obszaru nazw, części są zwykle zapisywane w ramach różnych deklaracji przestrzeni nazw.Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. W ten sposób różne dyrektywy using (dyrektywy using) mogą być obecne dla każdej części.Thus, different using directives (Using directives) may be present for each part. Podczas interpretacji prostych nazw (wnioskowanie o typie) w jednej części należy wziąć pod uwagę tylko dyrektywy using deklaracji przestrzeni nazw otaczającej tę część.When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. Może to spowodować, że ten sam identyfikator ma różne znaczenie w różnych częściach: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
    }
}

Elementy członkowskie klasyClass members

Elementy członkowskie klasy składają się z elementów członkowskich wprowadzonych przez class_member_declarations i członków dziedziczonych z bezpośredniej klasy podstawowej.The members of a class consist of the members introduced by its class_member_declarations and the members inherited from the direct base class.

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

Elementy członkowskie typu klasy są podzielone na następujące kategorie:The members of a class type are divided into the following categories:

  • Stałe, które reprezentują wartości stałe skojarzone z klasą (stałe).Constants, which represent constant values associated with the class (Constants).
  • Pola, które są zmiennymi klasy (pól).Fields, which are the variables of the class (Fields).
  • Metody, które implementują obliczenia i akcje, które mogą być wykonywane przez klasę (metody).Methods, which implement the computations and actions that can be performed by the class (Methods).
  • Właściwości, które definiują nazwane cechy i akcje związane z odczytem i pisaniem tych cech (Właściwości).Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • Zdarzenia, które definiują powiadomienia, które mogą być generowane przez klasę (zdarzenia).Events, which define notifications that can be generated by the class (Events).
  • Indeksatory, które zezwalają na indeksowanie wystąpień klasy w taki sam sposób (syntaktycznie) jak tablice (indeksatory).Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • Operatory definiujące operatory wyrażeń, które mogą być stosowane do wystąpień klasy (Operatory).Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • Konstruktory wystąpień, które implementują akcje wymagane do zainicjowania wystąpień klasy (konstruktorów wystąpień)Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • Destruktory, które implementują akcje do wykonania przed wystąpieniem klasy, zostaną trwale odrzucone (destruktory).Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • Konstruktory statyczne, które implementują akcje wymagane do zainicjowania samej klasy (konstruktory statyczne).Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • Typy, które reprezentują typy, które są lokalne dla klasy (typy zagnieżdżone).Types, which represent the types that are local to the class (Nested types).

Elementy członkowskie, które mogą zawierać kod wykonywalny, są określane zbiorczo jako elementy członkowskie typu klasy.Members that can contain executable code are collectively known as the function members of the class type. Składowymi funkcji typu klasy są metody, właściwości, zdarzenia, indeksatory, operatory, konstruktory wystąpień, destruktory i konstruktory statyczne tego typu klasy.The function members of a class type are the methods, properties, events, indexers, operators, instance constructors, destructors, and static constructors of that class type.

Class_declaration tworzy nowe miejsce deklaracji (deklaracje), a class_member_declarations bezpośrednio zawarte w class_declaration wprowadzić nowych członków do tego obszaru deklaracji.A class_declaration creates a new declaration space (Declarations), and the class_member_declarations immediately contained by the class_declaration introduce new members into this declaration space. Do class_member_declarations mają zastosowanie następujące reguły:The following rules apply to class_member_declarations:

  • Konstruktory wystąpień, destruktory i konstruktory statyczne muszą mieć taką samą nazwę jak bezpośrednio otaczającą klasę.Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. Wszystkie inne elementy członkowskie muszą mieć nazwy, które różnią się od nazwy bezpośrednio otaczającej klasy.All other members must have names that differ from the name of the immediately enclosing class.
  • Nazwa stałej, pola, właściwości, zdarzenia lub typu musi różnić się od nazw wszystkich innych elementów członkowskich zadeklarowanych w tej samej klasie.The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • Nazwa metody musi różnić się od nazw wszystkich innych metod, które nie są zadeklarowane w tej samej klasie.The name of a method must differ from the names of all other non-methods declared in the same class. Ponadto sygnatura (sygnatury i Przeciążenie) metody musi różnić się od sygnatur wszystkich innych metod zadeklarowanych w tej samej klasie, a dwie metody zadeklarowane w tej samej klasie mogą nie mieć sygnatur, które różnią się wyłącznie ref i out.In addition, the signature (Signatures and overloading) of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.
  • Sygnatura konstruktora wystąpienia musi różnić się od sygnatur wszystkich innych konstruktorów wystąpień zadeklarowanych w tej samej klasie, a dwa konstruktory zadeklarowane w tej samej klasie mogą nie mieć sygnatur, które różnią się wyłącznie ref i 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 by ref and out.
  • Sygnatura indeksatora musi różnić się od sygnatur wszystkich innych indeksatorów zadeklarowanych w tej samej klasie.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • Sygnatura operatora musi różnić się od sygnatur wszystkich innych operatorów zadeklarowanych w tej samej klasie.The signature of an operator must differ from the signatures of all other operators declared in the same class.

Dziedziczone elementy członkowskie typu klasy (dziedziczenie) nie są częścią obszaru deklaracji klasy.The inherited members of a class type (Inheritance) are not part of the declaration space of a class. W efekcie Klasa pochodna może zadeklarować element członkowski o tej samej nazwie lub podpisie jako dziedziczonego elementu członkowskiego (co w efekcie powoduje ukrycie dziedziczonego elementu członkowskiego).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).

Typ wystąpieniaThe instance type

Każda deklaracja klasy ma skojarzony typ powiązania (powiązane i niepowiązane typy), Typ wystąpienia.Each class declaration has an associated bound type (Bound and unbound types), the instance type. Dla deklaracji klasy generycznej typ wystąpienia jest tworzony przez utworzenie typu złożonego (konstruowane typy) z deklaracji typu, z każdym z podanych argumentów typu, które są odpowiednim parametrem typu.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. Ponieważ typ wystąpienia używa parametrów typu, może być używany tylko wtedy, gdy parametry typu znajdują się w zakresie; oznacza to, że wewnątrz deklaracji klasy.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. Typ wystąpienia jest typem this dla kodu zapisaną wewnątrz deklaracji klasy.The instance type is the type of this for code written inside the class declaration. Dla klas nieogólnych typ wystąpienia jest po prostu zadeklarowaną klasą.For non-generic classes, the instance type is simply the declared class. Poniżej przedstawiono kilka deklaracji klasy wraz z ich typami wystąpień: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

Elementy członkowskie skonstruowanych typówMembers of constructed types

Niedziedziczone elementy członkowskie złożonego typu są uzyskiwane przez podstawianie, dla każdego type_parameter w deklaracji elementu członkowskiego, odpowiadającego type_argument typu złożonego.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. Proces podstawiania jest oparty na semantycznym znaczeniu deklaracji typu i nie jest po prostu podstawianie tekstu.The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

Na przykład, dana deklaracja klasy generycznejFor 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) {...}
}

skonstruowany typ Gen<int[],IComparable<string>> ma następujących członków: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) {...}

Typ elementu członkowskiego a w deklaracji klasy generycznej Gen to "Dwuwymiarowa tablica T", więc typ elementu członkowskiego a w skonstruowanym typie powyżej to "Dwuwymiarowa tablica Jednowymiarowa tablicy int" lub 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[,][].

W ramach elementów członkowskich funkcji wystąpienia typ this jest typem wystąpienia (typem wystąpienia) zawierającej deklaracji.Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

Wszystkie elementy członkowskie klasy generycznej mogą używać parametrów typu z dowolnej klasy otaczającej, bezpośrednio lub jako część typu złożonego.All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. Gdy określony zamknięty typ skonstruowany (otwarty i zamknięty) jest używany w czasie wykonywania, każde użycie parametru typu jest zastępowane rzeczywistym argumentem typu dostarczonym do typu złożonego.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. Na przykład: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
    }
}

DziedziczenieInheritance

Klasa dziedziczy elementy członkowskie jego bezpośredniego typu klasy podstawowej.A class inherits the members of its direct base class type. Dziedziczenie oznacza, że Klasa niejawnie zawiera wszystkie elementy członkowskie typu bezpośredniej klasy podstawowej, z wyjątkiem konstruktorów wystąpień, destruktorów i konstruktorów statycznych klasy bazowej.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. Oto kilka ważnych aspektów dziedziczenia:Some important aspects of inheritance are:

  • Dziedziczenie jest przechodnie.Inheritance is transitive. Jeśli C pochodzi od B, a B pochodzi od A, wówczas C dziedziczy członków zadeklarowanych w B, jak również członków zadeklarowanych w A.If C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A.
  • Klasa pochodna rozszerza swoją bezpośrednią klasę bazową.A derived class extends its direct base class. Klasa pochodna może dodawać nowych członków do tych, które dziedziczy, ale nie może usunąć definicji dziedziczonego elementu członkowskiego.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • Konstruktory wystąpień, destruktory i konstruktory statyczne nie są dziedziczone, ale wszystkie inne elementy członkowskie są, niezależnie od ich zadeklarowanej dostępności (dostęp do elementów członkowskich).Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). Jednak w zależności od ich zadeklarowanej dostępności dziedziczone elementy członkowskie mogą nie być dostępne w klasie pochodnej.However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • Klasa pochodna może ukryć (ukrywając przez dziedziczenie) dziedziczone elementy członkowskie przez zadeklarowanie nowych członków o tej samej nazwie lub podpisie.A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. Należy jednak zauważyć, że ukrycie dziedziczonego elementu członkowskiego nie powoduje usunięcia tego elementu członkowskiego — jedynie sprawia, że ten element członkowski jest niedostępny bezpośrednio za pomocą klasy pochodnej.Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • Wystąpienie klasy zawiera zestaw wszystkich pól wystąpienia zadeklarowanych w klasie i jej klasy bazowej, a niejawna konwersja (niejawne konwersje odwołań) istnieje z klasy pochodnej do dowolnego typu klasy bazowej.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. W tym celu odwołanie do wystąpienia jakiejś klasy pochodnej może być traktowane jako odwołanie do wystąpienia którejkolwiek z jego klas podstawowych.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.
  • Klasa może deklarować metody wirtualne, właściwości i indeksatory, a klasy pochodne mogą przesłaniać implementację tych elementów członkowskich funkcji.A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. Dzięki temu klasy mogą mieć zachowanie polimorficzne, w którym akcje wykonywane przez wywołanie elementu członkowskiego funkcji różnią się w zależności od typu czasu wykonywania wystąpienia, za pomocą którego wywoływany jest element członkowski funkcji.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.

Dziedziczone składowe typu konstruowanej klasy są elementami członkowskimi bezpośredniego typu klasy podstawowej (klasy bazowe), które można znaleźć przez podstawianie argumentów typu konstruowanego typu dla każdego wystąpienia odpowiednich parametrów typu w specyfikacji class_base .The inherited member of a constructed class type are the members of the immediate base class type (Base classes), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the class_base specification. Te elementy członkowskie z kolei są przekształcane przez podstawianie dla każdego type_parameter w deklaracji elementu członkowskiego, odpowiadającego type_argument specyfikacji class_base .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) {...}
}

W powyższym przykładzie, skonstruowany typ D<int> ma niedziedziczonej składowej public int G(string s) uzyskany przez podstawianie argumentu typu int dla parametru typu 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> ma także Dziedziczony element członkowski z deklaracji klasy B.D<int> also has an inherited member from the class declaration B. Ten Dziedziczony element członkowski jest określany przez najpierw określenie typu klasy bazowej B<int[]> D<int> przez podstawianie int dla T w specyfikacji klasy podstawowej 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[]>. Następnie jako argument typu do B, int[] jest zastępowany dla U w public U F(long index), co powoduje zwrócenie dziedziczonego elementu członkowskiego 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).

Nowy modyfikatorThe new modifier

Class_member_declaration może zadeklarować składową o tej samej nazwie lub podpisie jako dziedziczonego elementu członkowskiego.A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. W takim przypadku element członkowski klasy pochodnej jest określany jako ukryty element członkowski klasy bazowej.When this occurs, the derived class member is said to hide the base class member. Ukrycie dziedziczonego elementu członkowskiego nie jest uważane za błąd, ale powoduje, że kompilator wystawia ostrzeżenie.Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. Aby pominąć ostrzeżenie, Deklaracja elementu członkowskiego klasy pochodnej może zawierać modyfikator new, aby wskazać, że pochodna składowa jest przeznaczona do ukrycia podstawowego elementu członkowskiego.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. Ten temat został omówiony w dalszej części w ukrywaniu przez dziedziczenie.This topic is discussed further in Hiding through inheritance.

Jeśli modyfikator new jest zawarty w deklaracji, która nie ukrywa dziedziczonego elementu członkowskiego, zostanie wygenerowane ostrzeżenie.If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. To ostrzeżenie jest pomijane przez usunięcie modyfikatora new.This warning is suppressed by removing the new modifier.

Modyfikatory dostępuAccess modifiers

Class_member_declaration może mieć jeden z pięciu możliwych rodzajów zadeklarowanych dostępności (deklarowane ułatwienia dostępu): public, protected internal, protected, internallub 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. Z wyjątkiem kombinacji protected internal, jest to błąd czasu kompilacji, aby określić więcej niż jeden modyfikator dostępu.Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. Gdy class_member_declaration nie zawiera żadnych modyfikatorów dostępu, przyjmowana jest private.When a class_member_declaration does not include any access modifiers, private is assumed.

Typy składnikówConstituent types

Typy, które są używane w deklaracji elementu członkowskiego, są nazywane typami elementów tego elementu członkowskiego.Types that are used in the declaration of a member are called the constituent types of that member. Możliwe typy składników to typ stałej, pola, właściwości, zdarzenia lub indeksatora, zwracany typ metody lub operatora oraz typy parametrów metody, indeksatora, operatora lub konstruktora wystąpienia.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. Typy składników składowych muszą być co najmniej tak samo dostępne, jak sam element członkowski (ograniczenia dotyczące ułatwień dostępu).The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

Elementy członkowskie statyczne i wystąpieniaStatic and instance members

Elementy członkowskie klasy są statycznymi elementami członkowskimi lub wystąpieniami.Members of a class are either static members or instance members. Ogólnie mówiąc, warto traktować statyczne elementy członkowskie jako należące do typów klas i elementów członkowskich wystąpienia jako należące do obiektów (wystąpień typów klas).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).

Gdy deklaracja pola, metody, właściwości, zdarzenia, operatora lub konstruktora zawiera modyfikator static, deklaruje statyczny element członkowski.When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. Ponadto deklaracja stałej lub typu niejawnie deklaruje statyczny element członkowski.In addition, a constant or type declaration implicitly declares a static member. Statyczne składowe mają następującą charakterystykę:Static members have the following characteristics:

  • Gdy statyczny element członkowski M jest przywoływany w member_access (dostęp do elementu członkowskiego) formularza E.M, E musi zwrócić uwagę na typ zawierający M.When a static member M is referenced in a member_access (Member access) of the form E.M, E must denote a type containing M. Jest to błąd czasu kompilacji dla E, aby zauważyć wystąpienie.It is a compile-time error for E to denote an instance.
  • Pole statyczne określa dokładnie jedną lokalizację magazynu, która ma być współużytkowana przez wszystkie wystąpienia danego typu zamkniętej klasy.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. Bez względu na to, ile wystąpień danego typu zamkniętej klasy są tworzone, istnieje tylko jedna kopia pola statycznego.No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.
  • Statyczny element członkowski funkcji (metoda, właściwość, zdarzenie, operator lub Konstruktor) nie działa w konkretnym wystąpieniu i jest to błąd czasu kompilacji, aby odwołać się do this w takiej składowej funkcji.A static function member (method, property, event, operator, or constructor) does not operate on a specific instance, and it is a compile-time error to refer to this in such a function member.

Gdy deklaracja pola, metody, właściwości, zdarzenia, indeksatora, konstruktora lub destruktora nie zawiera modyfikatora static, deklaruje element członkowski wystąpienia.When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (Składowa wystąpienia jest czasami nazywana niestatyczną składową). Elementy członkowskie wystąpienia mają następującą charakterystykę:(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • Gdy element członkowski wystąpienia M jest przywoływany w member_access (dostęp do elementu członkowskiego) formularza E.M, E musi zwrócić uwagę na wystąpienie typu zawierającego M.When an instance member M is referenced in a member_access (Member access) of the form E.M, E must denote an instance of a type containing M. Jest to błąd czasu powiązania dla E, aby zauważyć typ.It is a binding-time error for E to denote a type.
  • Każde wystąpienie klasy zawiera oddzielny zestaw wszystkich pól wystąpienia klasy.Every instance of a class contains a separate set of all instance fields of the class.
  • Element członkowski funkcji wystąpienia (metoda, właściwość, indeksator, Konstruktor wystąpienia lub destruktor) działa w danym wystąpieniu klasy i można uzyskać do niego dostęp jako this (ten dostęp).An instance function member (method, property, indexer, instance constructor, or destructor) operates on a given instance of the class, and this instance can be accessed as this (This access).

Poniższy przykład ilustruje reguły uzyskiwania dostępu do statycznych i składowych wystąpień: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
    }
}

Metoda F pokazuje, że w elemencie członkowskim funkcji wystąpienia simple_name (proste nazwy) mogą być używane do uzyskiwania dostępu do elementów członkowskich wystąpienia i statycznych elementów członkowskich.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. Metoda G pokazuje, że w składowej funkcji statycznej jest to błąd czasu kompilacji, aby uzyskać dostęp do elementu członkowskiego wystąpienia za pomocą simple_name.The G method shows that in a static function member, it is a compile-time error to access an instance member through a simple_name. Metoda Main pokazuje, że w member_access (dostęp do elementów członkowskich) należy uzyskać dostęp do składowych wystąpień za pomocą wystąpień, a statyczne elementy członkowskie muszą być dostępne za pomocą typów.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.

Typy zagnieżdżoneNested types

Typ zadeklarowany w deklaracji klasy lub struktury jest nazywany typem zagnieżdżonym.A type declared within a class or struct declaration is called a nested type. Typ, który jest zadeklarowany w ramach jednostki kompilacji lub przestrzeni nazw, jest nazywany typem niezagnieżdżonym.A type that is declared within a compilation unit or namespace is called a non-nested type.

w przykładzieIn the example

using System;

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

Klasa B jest typem zagnieżdżonym, ponieważ jest zadeklarowana w klasie A, a Klasa A jest typem niezagnieżdżonym, ponieważ jest zadeklarowana w ramach jednostki kompilacji.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.

W pełni kwalifikowana nazwaFully qualified name

W pełni kwalifikowana nazwa (wpełni kwalifikowananazwa) dla typu zagnieżdżonego jest S.N gdzie S jest w pełni kwalifikowaną nazwą typu, w którym jest zadeklarowany N typu.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.

Zadeklarowane ułatwienia dostępuDeclared accessibility

Typy niezagnieżdżone mogą mieć public lub internal zadeklarowane ułatwienia dostępu i mieć domyślnie internal zadeklarowane ułatwienia dostępu.Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. Typy zagnieżdżone mogą mieć te formy zadeklarowanej dostępności, a także co najmniej jedną dodatkową postać zadeklarowanej dostępności, w zależności od tego, czy typ zawierający jest klasą czy strukturą: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:

  • Typ zagnieżdżony zadeklarowany w klasie może mieć jedną z pięciu postaci zadeklarowanej dostępności (public, protected internal, protected, internallub private) i, podobnie jak inne elementy członkowskie klasy, domyślnie private zadeklarowane ułatwienia dostępu.A nested type that is declared in a class can have any of five forms of declared accessibility (public, protected internal, protected, internal, or private) and, like other class members, defaults to private declared accessibility.
  • Zagnieżdżony typ, który jest zadeklarowany w strukturze, może mieć jedną z trzech postaci zadeklarowanej dostępności (public, internallub private) i, podobnie jak w przypadku innych elementów członkowskich struktury, domyślnie private zadeklarowane ułatwienia dostępu.A nested type that is declared in a struct can have any of three forms of declared accessibility (public, internal, or private) and, like other struct members, defaults to private declared accessibility.

PrzykładThe 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 {...} }
}

deklaruje prywatną Nodeklasy zagnieżdżonej.declares a private nested class Node.

UkryjHiding

Zagnieżdżony typ może ukrywać (ukrywając nazwę) podstawową składową.A nested type may hide (Name hiding) a base member. Modyfikator new jest dozwolony w deklaracjach typu zagnieżdżonego, dzięki czemu ukrywanie może być wyrażone jawnie.The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. PrzykładThe 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();
    }
}

pokazuje zagnieżdżoną klasę M, która ukrywa M metody zdefiniowanej w Base.shows a nested class M that hides the method M defined in Base.

Ten dostępthis access

Typ zagnieżdżony i jego typ zawierający nie mają specjalnej relacji w odniesieniu do this_access (ten dostęp).A nested type and its containing type do not have a special relationship with regard to this_access (This access). W celu odwoływania się do elementów członkowskich wystąpienia typu zawierającego nie można używać w tym celu this w ramach typu zagnieżdżonego.Specifically, this within a nested type cannot be used to refer to instance members of the containing type. W przypadkach, gdy Typ zagnieżdżony wymaga dostępu do elementów członkowskich wystąpienia typu zawierającego, można uzyskać dostęp, dostarczając this dla wystąpienia typu zawierającego jako argument konstruktora dla typu zagnieżdżonego.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. Poniższy przykładThe 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();
    }
}

pokazuje tę technikę.shows this technique. Wystąpienie C tworzy wystąpienie Nested i przekazuje własne this do konstruktora Nested, aby zapewnić kolejny dostęp do elementów członkowskich wystąpienia C.An instance of C creates an instance of Nested and passes its own this to Nested's constructor in order to provide subsequent access to C's instance members.

Dostęp do prywatnych i chronionych składowych typu zawierającegoAccess to private and protected members of the containing type

Typ zagnieżdżony ma dostęp do wszystkich elementów członkowskich, które są dostępne dla tego typu zawierającego, w tym elementów członkowskich typu zawierającego private i protected zadeklarowane ułatwienia dostępu.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. PrzykładThe 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();
    }
}

pokazuje klasę C, która zawiera Nestedklasy zagnieżdżonej.shows a class C that contains a nested class Nested. W NestedMetoda G wywołuje metodę statyczną F zdefiniowaną w Ci F ma prywatnie zadeklarowane ułatwienia dostępu.Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

Typ zagnieżdżony może również uzyskiwać dostęp do chronionych elementów członkowskich zdefiniowanych w typie podstawowym typu zawierającego.A nested type also may access protected members defined in a base type of its containing type. w przykładzieIn 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();
    }
}

Klasa zagnieżdżona Derived.Nested uzyskuje dostęp do chronionej metody F zdefiniowanej w klasie podstawowej Derived``Baseprzez wywołanie przez wystąpienie 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.

Zagnieżdżone typy w klasach ogólnychNested types in generic classes

Deklaracja klasy generycznej może zawierać deklaracje typu zagnieżdżonego.A generic class declaration can contain nested type declarations. Parametry typu otaczającej klasy mogą być używane w zagnieżdżonych typach.The type parameters of the enclosing class can be used within the nested types. Deklaracja typu zagnieżdżonego może zawierać dodatkowe parametry typu, które mają zastosowanie tylko do typu zagnieżdżonego.A nested type declaration can contain additional type parameters that apply only to the nested type.

Każda deklaracja typu znajdująca się w deklaracji klasy generycznej jest niejawnie deklaracją typu ogólnego.Every type declaration contained within a generic class declaration is implicitly a generic type declaration. Podczas pisania odwołania do typu zagnieżdżonego w typie ogólnym, zawierający typ skonstruowany, w tym jego argumenty typu, musi mieć nazwę.When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. Jednakże z poziomu klasy zewnętrznej można używać typu zagnieżdżonego bez kwalifikacji; Typ wystąpienia klasy zewnętrznej może być niejawnie używany podczas konstruowania typu zagnieżdżonego.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. Poniższy przykład pokazuje trzy różne prawidłowe sposoby odwoływania się do złożonego typu utworzonego na podstawie Inner; pierwsze dwa są równoważne: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
    }
}

Chociaż jest to zły styl programowania, parametr typu w typie zagnieżdżonym może ukryć element członkowski lub parametr typu zadeklarowany w typie zewnętrznym: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
    }
}

Zastrzeżone nazwy składowychReserved member names

Aby ułatwić bazowa C# implementacji w czasie wykonywania, dla każdej źródłowej deklaracji składowej, która jest właściwością, zdarzeniem lub indeksatorem, implementacja musi zarezerwować dwa podpisy metod na podstawie rodzaju deklaracji elementu członkowskiego, jego nazwy i typu.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. Jest to błąd czasu kompilacji dla programu w celu zadeklarowania elementu członkowskiego, którego sygnatura pasuje do jednego z tych zarezerwowanych podpisów, nawet jeśli bazowa implementacja czasu wykonywania nie używa tych rezerwacji.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.

Nazwy zastrzeżone nie wprowadzają deklaracji, dlatego nie uczestniczą w wyszukiwaniu elementów członkowskich.The reserved names do not introduce declarations, thus they do not participate in member lookup. Jednak skojarzone podpisy metod zarezerwowanych deklaracji, które uczestniczą w dziedziczeniu (dziedziczenie) i mogą być ukrywane za pomocą modyfikatora new (nowy modyfikator).However, a declaration's associated reserved method signatures do participate in inheritance (Inheritance), and can be hidden with the new modifier (The new modifier).

Rezerwacja tych nazw służy trzy cele:The reservation of these names serves three purposes:

  • Aby umożliwić podstawowej implementacji użycie zwykłego identyfikatora jako nazwy metody do pobierania lub ustawiania dostępu do funkcji C# języka.To allow the underlying implementation to use an ordinary identifier as a method name for get or set access to the C# language feature.
  • Aby zezwolić innym językom na współdziałanie przy użyciu zwykłego identyfikatora jako nazwy metody do pobierania lub ustawiania C# dostępu do funkcji języka.To allow other languages to interoperate using an ordinary identifier as a method name for get or set access to the C# language feature.
  • Aby zapewnić, że źródło zaakceptowane przez jeden z zgodnych kompilatorów jest akceptowane przez inny, przez wprowadzenie określonych nazw zarezerwowanych elementów członkowskich spójnych we C# wszystkich implementacjach.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.

Deklaracja destruktora (destruktory) powoduje również, że podpis ma być zarezerwowany (nazwy składowe zarezerwowane dla destruktorów).The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

Nazwy elementów członkowskich zarezerwowane dla właściwościMember names reserved for properties

Dla właściwości P (Właściwości) typu Tnastępujące podpisy są zastrzeżone:For a property P (Properties) of type T, the following signatures are reserved:

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

Oba podpisy są zastrzeżone, nawet jeśli właściwość jest tylko do odczytu lub tylko do zapisu.Both signatures are reserved, even if the property is read-only or write-only.

w przykładzieIn 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());
    }
}

Klasa A definiuje właściwość tylko do odczytu P, w ten sposób zachowując sygnatury dla metod get_P i set_P.a class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. Klasa B pochodzi od A i ukrywa oba te podpisy zarezerwowane.A class B derives from A and hides both of these reserved signatures. Przykład generuje dane wyjściowe:The example produces the output:

123
123
456

Nazwy elementów członkowskich zarezerwowane dla zdarzeńMember names reserved for events

W przypadku zdarzenia E (zdarzenia) typu delegata Tnastępujące podpisy są zastrzeżone:For an event E (Events) of delegate type T, the following signatures are reserved:

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

Nazwy elementów członkowskich zarezerwowane dla indeksatorówMember names reserved for indexers

Dla indeksatora (indeksatorów) typu T z Llisty parametrów następujące podpisy są zastrzeżone: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);

Oba podpisy są zastrzeżone, nawet jeśli indeksator jest tylko do odczytu lub tylko do zapisu.Both signatures are reserved, even if the indexer is read-only or write-only.

Ponadto nazwa elementu członkowskiego Item jest zarezerwowana.Furthermore the member name Item is reserved.

Nazwy elementów członkowskich zarezerwowane dla destruktorówMember names reserved for destructors

W przypadku klasy zawierającej destruktor (destruktory) następujący podpis jest zastrzeżony:For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

{1>Stałe<1}Constants

Stała jest składową klasy, która reprezentuje wartość stałą: wartość, która może być obliczana w czasie kompilacji.A constant is a class member that represents a constant value: a value that can be computed at compile-time. Constant_declaration wprowadza jedną lub więcej stałych danego typu.A constant_declaration introduces one or more constants of a given type.

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

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

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Constant_declaration może zawierać zestaw atrybutów (atrybutów), modyfikator new (nowy modyfikator) i prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu).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). Atrybuty i Modyfikatory mają zastosowanie do wszystkich elementów członkowskich zadeklarowanych przez constant_declaration.The attributes and modifiers apply to all of the members declared by the constant_declaration. Mimo że stałe są uznawane za statyczne elementy członkowskie, constant_declaration nie wymagają ani nie dopuszcza modyfikatora static.Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. Jest to błąd dla tego samego modyfikatora, który może występować wiele razy w deklaracji stałej.It is an error for the same modifier to appear multiple times in a constant declaration.

Typ constant_declaration określa typ elementów członkowskich wprowadzonych przez deklarację.The type of a constant_declaration specifies the type of the members introduced by the declaration. Po tym typie następuje lista constant_declarators, z których każdy wprowadza nowy element członkowski.The type is followed by a list of constant_declarators, each of which introduces a new member. Constant_declarator składa się z identyfikatora , który ma nazwę elementu członkowskiego, po którym następuje token "=", po którym następuje constant_expression (wyrażenia stałe), które dają wartość elementu członkowskiego.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.

Typ określony w deklaracji stałej musi mieć wartość sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, enum_typei reference_type.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. Każdy constant_expression musi zwracać wartość typu docelowego lub typu, który można przekonwertować na typ docelowy przez niejawną konwersję (konwersje niejawne).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).

Typ stałej musi być co najmniej tak samo jak sam sam (ograniczenia dostępności).The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

Wartość stałej jest uzyskiwana w wyrażeniu przy użyciu simple_name (nazw prostych) lub member_access (dostęp do elementu członkowskiego).The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

Stała może same uczestniczyć w constant_expression.A constant can itself participate in a constant_expression. W tym celu można użyć stałej w dowolnej konstrukcji, która wymaga constant_expression.Thus, a constant may be used in any construct that requires a constant_expression. Przykłady takich konstrukcji obejmują case etykiet, instrukcji goto case, enum deklaracji składowych, atrybutów i innych deklaracji stałych.Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

Zgodnie z opisem w wyrażeniach stałych constant_expression jest wyrażeniem, które może być w pełni oceniane w czasie kompilacji.As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. Ze względu na to, że jedynym sposobem utworzenia wartości innej niż null reference_type innej niż string jest zastosowanie operatora new, a ponieważ operator new nie jest dozwolony w constant_expression, jedyną możliwą wartością dla stałych reference_types innych niż string jest null.Since the only way to create a non-null value of a reference_type other than string is to apply the new operator, and since the new operator is not permitted in a constant_expression, the only possible value for constants of reference_types other than string is null.

Gdy wymagana jest Nazwa symboliczna wartości stałej, ale jeśli typ tej wartości nie jest dozwolony w deklaracji stałej lub gdy wartość nie może zostać obliczona w czasie kompilacji przez constant_expression, w zamian może być używane pole readonly (pola tylko do odczytu).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.

Deklaracja stałej, która deklaruje wiele stałych, jest równoważna z wieloma deklaracjami pojedynczych stałych z tymi samymi atrybutami, modyfikatorami i typem.A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Na przykład:For example

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

odpowiada wyrażeniuis equivalent to

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

Stałe mogą zależeć od innych stałych w ramach tego samego programu, o ile zależności nie mają charakteru cyklicznego.Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. Kompilator automatycznie organizuje ocenę stałych deklaracji w odpowiedniej kolejności.The compiler automatically arranges to evaluate the constant declarations in the appropriate order. w przykładzieIn 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;
}

Kompilator najpierw szacuje A.Y, następnie szacuje B.Z, a wreszcie szacuje A.X, generując wartości 10, 11i 12.the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. Deklaracje stałe mogą zależeć od stałych z innych programów, ale takie zależności są możliwe tylko w jednym kierunku.Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. W odniesieniu do powyższego przykładu, jeśli A i B zostały zadeklarowane w osobnych programach, możliwe jest, aby A.X zależały od B.Z, ale B.Z nie zależały jednocześnie od 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.

PolaFields

Pole jest składową, która reprezentuje zmienną skojarzoną z obiektem lub klasą.A field is a member that represents a variable associated with an object or class. Field_declaration wprowadza co najmniej jedno pole danego typu.A field_declaration introduces one or more fields of a given type.

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

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

variable_declarators
    : variable_declarator (',' variable_declarator)*
    ;

variable_declarator
    : identifier ('=' variable_initializer)?
    ;

variable_initializer
    : expression
    | array_initializer
    ;

Field_declaration może zawierać zestaw atrybutów (atrybutów), modyfikator new (nowy modyfikator), prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu) i modyfikator static (pola statyczne i wystąpienia).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). Ponadto field_declaration może zawierać modyfikator readonly (pola tylko do odczytu) lub modyfikator volatile (pola nietrwałe), ale nie oba jednocześnie.In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. Atrybuty i Modyfikatory mają zastosowanie do wszystkich elementów członkowskich zadeklarowanych przez field_declaration.The attributes and modifiers apply to all of the members declared by the field_declaration. Jest to błąd dla tego samego modyfikatora, który może występować wiele razy w deklaracji pola.It is an error for the same modifier to appear multiple times in a field declaration.

Typ field_declaration określa typ elementów członkowskich wprowadzonych przez deklarację.The type of a field_declaration specifies the type of the members introduced by the declaration. Po tym typie następuje lista variable_declarators, z których każdy wprowadza nowy element członkowski.The type is followed by a list of variable_declarators, each of which introduces a new member. Variable_declarator składa się z identyfikatora , który jest nazwą tego elementu członkowskiego, opcjonalnie następuje token "=" i variable_initializer (inicjatory zmiennych), który daje początkową wartość tego elementu członkowskiego.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.

Typ pola musi być co najmniej tak samo jak w przypadku samego pola (ograniczenia dostępności).The type of a field must be at least as accessible as the field itself (Accessibility constraints).

Wartość pola jest uzyskiwana w wyrażeniu przy użyciu simple_name (nazw prostych) lub member_access (dostęp do elementu członkowskiego).The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). Wartość pola, które nie jest tylko do odczytu, jest modyfikowana przy użyciu przypisania (Operatory przypisania).The value of a non-readonly field is modified using an assignment (Assignment operators). Wartość pola, które nie jest tylko do odczytu, może być zarówno uzyskana, jak i modyfikowana przy użyciu przyrostka przyrostkowego i zmniejszania (operatoryprzyrostu i zmniejszania) oraz operatory przyrostu i zmniejszania wartości prefiksów (Operatory przyrostu izmniejszania).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).

Deklaracja pola, która deklaruje wiele pól, jest równoważna z wieloma deklaracjami pojedynczych pól z tymi samymi atrybutami, modyfikatorami i typem.A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. Na przykład:For example

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

odpowiada wyrażeniuis equivalent to

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

Pola statyczne i wystąpieniaStatic and instance fields

Gdy deklaracja pola zawiera modyfikator static, pola wprowadzane przez deklarację są polami statycznymi.When a field declaration includes a static modifier, the fields introduced by the declaration are static fields. Gdy modyfikator static nie jest obecny, pola wprowadzane przez deklarację są polami wystąpień.When no static modifier is present, the fields introduced by the declaration are instance fields. Pola statyczne i wystąpienia są dwa rodzaje zmiennych (zmienne) obsługiwane przez C#, a czasami są one określane odpowiednio jako zmienne statyczne i zmienne wystąpień.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.

Pole statyczne nie jest częścią określonego wystąpienia; Zamiast tego jest współużytkowany między wszystkimi wystąpieniami typu zamkniętego (otwarte i zamknięte).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). Niezależnie od tego, ile wystąpień typu zamkniętej klasy są tworzone, istnieje tylko jedna kopia pola statycznego dla skojarzonej domeny aplikacji.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.

Na przykład: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
    }
}

Pole wystąpienia należy do wystąpienia.An instance field belongs to an instance. Każde wystąpienie klasy zawiera oddzielny zestaw wszystkich pól wystąpienia tej klasy.Specifically, every instance of a class contains a separate set of all the instance fields of that class.

Gdy odwołanie do pola znajduje się w member_access (dostęp do elementu członkowskiego) formularza E.M, jeśli M jest polem statycznym, E musi zwrócić uwagę na typ zawierający Mi jeśli M jest polem wystąpienia, E musi zwrócić uwagę na wystąpienie typu zawierającego 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.

Różnice między elementami statycznymi a składowymi wystąpienia są omówione bardziej szczegółowo w elementach członkowskich static i instance.The differences between static and instance members are discussed further in Static and instance members.

Pola tylko do odczytuReadonly fields

Gdy field_declaration zawiera modyfikator readonly, pola wprowadzone przez deklarację to pola tylko do odczytu.When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Bezpośrednie przypisania do pól tylko do odczytu mogą wystąpić tylko jako część tej deklaracji lub w konstruktorze wystąpienia lub w konstruktorze statycznym w tej samej klasie.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. (Pole tylko do odczytu można przypisać wiele razy w tych kontekstach). W szczególnych przypadkach przypisania bezpośrednie do pola readonly są dozwolone tylko w następujących kontekstach:(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:

  • W variable_declarator , które wprowadza pole (poprzez uwzględnienie variable_initializer w deklaracji).In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • Dla pola wystąpienia w konstruktorach wystąpień klasy, która zawiera deklarację pola; dla pola statycznego w konstruktorze statycznym klasy, która zawiera deklarację pola.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. Są to również jedyne konteksty, w których prawidłowe jest przekazanie pola readonly jako parametru out lub ref.These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Próba przypisania do pola readonly lub przekazania go jako parametru out lub ref w dowolnym innym kontekście jest błędem czasu kompilacji.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.

Używanie statycznych pól tylko do odczytu dla stałychUsing static readonly fields for constants

Pole static readonly jest przydatne, gdy wymagana jest Nazwa symboliczna wartości stałej, ale jeśli typ wartości nie jest dozwolony w deklaracji const lub nie można obliczyć wartości w czasie kompilacji.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. w przykładzieIn 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;
    }
}

elementów członkowskich Black, White, Red, Greeni Blue nie można zadeklarować jako elementów członkowskich const, ponieważ ich wartości nie mogą być obliczane w czasie kompilacji.the Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. Jednakże deklarując je static readonly ma znacznie taki sam skutek.However, declaring them static readonly instead has much the same effect.

Przechowywanie wersji stałych i statycznych pól tylko do odczytuVersioning of constants and static readonly fields

Stałe i tylko do odczytu pola mają różne semantyki wersji binarnej.Constants and readonly fields have different binary versioning semantics. Gdy wyrażenie odwołuje się do stałej, wartość stałej jest uzyskiwana w czasie kompilacji, ale gdy wyrażenie odwołuje się do pola tylko do odczytu, wartość pola nie zostanie uzyskana do czasu wykonania.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. Rozważmy aplikację, która składa się z dwóch oddzielnych programów: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);
        }
    }
}

Przestrzenie nazw Program1 i Program2 oznaczają dwa programy, które są kompilowane osobno.The Program1 and Program2 namespaces denote two programs that are compiled separately. Ponieważ Program1.Utils.X jest zadeklarowana jako statyczne pole tylko do odczytu, wartość wyjściowa instrukcji Console.WriteLine nie jest znana w czasie kompilacji, ale jest uzyskiwana w czasie wykonywania.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. W takim przypadku, jeśli wartość X zostanie zmieniona i Program1 zostanie ponownie skompilowana, instrukcja Console.WriteLine będzie wyprowadzać nową wartość nawet wtedy, gdy Program2 nie zostanie ponownie skompilowana.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. Jednak X była stałą, wartość X zostałaby uzyskana w czasie, Program2 została skompilowana i pozostanie nienaruszona przez zmiany w Program1 do momentu ponownego skompilowania Program2.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.

Pola nietrwałeVolatile fields

Gdy field_declaration zawiera modyfikator volatile, pola wprowadzone przez tę deklarację są polami nietrwałymi.When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

W przypadku pól nietrwałych, techniki optymalizacji, które zmieniają kolejność instrukcji, mogą prowadzić do nieoczekiwanych i nieprzewidzianych wyników w programach wielowątkowych, które uzyskują dostęp do pól bez synchronizacji, takich jak zapewniane przez lock_statement (instrukcja Lock).For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock_statement (The lock statement). Optymalizacje mogą być wykonywane przez kompilator, przez system czasu wykonywania lub przez sprzęt.These optimizations can be performed by the compiler, by the run-time system, or by hardware. W przypadku pól nietrwałych takie zmiany kolejności są ograniczone:For volatile fields, such reordering optimizations are restricted:

  • Odczyt pola nietrwałego jest nazywany nietrwałym odczytem.A read of a volatile field is called a volatile read. Odczyt nietrwały ma "pozyskiwanie semantyki"; oznacza to, że jest to konieczne przed wszelkimi odwołaniami do pamięci, która następuje po niej w sekwencji instrukcji.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.
  • Zapis pola nietrwałego jest nazywany zapisem nietrwałym.A write of a volatile field is called a volatile write. Zapis nietrwały ma "semantykę wersji"; oznacza to, że ma to miejsce po dowolnych odwołaniach do pamięci przed instrukcją zapisu w sekwencji instrukcji.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.

Te ograniczenia zapewniają, że wszystkie wątki będą obserwować nietrwałe zapisy wykonywane przez każdy inny wątek w kolejności, w której zostały wykonane.These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. Implementacja zgodna z implementacją nie jest wymagana do zapewnienia pojedynczej kolejności zapisów nietrwałych w postaci zaobserwowanej ze wszystkich wątków wykonania.A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. Typ pola nietrwałego musi mieć jedną z następujących wartości:The type of a volatile field must be one of the following:

  • Reference_type.A reference_type.
  • Typ byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtrlub System.UIntPtr.The type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • Enum_type ma typ podstawowy wyliczenia byte, sbyte, short, ushort, intlub uint.An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

PrzykładThe 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;
            }
        }
    }
}

tworzy dane wyjściowe:produces the output:

result = 143

W tym przykładzie metoda Main uruchamia nowy wątek, w którym jest uruchamiana Metoda Thread2.In this example, the method Main starts a new thread that runs the method Thread2. Ta metoda przechowuje wartość w polu nietrwałym o nazwie result, a następnie przechowuje true w finishedpolu trwałym.This method stores a value into a non-volatile field called result, then stores true in the volatile field finished. Główny wątek czeka na ustawienie pola finished na true, a następnie odczytuje resultpola.The main thread waits for the field finished to be set to true, then reads the field result. Ponieważ finished został zadeklarowany volatile, główny wątek musi odczytać wartość 143 z resultpola.Since finished has been declared volatile, the main thread must read the value 143 from the field result. Jeśli pole finished nie zostało zadeklarowane volatile, będzie dozwolone, aby Magazyn result widoczny dla głównego wątku po magazynie do finishedi dlatego dla wątku głównego odczytać wartość 0 z pola 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. Deklarowanie finished jako pola volatile uniemożliwia takie niespójność.Declaring finished as a volatile field prevents any such inconsistency.

Inicjowanie polaField initialization

Wartość początkowa pola, niezależnie od tego, czy jest to pole statyczne czy pole wystąpienia, jest wartością domyślną (wartości domyślne) typu pola.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. Nie jest możliwe przestrzeganie wartości pola przed wystąpieniem domyślnej inicjalizacji, a w takim przypadku pole nigdy nie jest "niezainicjowane".It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". PrzykładThe 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);
    }
}

tworzy dane wyjścioweproduces the output

b = False, i = 0

ponieważ b i i są automatycznie inicjowane do wartości domyślnych.because b and i are both automatically initialized to default values.

Inicjatory zmiennychVariable initializers

Deklaracje pól mogą zawierać variable_initializers.Field declarations may include variable_initializers. Dla pól statycznych, inicjatory zmiennych odpowiadają instrukcjom przypisania, które są wykonywane podczas inicjowania klasy.For static fields, variable initializers correspond to assignment statements that are executed during class initialization. W przypadku pól wystąpienia zmienne inicjatory odpowiadają instrukcjom przypisania, które są wykonywane podczas tworzenia wystąpienia klasy.For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

PrzykładThe 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);
    }
}

tworzy dane wyjścioweproduces the output

x = 1.4142135623731, i = 100, s = Hello

Ponieważ przypisanie do x występuje, gdy inicjatory pola statycznego są wykonywane, a przypisania do i i s wystąpią, kiedy inicjator pola wystąpienia zostanie wykonany.because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

Inicjalizacja wartości domyślnej opisana w ramach inicjalizacji pola występuje dla wszystkich pól, w tym pól, które mają inicjatory zmiennych.The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. W tym przypadku po zainicjowaniu klasy wszystkie pola statyczne w tej klasie są najpierw inicjowane na wartości domyślne, a następnie inicjatory pola statycznego są wykonywane w kolejności tekstowej.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. Podobnie, gdy tworzone jest wystąpienie klasy, wszystkie pola wystąpienia w tym wystąpieniu są najpierw inicjowane na wartości domyślne, a następnie inicjatory pola wystąpienia są wykonywane w kolejności tekstowej.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.

Istnieje możliwość, że pola statyczne z inicjatorami zmiennych mają być przestrzegane w stanie wartości domyślnej.It is possible for static fields with variable initializers to be observed in their default value state. Jest to jednak zdecydowanie odradzane jako kwestia stylu.However, this is strongly discouraged as a matter of style. PrzykładThe 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);
    }
}

wykazuje takie zachowanie.exhibits this behavior. Pomimo definicji cyklicznych a i b, program jest prawidłowy.Despite the circular definitions of a and b, the program is valid. Powoduje to wyjścieIt results in the output

a = 1, b = 2

ponieważ pola statyczne a i b są inicjowane do 0 (wartość domyślna dla int) przed wykonaniem inicjatorów.because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. Gdy inicjator a jest uruchomiony, wartość b jest równa zero, więc a jest inicjowana do 1.When the initializer for a runs, the value of b is zero, and so a is initialized to 1. Po uruchomieniu inicjatora b, wartość a jest już 1, a więc b jest inicjowana do 2.When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

Inicjowanie pola statycznegoStatic field initialization

Inicjatory zmiennych pól statycznych klasy odpowiadają sekwencji przypisań, które są wykonywane w kolejności tekstowej, w której pojawiają się w deklaracji klasy.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. Jeśli w klasie istnieje Konstruktor statyczny (konstruktory statyczne), wykonywanie inicjatorów pól statycznych odbywa się bezpośrednio przed wykonaniem tego konstruktora statycznego.If a static constructor (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. W przeciwnym razie inicjatory pola statycznego są wykonywane w czasie zależnym od implementacji przed pierwszym użyciem pola statycznego tej klasy.Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. PrzykładThe 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");
}

może generować dane wyjściowe:might produce either the output:

Init A
Init B
1 1

lub dane wyjściowe:or the output:

Init B
Init A
1 1

ponieważ wykonywanie inicjatora Xi inicjatora Ymoże wystąpić w dowolnej kolejności; są one ograniczone tylko przed odwołaniami do tych pól.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. Jednak w przykładzie: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");
}

dane wyjściowe muszą być następujące:the output must be:

Init B
Init A
1 1

ponieważ reguły, które są wykonywane przez konstruktory statyczne (zgodnie z definicją w konstruktorach statycznych), zapewniają, że BKonstruktor statyczny (i dlatego, że Bpola statyczne inicjatorów) muszą zostać uruchomione przed Astatycznego konstruktora i pól.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.

Inicjowanie pola wystąpieniaInstance field initialization

Inicjatory zmiennych pola wystąpienia klasy odpowiadają sekwencji przypisań, które są wykonywane natychmiast po wprowadzeniu do dowolnego jednego z konstruktorów wystąpień (inicjatory konstruktorów) tej klasy.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. Inicjatory zmiennych są wykonywane w kolejności tekstowej, w której pojawiają się w deklaracji klasy.The variable initializers are executed in the textual order in which they appear in the class declaration. Proces tworzenia i inicjowania wystąpienia klasy został szczegółowo opisany w konstruktorach wystąpień.The class instance creation and initialization process is described further in Instance constructors.

Inicjator zmiennej dla pola wystąpienia nie może odwoływać się do tworzonego wystąpienia.A variable initializer for an instance field cannot reference the instance being created. W ten sposób jest to błąd czasu kompilacji, aby odwołać się do this w inicjatorze zmiennej, ponieważ jest to błąd czasu kompilacji dla inicjatora zmiennej, który odwołuje się do dowolnego elementu członkowskiego wystąpienia za pomocą simple_name.Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name. w przykładzieIn the example

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

Inicjator zmiennej dla y powoduje błąd czasu kompilacji, ponieważ odwołuje się do elementu członkowskiego tworzonego wystąpienia.the variable initializer for y results in a compile-time error because it references a member of the instance being created.

MetodyMethods

Metoda to element członkowski implementujący obliczenia lub akcję, które mogą być wykonywane przez obiekt lub klasę.A method is a member that implements a computation or action that can be performed by an object or class. Metody są deklarowane przy użyciu method_declarations:Methods are declared using method_declarations:

method_declaration
    : method_header method_body
    ;

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

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

return_type
    : type
    | 'void'
    ;

member_name
    : identifier
    | interface_type '.' identifier
    ;

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

Method_declaration może zawierać zestaw atrybutów (atrybutów) i prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu), new (nowy modyfikator), static (Metodastatyczna i wystąpienia), virtual (metody wirtualne), override (metody zastępowania), sealed (zapieczętowane metody), abstract (metody abstrakcyjne) i extern (metodyzewnętrzne) modyfikatory.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.

Deklaracja ma prawidłową kombinację modyfikatorów, jeśli spełnione są wszystkie następujące warunki:A declaration has a valid combination of modifiers if all of the following are true:

  • Deklaracja zawiera prawidłową kombinację modyfikatorów dostępu (Modyfikatory dostępu).The declaration includes a valid combination of access modifiers (Access modifiers).
  • Deklaracja nie zawiera tego samego modyfikatora wiele razy.The declaration does not include the same modifier multiple times.
  • Deklaracja zawiera co najwyżej jeden z następujących modyfikatorów: static, virtuali override.The declaration includes at most one of the following modifiers: static, virtual, and override.
  • Deklaracja zawiera co najwyżej jeden z następujących modyfikatorów: new i override.The declaration includes at most one of the following modifiers: new and override.
  • Jeśli deklaracja zawiera modyfikator abstract, to Deklaracja nie zawiera żadnego z następujących modyfikatorów: static, virtual, sealed lub extern.If the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • Jeśli deklaracja zawiera modyfikator private, to Deklaracja nie zawiera żadnego z następujących modyfikatorów: virtual, overridelub abstract.If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • Jeśli deklaracja zawiera modyfikator sealed, wówczas deklaracja zawiera również modyfikator override.If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • Jeśli deklaracja zawiera modyfikator partial, wówczas nie zawiera żadnego z następujących modyfikatorów: new, public, protected, internal, private, virtual, sealed, override, abstractlub extern.If the declaration includes the partial modifier, then it does not include any of the following modifiers: new, public, protected, internal, private, virtual, sealed, override, abstract, or extern.

Metoda, która ma modyfikator async jest funkcją asynchroniczną i jest zgodna z regułami opisanymi w funkcjach asynchronicznych.A method that has the async modifier is an async function and follows the rules described in Async functions.

Return_type deklaracji metody Określa typ wartości obliczanej i zwracanej przez metodę.The return_type of a method declaration specifies the type of the value computed and returned by the method. Return_type jest void, jeśli metoda nie zwraca wartości.The return_type is void if the method does not return a value. Jeśli deklaracja zawiera modyfikator partial, zwracany typ musi być void.If the declaration includes the partial modifier, then the return type must be void.

MEMBER_NAME określa nazwę metody.The member_name specifies the name of the method. O ile Metoda nie jest jawną implementacją składowej interfejsu (jawne implementacje elementu członkowskiego interfejsu), MEMBER_NAME jest po prostu identyfikatorem.Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. W przypadku jawnej implementacji elementu członkowskiego interfejsu MEMBER_NAME składa się z INTERFACE_TYPE po którym następuje "." i Identyfikator.For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

Opcjonalne type_parameter_list określa parametry typu metody (parametry typu).The optional type_parameter_list specifies the type parameters of the method (Type parameters). Jeśli type_parameter_list jest określony, metoda jest metodą rodzajową.If a type_parameter_list is specified the method is a generic method. Jeśli metoda ma modyfikator extern, nie można określić type_parameter_list .If the method has an extern modifier, a type_parameter_list cannot be specified.

Opcjonalne formal_parameter_list określa parametry metody (Parametry metody).The optional formal_parameter_list specifies the parameters of the method (Method parameters).

Opcjonalne type_parameter_constraints_clauses określają ograniczenia dotyczące poszczególnych parametrów typu (ograniczenia parametru typu) i można je określić tylko w przypadku podania type_parameter_list również, a metoda nie ma modyfikatora override.The optional type_parameter_constraints_clauses specify constraints on individual type parameters (Type parameter constraints) and may only be specified if a type_parameter_list is also supplied, and the method does not have an override modifier.

Return_type i każdy z typów, do których odwołuje się formal_parameter_list metody, musi być co najmniej tak samo samo jak Metoda (ograniczenia dostępności).The return_type and each of the types referenced in the formal_parameter_list of a method must be at least as accessible as the method itself (Accessibility constraints).

Method_body jest średnikiem, treścią instrukcji lub treścią wyrażenia.The method_body is either a semicolon, a statement body or an expression body. Treść instrukcji składa się z bloku, który określa instrukcje do wykonania, gdy metoda jest wywoływana.A statement body consists of a block, which specifies the statements to execute when the method is invoked. Treść wyrażenia składa się z => po którym następuje wyrażenie i średnik, i oznacza pojedyncze wyrażenie do wykonania, gdy wywoływana jest metoda.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the method is invoked.

W przypadku metod abstract i extern method_body składa się po prostu z średnika.For abstract and extern methods, the method_body consists simply of a semicolon. Dla partial metod, method_body może składać się z średnika, treści bloku lub treści wyrażenia.For partial methods the method_body may consist of either a semicolon, a block body or an expression body. Dla wszystkich innych metod method_body jest treścią bloku lub treścią wyrażenia.For all other methods, the method_body is either a block body or an expression body.

Jeśli method_body składa się z średnika, wówczas deklaracja nie może zawierać modyfikatora async.If the method_body consists of a semicolon, then the declaration may not include the async modifier.

Nazwa, lista parametrów typu i formalna lista parametrów metody definiują sygnaturę (sygnatury i Przeciążenie) metody.The name, the type parameter list and the formal parameter list of a method define the signature (Signatures and overloading) of the method. W każdym przypadku podpis metody składa się z jej nazwy, liczby parametrów typu oraz liczby, modyfikatorów i typów jego parametrów formalnych.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. W tym celu wszelkie parametry typu metody, która występuje w typie parametru formalnego, nie są identyfikowane przez jego nazwę, ale według pozycji porządkowej na liście argumentów typu metody. Zwracany typ nie jest częścią podpisu metody ani nie są nazwami parametrów typu lub parametrów formalnych.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.

Nazwa metody musi różnić się od nazw wszystkich innych metod, które nie są zadeklarowane w tej samej klasie.The name of a method must differ from the names of all other non-methods declared in the same class. Ponadto podpis metody musi różnić się od sygnatur wszystkich innych metod zadeklarowanych w tej samej klasie, a dwie metody zadeklarowane w tej samej klasie mogą nie mieć podpisów, które różnią się wyłącznie ref i out.In addition, the signature of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.

Type_parameters metody są w zakresie w method_declarationi mogą być używane do tworzenia typów w całym zakresie w return_type, method_bodyi type_parameter_constraints_clauses, ale nie w atrybutach.The method's type_parameters are in scope throughout the method_declaration, and can be used to form types throughout that scope in return_type, method_body, and type_parameter_constraints_clauses but not in attributes.

Wszystkie parametry formalne i parametry typu muszą mieć różne nazwy.All formal parameters and type parameters must have different names.

Parametry metodyMethod parameters

Parametry metody, jeśli istnieje, są deklarowane przez formal_parameter_listmetody.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
    ;

Formalna lista parametrów składa się z jednego lub więcej parametrów oddzielonych przecinkami, których tylko ostatni może być parameter_array.The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Fixed_parameter składa się z opcjonalnego zestawu atrybutów (atrybutów), opcjonalnego modyfikatora ref, out lub this, typu, identyfikatora i opcjonalnego 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. Każda fixed_parameter deklaruje parametr danego typu o podaną nazwę.Each fixed_parameter declares a parameter of the given type with the given name. Modyfikator this wyznacza metodę jako metodę rozszerzającą i jest dozwolony tylko dla pierwszego parametru metody statycznej.The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method. Metody rozszerzające są dokładniej opisane w metodach rozszerzających.Extension methods are further described in Extension methods.

Fixed_parameter z default_argument jest znany jako parametr opcjonalny, podczas gdy fixed_parameter bez default_argument jest wymaganym parametrem.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. Wymagany parametr nie może występować po opcjonalnym parametrze w formal_parameter_list.A required parameter may not appear after an optional parameter in a formal_parameter_list.

Parametr ref lub out nie może mieć default_argument.A ref or out parameter cannot have a default_argument. Wyrażenie w default_argument musi mieć jedną z następujących wartości:The expression in a default_argument must be one of the following:

  • constant_expressiona constant_expression
  • wyrażenie new S(), gdzie S jest typem wartościan expression of the form new S() where S is a value type
  • wyrażenie default(S), gdzie S jest typem wartościan expression of the form default(S) where S is a value type

Wyrażenie musi być niejawnie konwertowane przez tożsamość lub konwersję dopuszczającą wartości null na typ parametru.The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

Jeśli parametry opcjonalne występują w deklaracji metody częściowej implementującej (metody częściowe), Jawna implementacja składowej interfejsu (jawne implementacje elementu członkowskiego interfejsu) lub w jednoparametrowej deklaracji indeksatora (indeksatory) kompilator powinien dać ostrzeżenie, ponieważ te elementy członkowskie nigdy nie mogą być wywoływane w sposób zezwalający na pominięcie argumentów.If optional parameters occur in an implementing partial method declaration (Partial methods) , an explicit interface member implementation (Explicit interface member implementations) or in a single-parameter indexer declaration (Indexers) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted.

Parameter_array składa się z opcjonalnego zestawu atrybutów (atrybutów), modyfikator params, array_typei Identyfikator.A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. Tablica parametrów deklaruje pojedynczy parametr danego typu tablicy o podaną nazwę.A parameter array declares a single parameter of the given array type with the given name. Array_type tablicy parametrów musi być typem tablicy jednowymiarowej (typy tablicowe).The array_type of a parameter array must be a single-dimensional array type (Array types). W wywołaniu metody, tablica parametrów zezwala na określenie pojedynczego argumentu danego typu tablicy lub dopuszcza zero lub więcej argumentów typu elementu tablicy, który ma zostać określony.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. Tablice parametrów są szczegółowo opisane w tablicach parametrów.Parameter arrays are described further in Parameter arrays.

Parameter_array może występować po opcjonalnym parametrze, ale nie może mieć wartości domyślnej — pominięcie argumentów dla parameter_array zamiast tego spowoduje utworzenie pustej tablicy.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.

Poniższy przykład ilustruje różne rodzaje parametrów: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
) { }

W formal_parameter_list dla M, i jest wymaganym parametrem ref, d jest wymaganym parametrem wartości, b, s, o i t są opcjonalnymi parametrami wartości, a a jest tablicą parametrów.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.

Deklaracja metody tworzy oddzielne miejsce deklaracji dla parametrów, parametrów typu i zmiennych lokalnych.A method declaration creates a separate declaration space for parameters, type parameters and local variables. Nazwy są wprowadzane do tej przestrzeni deklaracji przez listę parametrów typu i formalną listę parametrów metody i według lokalnych deklaracji zmiennych w bloku metody.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. Występuje błąd dla dwóch elementów członkowskich przestrzeni deklaracji metody, które mają taką samą nazwę.It is an error for two members of a method declaration space to have the same name. Jest to błąd dla przestrzeni deklaracji metody i przestrzeni lokalnej deklaracji zmiennej obszaru zagnieżdżonej deklaracji, która zawiera elementy o tej samej nazwie.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.

Wywołanie metody (wywołania metody) tworzy kopię, specyficzną dla tego wywołania, parametrów formalnych i zmiennych lokalnych metody, a lista argumentów wywołania przypisuje wartości lub odwołania do zmiennych do nowo utworzonych parametrów formalnych.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. W bloku metody do parametrów formalnych można odwoływać się ich identyfikatory w wyrażeniach simple_name (proste nazwy).Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

Istnieją cztery rodzaje parametrów formalnych:There are four kinds of formal parameters:

  • Parametry wartości, które są zadeklarowane bez żadnych modyfikatorów.Value parameters, which are declared without any modifiers.
  • Parametry odwołania, które są zadeklarowane za pomocą modyfikatora ref.Reference parameters, which are declared with the ref modifier.
  • Parametry wyjściowe, które są zadeklarowane za pomocą modyfikatora out.Output parameters, which are declared with the out modifier.
  • Tablice parametrów, które są zadeklarowane za pomocą modyfikatora params.Parameter arrays, which are declared with the params modifier.

Zgodnie z opisem w podpisach i przeciążeniu, modyfikatory ref i out są częścią podpisu metody, ale modyfikator params nie jest.As described in Signatures and overloading, the ref and out modifiers are part of a method's signature, but the params modifier is not.

Parametry wartościValue parameters

Parametr zadeklarowany bez modyfikatorów jest parametrem wartości.A parameter declared with no modifiers is a value parameter. Parametr value odnosi się do zmiennej lokalnej, która pobiera jej wartość początkową z odpowiedniego argumentu dostarczonego w wywołaniu metody.A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

Gdy parametr formalny jest parametrem wartości, odpowiadający mu argument w wywołaniu metody musi być wyrażeniem, które jest niejawnie konwertowane (konwersje niejawne) na typ parametru formalnego.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.

Metoda może przypisywać nowe wartości do parametru value.A method is permitted to assign new values to a value parameter. Takie przypisania mają wpływ tylko na lokalizację magazynu lokalnego reprezentowane przez parametr value — nie mają wpływu na rzeczywisty argument określony w wywołaniu metody.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.

Parametry odwołaniaReference parameters

Parametr zadeklarowany za pomocą modyfikatora ref jest parametrem referencyjnym.A parameter declared with a ref modifier is a reference parameter. W przeciwieństwie do parametru wartości, parametr odwołania nie tworzy nowej lokalizacji magazynu.Unlike a value parameter, a reference parameter does not create a new storage location. Zamiast tego parametr odwołania reprezentuje tę samą lokalizację magazynu, co zmienna określona jako argument w wywołaniu metody.Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

Gdy parametr formalny jest parametrem referencyjnym, odpowiadający mu argument w wywołaniu metody musi zawierać słowo kluczowe ref, a następnie variable_reference (precyzyjne reguły określania przypisania, które mają być określone) tego samego typu co parametr formalny.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. Zmienna musi być ostatecznie przypisana, zanim będzie mogła zostać przeniesiona jako parametr referencyjny.A variable must be definitely assigned before it can be passed as a reference parameter.

W ramach metody parametr odwołania jest zawsze uznawany za ostatecznie przypisany.Within a method, a reference parameter is always considered definitely assigned.

Metoda zadeklarowana jako iterator (Iteratory) nie może mieć parametrów referencyjnych.A method declared as an iterator (Iterators) cannot have reference parameters.

PrzykładThe 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);
    }
}

tworzy dane wyjścioweproduces the output

i = 2, j = 1

W przypadku wywołania Swap w Main``x reprezentuje i i y reprezentuje j.For the invocation of Swap in Main, x represents i and y represents j. W efekcie wywołanie ma wpływ na zamianę wartości i i j.Thus, the invocation has the effect of swapping the values of i and j.

W metodzie, która pobiera parametry referencyjne, istnieje możliwość, że wiele nazw reprezentuje tę samą lokalizację magazynu.In a method that takes reference parameters it is possible for multiple names to represent the same storage location. w przykładzieIn 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);
    }
}

Wywołanie F w G przekazuje odwołanie do s dla a i b.the invocation of F in G passes a reference to s for both a and b. W takim przypadku nazwy s, ai b wszystkie odnoszą się do tej samej lokalizacji przechowywania, a wszystkie trzy przydziały modyfikują pole wystąpienia 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.

Parametry wyjścioweOutput parameters

Parametr zadeklarowany za pomocą modyfikatora out jest parametrem wyjściowym.A parameter declared with an out modifier is an output parameter. Podobnie jak w przypadku parametru Reference, parametr wyjściowy nie tworzy nowej lokalizacji magazynu.Similar to a reference parameter, an output parameter does not create a new storage location. Zamiast tego parametr wyjściowy reprezentuje taką samą lokalizację przechowywania jak zmienna określona jako argument w wywołaniu metody.Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

Gdy parametr formalny jest parametrem wyjściowym, odpowiadający mu argument w wywołaniu metody musi zawierać słowo kluczowe out, a następnie variable_reference (precyzyjne reguły określania przypisania, które mają być określone) tego samego typu co parametr formalny.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. Zmienna nie musi być ostatecznie przypisana, zanim będzie mogła zostać przeniesiona jako parametr wyjściowy, ale po wywołaniu, gdzie zmienna została przeniesiona jako parametr wyjściowy, zmienna jest uważana za ostatecznie przypisaną.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.

W ramach metody, podobnie jak zmienna lokalna, parametr wyjściowy jest początkowo uznawany za nieprzypisany i musi być ostatecznie przypisany przed użyciem wartości.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.

Każdy parametr wyjściowy metody musi być ostatecznie przypisany przed zwróceniem metody.Every output parameter of a method must be definitely assigned before the method returns.

Metoda zadeklarowana jako metoda częściowa (metody częściowe) lub iterator (Iteratory) nie może mieć parametrów wyjściowych.A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

Parametry wyjściowe są zwykle używane w metodach, które generują wiele wartości zwracanych.Output parameters are typically used in methods that produce multiple return values. Na przykład: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);
    }
}

Przykład generuje dane wyjściowe:The example produces the output:

c:\Windows\System\
hello.txt

Należy pamiętać, że zmienne dir i name mogą być nieprzypisane przed przekazaniem do SplitPathi że są uznawane za ostatecznie przypisane po wywołaniu.Note that the dir and name variables can be unassigned before they are passed to SplitPath, and that they are considered definitely assigned following the call.

Tablice parametrówParameter arrays

Parametr zadeklarowany za pomocą modyfikatora params jest tablicą parametrów.A parameter declared with a params modifier is a parameter array. Jeśli lista parametrów formalnych zawiera tablicę parametrów, musi być ostatnim parametrem na liście i musi być typu tablicy jednowymiarowej.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. Na przykład typy string[] i string[][] mogą być używane jako typ tablicy parametrów, ale string[,] typu nie może.For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. Nie można połączyć modyfikatora params z modyfikatorami ref i out.It is not possible to combine the params modifier with the modifiers ref and out.

Tablica parametrów zezwala na określenie argumentów na jeden z dwóch sposobów w wywołaniu metody:A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • Argument określony dla tablicy parametrów może być pojedynczym wyrażeniem, które jest niejawnie konwertowane (konwersje niejawne) na typ tablicy parametrów.The argument given for a parameter array can be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. W takim przypadku tablica parametrów działa dokładnie tak, jak parametr value.In this case, the parameter array acts precisely like a value parameter.
  • Alternatywnie, wywołanie może określać zero lub więcej argumentów tablicy parametrów, gdzie każdy argument jest wyrażeniem, które jest niejawnie konwertowane (konwersje niejawne) na typ elementu tablicy parametrów.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. W takim przypadku wywołanie tworzy wystąpienie typu tablicy parametrów o długości odpowiadającej liczbie argumentów, inicjuje elementy instancji Array z podaną wartością argumentów i używa nowo utworzonego wystąpienia tablicy jako wartości rzeczywistej 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.

Oprócz dopuszczania zmiennej liczby argumentów w wywołaniu, tablica parametrów jest dokładnie równoważna z parametrem wartości (Parametry wartości) tego samego typu.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.

PrzykładThe 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();
    }
}

tworzy dane wyjścioweproduces the output

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

Pierwsze wywołanie F po prostu przekazuje tablicę a jako parametr wartości.The first invocation of F simply passes the array a as a value parameter. Drugie wywołanie F automatycznie tworzy cztery elementy int[] z podaną wartością elementu i przekazuje to wystąpienie tablicy jako parametr wartości.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. Podobnie trzecie wywołanie F powoduje utworzenie elementu zero int[] i przekazanie tego wystąpienia jako parametru wartości.Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. Drugie i trzecie wywołania są dokładnie równoważne zapisowi:The second and third invocations are precisely equivalent to writing:

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

Podczas rozpoznawania przeciążenia metoda z tablicą parametrów może być stosowana w jego normalnej postaci lub w rozwiniętej formie (odpowiedni element członkowski funkcji).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). Rozwinięta forma metody jest dostępna tylko wtedy, gdy normalna forma metody nie ma zastosowania i tylko wtedy, gdy odpowiednia metoda o tej samej sygnaturze, co rozwinięta forma, nie jest już zadeklarowana w tym samym typie.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.

PrzykładThe 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);
    }
}

tworzy dane wyjścioweproduces the output

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

W przykładzie dwa z możliwych rozwiniętych formularzy metody z tablicą parametrów są już zawarte w klasie jako metody zwykłe.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. Te rozwinięte formularze nie są więc brane pod uwagę podczas rozpoznawania przeciążenia, a pierwsze i trzecie wywołania metody wybierają metody zwykłe.These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. Gdy Klasa deklaruje metodę z tablicą parametrów, nierzadko jest również zawierać część rozwiniętych formularzy jako zwykłe metody.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. Dzięki temu można uniknąć alokacji wystąpienia tablicy, które występuje, gdy zostanie wywołana rozwinięta forma metody z tablicą parametrów.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.

Gdy typ tablicy parametrów jest object[], potencjalna niejednoznaczność występuje między normalną formą metody a wykorzystaną formą dla jednego parametru object.When the type of a parameter array is object[], a potential ambiguity arises between the normal form of the method and the expended form for a single object parameter. Przyczyna niejednoznaczności polega na tym, że object[] jest sama niejawnie przekonwertowana na typ object.The reason for the ambiguity is that an object[] is itself implicitly convertible to type object. Niejednoznaczność nie ma jednak żadnego problemu, ponieważ można ją rozwiązać, wstawiając Rzutowanie w razie konieczności.The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

PrzykładThe 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);
    }
}

tworzy dane wyjścioweproduces the output

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

W pierwszym i ostatnim wywołaniu Fjest stosowana normalna forma F, ponieważ istnieje niejawna konwersja z typu argumentu na typ parametru (obie są typu 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[]). W rezultacie rozwiązanie przeciążania wybiera normalną postać Fi argument jest przenoszona jako parametr wartości regularnej.Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. W drugim i trzecim wywołaniu, normalna forma F nie ma zastosowania, ponieważ nie istnieje niejawna konwersja z typu argumentu na typ parametru (typ object nie może być niejawnie konwertowany na typ 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[]). Jednakże rozwinięta forma F ma zastosowanie, więc jest wybierana przez metodę rozpoznawania przeciążenia.However, the expanded form of F is applicable, so it is selected by overload resolution. W związku z tym object[] jest tworzony przez wywołanie, a pojedynczy element tablicy jest inicjowany z daną wartością argumentu (która sama jest odwołaniem do 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[]).

Metody static i instanceStatic and instance methods

Gdy deklaracja metody zawiera modyfikator static, ta metoda jest określana jako metoda statyczna.When a method declaration includes a static modifier, that method is said to be a static method. Gdy nie ma modyfikatora static, metoda jest uznawana za metodę wystąpienia.When no static modifier is present, the method is said to be an instance method.

Metoda statyczna nie działa w określonym wystąpieniu i jest błędem czasu kompilacji, aby odwołać się do this w metodzie statycznej.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.

Metoda wystąpienia działa w danym wystąpieniu klasy i można uzyskać do niego dostęp jako this (ten dostęp).An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

Gdy metoda jest przywoływana w member_access (dostęp do elementu członkowskiego) formularza E.M, jeśli M jest metodą statyczną, E musi zwrócić uwagę na typ zawierający M, a jeśli M jest metodą wystąpienia, E musi zwrócić uwagę na wystąpienie typu zawierającego 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.

Różnice między elementami statycznymi a składowymi wystąpienia są omówione bardziej szczegółowo w elementach członkowskich static i instance.The differences between static and instance members are discussed further in Static and instance members.

Metody wirtualneVirtual methods

Gdy deklaracja metody wystąpienia zawiera modyfikator virtual, ta metoda jest określana jako metoda wirtualna.When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. Gdy modyfikator virtual nie jest obecny, metoda jest uznawana za metodę niewirtualną.When no virtual modifier is present, the method is said to be a non-virtual method.

Implementacja metody niewirtualnej jest niezmienna: implementacja jest taka sama, niezależnie od tego, czy metoda jest wywoływana w wystąpieniu klasy, w której jest zadeklarowana, czy też wystąpieniem klasy pochodnej.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. W przeciwieństwie do implementacji metody wirtualnej można zastąpić klasy pochodne.In contrast, the implementation of a virtual method can be superseded by derived classes. Proces zastępowania implementacji dziedziczonej metody wirtualnej jest znany jako zastępowanie tej metody (metody zastępowania).The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

W wywołaniu metody wirtualnej, Typ czasu wykonywania wystąpienia, dla którego odbywa się wywołanie określa rzeczywistą implementację metody do wywołania.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. W wywołaniu metody niewirtualnej Typ czasu kompilacji wystąpienia jest czynnikiem decydującym.In a non-virtual method invocation, the compile-time type of the instance is the determining factor. W precyzyjnej terminologii, gdy metoda o nazwie N jest wywoływana z listą argumentów A w wystąpieniu z typem czasu kompilacji C i typem czasu wykonywania R (gdzie R jest C lub Klasa pochodna C), wywołanie jest przetwarzane w następujący sposób: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:

  • Najpierw rozwiązanie przeciążenia jest stosowane do C, Ni Aw celu wybrania konkretnej metody M z zestawu metod zadeklarowanych w i dziedziczonych przez C.First, overload resolution is applied to C, N, and A, to select a specific method M from the set of methods declared in and inherited by C. Jest to opisane w wywołaniu metody.This is described in Method invocations.
  • Następnie Jeśli M jest metodą niewirtualną, M jest wywoływana.Then, if M is a non-virtual method, M is invoked.
  • W przeciwnym razie M jest metodą wirtualną i zostanie wywołana najbardziej pochodna implementacja M w odniesieniu do R.Otherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

Dla każdej metody wirtualnej zadeklarowanej w lub dziedziczonej przez klasę istnieje najbardziej pochodna implementacja metody w odniesieniu do tej klasy.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. Najbardziej pochodna implementacja metody wirtualnej M w odniesieniu do klasy R jest określana w następujący sposób:The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • Jeśli R zawiera virtual deklaracji M, to najbardziej pochodna implementacja M.If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • W przeciwnym razie, jeśli R zawiera override M, to najbardziej pochodna implementacja M.Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • W przeciwnym razie najbardziej pochodna implementacja M w odniesieniu do R jest taka sama jak w przypadku najbardziej pochodnej implementacji M w odniesieniu do bezpośredniej klasy podstawowej R.Otherwise, the most derived implementation of M with respect to R is the same as the most derived implementation of M with respect to the direct base class of R.

Poniższy przykład ilustruje różnice między metodami wirtualnymi i niewirtualnymi: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();
    }
}

W przykładzie A wprowadza metodę niewirtualną F i Gmetodę wirtualną.In the example, A introduces a non-virtual method F and a virtual method G. Klasa B wprowadza nową metodę niewirtualną F, ukrywając dziedziczone F, a także przesłania metodę dziedziczenia G.The class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. Przykład generuje dane wyjściowe:The example produces the output:

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

Zwróć uwagę, że instrukcja a.G() wywołuje B.G, a nie A.G.Notice that the statement a.G() invokes B.G, not A.G. Wynika to z faktu, że typ czasu wykonywania wystąpienia (czyli B), a nie typ czasu kompilacji wystąpienia (czyli A), określa rzeczywistą implementację metody do wywołania.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.

Ponieważ metody są dozwolone do ukrycia metod dziedziczonych, istnieje możliwość, że Klasa zawiera kilka metod wirtualnych o tej samej sygnaturze.Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. Nie jest to problem niejednoznaczny, ponieważ wszystkie z nich oprócz metoda pochodna są ukryte.This does not present an ambiguity problem, since all but the most derived method are hidden. w przykładzieIn 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();
    }
}

klasy C i D zawierają dwie metody wirtualne o tej samej sygnaturze: wprowadzoną przez A i wprowadzoną przez 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. Metoda wprowadzona przez C ukrywa metodę dziedziczoną z A.The method introduced by C hides the method inherited from A. W związku z tym, deklaracja przesłonięcia w D przesłania metodę wprowadzoną przez Ci nie jest możliwe, aby D przesłonić metodę wprowadzoną przez 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. Przykład generuje dane wyjściowe:The example produces the output:

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

Należy zauważyć, że można wywołać ukrytą metodę wirtualną, uzyskując dostęp do wystąpienia D za pomocą mniej pochodnego typu, w którym metoda nie jest ukryta.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.

Metody przesłonięciaOverride methods

Gdy deklaracja metody wystąpienia zawiera modyfikator override, metoda jest określana jako Metoda przesłonięcia.When an instance method declaration includes an override modifier, the method is said to be an override method. Metoda przesłonięcia zastępuje dziedziczoną metodę wirtualną z tą samą sygnaturą.An override method overrides an inherited virtual method with the same signature. Podczas gdy deklaracja metody wirtualnej wprowadza nową metodę, Deklaracja metody przesłonięcia specjalizacji istniejącej dziedziczonej metody wirtualnej, dostarczając nową implementację tej metody.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.

Metoda zastąpiona przez deklarację override jest znana jako zastąpiona metoda podstawowa.The method overridden by an override declaration is known as the overridden base method. Dla metody przesłonięcia M zadeklarowanej w klasie C, zastąpiona metoda bazowa jest określana przez badanie każdego typu klasy bazowej C, rozpoczynając od bezpośredniego typu klasy bazowej C i kontynuując każdy kolejny bezpośredni typ klasy bazowej, do momentu w danym typie klasy bazowej, który ma taką samą sygnaturę jak M po podstawieniu argumentów typu.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. Na potrzeby lokalizowania zastąpionej metody bazowej Metoda jest uważana za dostępną, jeśli jest public, jeśli jest protected, jeśli jest protected internallub jeśli jest internal i zadeklarowana w tym samym programie jako 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.

Błąd czasu kompilacji występuje, chyba że wszystkie poniższe warunki są spełnione dla deklaracji przesłonięcia:A compile-time error occurs unless all of the following are true for an override declaration:

  • Zastąpiona metoda bazowa może być zlokalizowana zgodnie z powyższym opisem.An overridden base method can be located as described above.
  • Istnieje dokładnie jedna taka zastąpiona metoda bazowa.There is exactly one such overridden base method. To ograniczenie ma wpływ tylko wtedy, gdy typ klasy bazowej jest typem skonstruowanym, w którym Podstawienie argumentów typu powoduje, że sygnatura dwóch metod jest taka sama.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.
  • Zastąpiona metoda bazowa to metoda wirtualna, abstrakcyjna lub zastępowania.The overridden base method is a virtual, abstract, or override method. Inaczej mówiąc, zastąpiona metoda bazowa nie może być statyczna ani niewirtualna.In other words, the overridden base method cannot be static or non-virtual.
  • Zastąpiona metoda bazowa nie jest metodą zapieczętowana.The overridden base method is not a sealed method.
  • Metoda override i zastąpiona metoda bazowa mają ten sam typ zwracany.The override method and the overridden base method have the same return type.
  • Deklaracja przesłonięcia i zastąpiona metoda bazowa mają takie same zadeklarowane ułatwienia dostępu.The override declaration and the overridden base method have the same declared accessibility. Inaczej mówiąc, deklaracja przesłonięcia nie może zmienić dostępności metody wirtualnej.In other words, an override declaration cannot change the accessibility of the virtual method. Jednakże jeśli zastąpiona metoda bazowa jest chroniona wewnętrznie i jest zadeklarowana w innym zestawie niż zestaw zawierający metodę przesłonięcia, zadeklarowana dostępność metody override musi być chroniona.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.
  • Deklaracja przesłonięcia nie określa klauzul typu-Parameter-Constraints.The override declaration does not specify type-parameter-constraints-clauses. Zamiast tego ograniczenia są dziedziczone z przesłoniętej metody podstawowej.Instead the constraints are inherited from the overridden base method. Należy zauważyć, że ograniczenia, które są parametrami typu w zastąpionej metodzie, mogą zostać zastąpione przez argumenty typu w dziedziczonym ograniczeniu.Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. Może to prowadzić do ograniczeń, które nie są dozwolone, gdy jawnie określono, takich jak typy wartości lub typy zapieczętowane.This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

W poniższym przykładzie pokazano, w jaki sposób zastępujące reguły działają dla klas ogólnych: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>
}

Deklaracja przesłonięcia może uzyskać dostęp do zastąpionej metody bazowej przy użyciu base_access (dostęp podstawowy).An override declaration can access the overridden base method using a base_access (Base access). w przykładzieIn 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);
    }
}

Wywołanie base.PrintFields() w B wywołuje metodę PrintFields zadeklarowaną w A.the base.PrintFields() invocation in B invokes the PrintFields method declared in A. Base_access wyłącza mechanizm wywoływania wirtualnego i po prostu traktuje metodę podstawową jako metodę niewirtualną.A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. Miało to, że w B został zapisany ((A)this).PrintFields(), spowoduje cykliczne wywołanie metody PrintFields zadeklarowanej w B, a nie zadeklarowanej w A, ponieważ PrintFields jest wirtualną, a typem czasu wykonywania ((A)this) jest 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.

Tylko poprzez dołączenie modyfikatora override może zastąpić inną metodę.Only by including an override modifier can a method override another method. We wszystkich innych przypadkach Metoda o tej samej sygnaturze, jako dziedziczona metoda po prostu ukrywa metodę dziedziczenia.In all other cases, a method with the same signature as an inherited method simply hides the inherited method. w przykładzieIn the example

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

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

Metoda F w B nie zawiera modyfikatora override i w związku z tym nie przesłania metody F w A.the F method in B does not include an override modifier and therefore does not override the F method in A. Zamiast tego Metoda F w B ukrywa metodę w Ai zostanie zgłoszone ostrzeżenie, ponieważ deklaracja nie zawiera modyfikatora new.Rather, the F method in B hides the method in A, and a warning is reported because the declaration does not include a new modifier.

w przykładzieIn 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
}

Metoda F w B powoduje ukrycie metody F wirtualnej dziedziczonej z A.the F method in B hides the virtual F method inherited from A. Ponieważ nowy F w B ma dostęp prywatny, jego zakres zawiera tylko treść klasy B i nie rozszerzy się do 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. W związku z tym, deklaracja F w C może przesłonić F dziedziczone z A.Therefore, the declaration of F in C is permitted to override the F inherited from A.

Zapieczętowane metodySealed methods

Gdy deklaracja metody wystąpienia zawiera modyfikator sealed, ta metoda jest określana jako Metoda zapieczętowana.When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. Jeśli deklaracja metody wystąpienia zawiera modyfikator sealed, musi również zawierać modyfikator override.If an instance method declaration includes the sealed modifier, it must also include the override modifier. Użycie modyfikatora sealed zapobiega dalszemu zastępowaniu metody przez klasę pochodną.Use of the sealed modifier prevents a derived class from further overriding the method.

w przykładzieIn 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");
    } 
}

Klasa B udostępnia dwie metody przesłaniania: metodę F, która ma modyfikator sealed i metodę G, która nie.the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. Użycie zapieczętowanych modifier Buniemożliwia C dalsze przesłanianie F.B's use of the sealed modifier prevents C from further overriding F.

Metody abstrakcyjneAbstract methods

Gdy deklaracja metody wystąpienia zawiera modyfikator abstract, ta metoda jest uznawana za metodę abstrakcyjną.When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Chociaż metoda abstrakcyjna jest niejawnie również metodą wirtualną, nie może mieć modyfikatora virtual.Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

Deklaracja metody abstrakcyjnej wprowadza nową metodę wirtualną, ale nie zapewnia implementacji tej metody.An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Zamiast tego nieabstrakcyjne klasy pochodne są wymagane do zapewnienia własnych implementacji przez zastąpienie tej metody.Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Ponieważ metoda abstrakcyjna nie zapewnia rzeczywistej implementacji, method_body metody abstrakcyjnej składa się z średnika.Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

Deklaracje metody abstrakcyjnej są dozwolone tylko w klasach abstrakcyjnych (klasach abstrakcyjnych).Abstract method declarations are only permitted in abstract classes (Abstract classes).

w przykładzieIn 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);
    }
}

Klasa Shape definiuje abstrakcyjne pojęcie obiektu kształtu geometrycznego, który może narysować sam siebie.the Shape class defines the abstract notion of a geometrical shape object that can paint itself. Metoda Paint jest abstrakcyjna, ponieważ nie ma żadnej znaczącej implementacji domyślnej.The Paint method is abstract because there is no meaningful default implementation. Klasy Ellipse i Box to konkretne implementacje Shape.The Ellipse and Box classes are concrete Shape implementations. Ponieważ te klasy nie są abstrakcyjne, są one wymagane do przesłonięcia metody Paint i zapewnienia rzeczywistej implementacji.Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

Jest to błąd czasu kompilacji dla base_access (dostęp podstawowy), aby odwołać się do metody abstrakcyjnej.It is a compile-time error for a base_access (Base access) to reference an abstract method. w przykładzieIn the example

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

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

zgłoszono błąd czasu kompilacji dla wywołania base.F(), ponieważ odwołuje się do metody abstrakcyjnej.a compile-time error is reported for the base.F() invocation because it references an abstract method.

Deklaracja metody abstrakcyjnej może przesłonić metodę wirtualną.An abstract method declaration is permitted to override a virtual method. Dzięki temu Klasa abstrakcyjna może wymusić ponowną implementację metody w klasach pochodnych i powoduje, że oryginalna implementacja metody jest niedostępna.This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. w przykładzieIn 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");
    }
}

Klasa A deklaruje metodę wirtualną, Klasa B przesłania tę metodę za pomocą metody abstrakcyjnej, a Klasa C przesłania metodę abstrakcyjną w celu zapewnienia własnej implementacji.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.

Metody zewnętrzneExternal methods

Gdy deklaracja metody zawiera modyfikator extern, ta metoda jest określana jako Metoda zewnętrzna.When a method declaration includes an extern modifier, that method is said to be an external method. Metody zewnętrzne są implementowane zewnętrznie, zazwyczaj przy użyciu języka innego C#niż.External methods are implemented externally, typically using a language other than C#. Ponieważ Deklaracja metody zewnętrznej nie zapewnia żadnej rzeczywistej implementacji, method_body metody zewnętrznej składa się z średnika.Because an external method declaration provides no actual implementation, the method_body of an external method simply consists of a semicolon. Metoda zewnętrzna nie może być rodzajowa.An external method may not be generic.

Modyfikator extern jest zwykle używany w połączeniu z atrybutem DllImport (współdziałaniem ze składnikami com i Win32), umożliwiając Implementowanie zewnętrznych metod przy użyciu bibliotek DLL (bibliotek dołączanych dynamicznie).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). Środowisko wykonawcze może obsługiwać inne mechanizmy, w których można dostarczyć implementacje metod zewnętrznych.The execution environment may support other mechanisms whereby implementations of external methods can be provided.

Gdy metoda zewnętrzna zawiera atrybut DllImport, Deklaracja metody musi zawierać również modyfikator static.When an external method includes a DllImport attribute, the method declaration must also include a static modifier. Ten przykład ilustruje użycie modyfikatora extern i atrybutu DllImport:This example demonstrates the use of the extern modifier and the DllImport attribute:

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

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

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

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

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

Metody częściowe (podsumowanie)Partial methods (recap)

Gdy deklaracja metody zawiera modyfikator partial, ta metoda jest uznawana za metodę częściową.When a method declaration includes a partial modifier, that method is said to be a partial method. Metody częściowe mogą być deklarowane tylko jako elementy członkowskie typów częściowych (typy częściowe) i podlegają wielu ograniczeniom.Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. Metody częściowe są szczegółowo opisane w metodach częściowych.Partial methods are further described in Partial methods.

Metody rozszerzająceExtension methods

Gdy pierwszy parametr metody zawiera modyfikator this, ta metoda jest określana jako Metoda rozszerzenia.When the first parameter of a method includes the this modifier, that method is said to be an extension method. Metody rozszerzające mogą być deklarowane tylko w nieogólnych, niezagnieżdżonych klasach statycznych.Extension methods can only be declared in non-generic, non-nested static classes. Pierwszy parametr metody rozszerzenia nie może mieć modyfikatorów innych niż this, a typ parametru nie może być typem wskaźnika.The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

Poniżej znajduje się przykład klasy statycznej, która deklaruje dwie metody rozszerzające: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;
    }
}

Metoda rozszerzenia to zwykła metoda statyczna.An extension method is a regular static method. Ponadto, gdy jej otaczająca Klasa statyczna znajduje się w zakresie, Metoda rozszerzenia może być wywoływana przy użyciu składni wywołania metody wystąpienia (wywołania metody rozszerzenia), przy użyciu wyrażenia odbiorcy jako pierwszego argumentu.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.

Następujący program używa metod rozszerzających zadeklarowanych powyżej: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());
        }
    }
}

Metoda Slice jest dostępna na string[], a metoda ToInt32 jest dostępna na string, ponieważ zostały zadeklarowane jako metody rozszerzania.The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. Znaczenie programu jest takie samo jak w przypadku następujących wywołań metod statycznych: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));
        }
    }
}

Treść metodyMethod body

Method_body deklaracji metody składa się z treści bloku, treści wyrażenia lub średnika.The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

Typ wyniku metody jest void, jeśli typem zwracanym jest void, lub jeśli metoda jest Async i typem zwracanym jest 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. W przeciwnym razie typ wyniku metody nieasynchronicznej jest jego typem zwracanym, a typ wyniku metody asynchronicznej z typem zwracanym System.Threading.Tasks.Task<T> jest 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.

Gdy metoda ma void typ wyniku i treść bloku, instrukcje return (instrukcja return) w bloku nie mogą określać wyrażenia.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. Jeśli wykonywanie bloku metody void kończy się normalnie (to oznacza, że sterowanie przepływem poza końcem treści metody), ta metoda po prostu wraca do bieżącego obiektu wywołującego.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.

Gdy metoda ma void wynik i treść wyrażenia, wyrażenie E musi być statement_expression, a treść jest dokładnie równoważna treści bloku formularza { 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; }.

Gdy metoda ma typ wyniku inny niż void i treści bloku, każda instrukcja return w bloku musi określać wyrażenie, które jest niejawnie konwertowane na typ wyniku.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. Punkt końcowy treści bloku metody zwracającej wartość nie może być osiągalny.The endpoint of a block body of a value-returning method must not be reachable. Innymi słowy, w metodzie zwracającej wartość z treści bloku, sterowanie nie jest dozwolone do przepływu poza końcem treści metody.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.

Gdy metoda ma typ wyniku inny niż void i treść wyrażenia, wyrażenie musi być niejawnie konwertowane na typ wyniku, a treść jest dokładnie równoważna z treścią bloku formularza { 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; }.

w przykładzieIn 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;
}

Metoda F zwracająca wartość powoduje błąd czasu kompilacji, ponieważ kontrolka może przepływać poza końcem treści metody.the value-returning F method results in a compile-time error because control can flow off the end of the method body. Metody G i H są poprawne, ponieważ wszystkie możliwe ścieżki wykonywania kończą się instrukcją Return, która określa wartość zwracaną.The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. Metoda I jest poprawna, ponieważ jej treść jest równoważna z blokiem instrukcji z tylko jedną instrukcją Return.The I method is correct, because its body is equivalent to a statement block with just a single return statement in it.

Przeciążanie metodyMethod overloading

Reguły rozpoznawania przeciążenia metody są opisane w wnioskach o typie.The method overload resolution rules are described in Type inference.

WłaściwościProperties

Właściwość jest członkiem, który zapewnia dostęp do cech obiektu lub klasy.A property is a member that provides access to a characteristic of an object or a class. Przykłady właściwości obejmują długość ciągu znaków, rozmiar czcionki, podpis okna, nazwę klienta i tak dalej...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. Właściwości są naturalnym rozszerzeniem pól — obie są nazwanymi elementami członkowskimi ze skojarzonymi typami, a Składnia służąca do uzyskiwania dostępu do pól i właściwości jest taka sama.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. Jednak w przeciwieństwie do pól właściwości nie oznacza lokalizacji magazynu.However, unlike fields, properties do not denote storage locations. Zamiast tego właściwości mają metody dostępu określające instrukcje, które mają być wykonywane, gdy ich wartości są odczytywane lub zapisywane.Instead, properties have accessors that specify the statements to be executed when their values are read or written. W ten sposób właściwości zapewniają mechanizm kojarzenia akcji z odczytem i pisaniem atrybutów obiektu; Ponadto umożliwiają one obliczenia takich atrybutów.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.

Właściwości są deklarowane przy użyciu property_declarations:Properties are declared using property_declarations:

property_declaration
    : attributes? property_modifier* type member_name property_body
    ;

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

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

property_initializer
    : '=' variable_initializer ';'
    ;

Property_declaration może zawierać zestaw atrybutów (atrybutów) i prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu), new (nowy modyfikator), static (Metodastatyczna i wystąpienia), virtual (metody wirtualne), override (metody zastępowania), sealed (zapieczętowane metody), abstract (metody abstrakcyjne) i extern (metodyzewnętrzne) modyfikatory.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.

Deklaracje właściwości podlegają tym samym regułom, które są deklaracjami metod (metodami) w odniesieniu do prawidłowych kombinacji modyfikatorów.Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Typ deklaracji właściwości określa typ właściwości wprowadzonej przez deklarację, a MEMBER_NAME określa nazwę właściwości.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. Chyba że właściwość jest jawną implementacją składowej interfejsu, MEMBER_NAME jest po prostu identyfikatorem.Unless the property is an explicit interface member implementation, the member_name is simply an identifier. W przypadku jawnej implementacji elementu członkowskiego interfejsu (jawne implementacje elementu członkowskiego interfejsu) MEMBER_NAME składa się z interface_type po którym następuje "." i Identyfikator.For an explicit interface member implementation (Explicit interface member implementations), the member_name consists of an interface_type followed by a "." and an identifier.

Typ właściwości musi być co najmniej taki sam jak wartość właściwości (ograniczenia dostępności).The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Property_body może składać się z treści metody dostępu lub treści wyrażenia.A property_body may either consist of an accessor body or an expression body. W treści metody dostępu accessor_declarations, która musi być ujęta w tokeny "{" i "}", zadeklaruj metody dostępu (Akcesory) właściwości.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Metody dostępu określają instrukcję wykonywalną skojarzoną z odczytem i zapisem właściwości.The accessors specify the executable statements associated with reading and writing the property.

Treść wyrażenia składająca się z => po którym następuje wyrażenie E, a średnik jest dokładnie równoważny z treścią instrukcji { get { return E; } }i można go użyć tylko do określenia właściwości metody pobierającej, w której wynik metody pobierającej jest określony przez pojedyncze wyrażenie.An expression body consisting of => followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify getter-only properties where the result of the getter is given by a single expression.

Property_initializer można udzielić tylko dla automatycznie zaimplementowanej właściwości (automatycznie implementowane właściwości) i powoduje inicjalizację pola bazowego takich właściwości z wartością podaną przez wyrażenie.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.

Mimo że składnia uzyskiwania dostępu do właściwości jest taka sama jak w przypadku pola, właściwość nie jest sklasyfikowana jako zmienna.Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Dlatego nie jest możliwe przekazanie właściwości jako argumentu ref lub out.Thus, it is not possible to pass a property as a ref or out argument.

Gdy Deklaracja właściwości zawiera modyfikator extern, właściwość jest określana jako Właściwość zewnętrzna.When a property declaration includes an extern modifier, the property is said to be an external property. Ponieważ Deklaracja właściwości zewnętrznej nie zapewnia żadnej rzeczywistej implementacji, każda z jej accessor_declarations składa się z średnika.Because an external property declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Właściwości statyczne i wystąpieniaStatic and instance properties

Gdy Deklaracja właściwości zawiera modyfikator static, właściwość jest określana jako właściwość statyczna.When a property declaration includes a static modifier, the property is said to be a static property. Gdy modyfikator static nie jest obecny, właściwość jest określana jako Właściwość wystąpienia.When no static modifier is present, the property is said to be an instance property.

Właściwość statyczna nie jest skojarzona z określonym wystąpieniem i jest błędem czasu kompilacji, aby odwołać się do this w obiektach dostępu do właściwości statycznej.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.

Właściwość instance jest skojarzona z danym wystąpieniem klasy i można uzyskać do niego dostęp jako this (ten dostęp) w obiektach dostępu tej właściwości.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.

Gdy właściwość jest przywoływana w member_access (dostęp do elementu członkowskiego) formularza E.M, jeśli M jest właściwością statyczną, E musi zwrócić uwagę na typ zawierający M, a jeśli M jest właściwością wystąpienia, E musi zwrócić uwagę na wystąpienie typu zawierającego 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.

Różnice między elementami statycznymi a składowymi wystąpienia są omówione bardziej szczegółowo w elementach członkowskich static i instance.The differences between static and instance members are discussed further in Static and instance members.

Metod dostępuAccessors

Accessor_declarations właściwości określa instrukcje wykonywalne skojarzone z odczytem i zapisem tej właściwości.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
    | ';'
    ;

Deklaracje metody dostępu składają się z get_accessor_declaration, set_accessor_declarationlub obu tych elementów.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Każda deklaracja metody dostępu składa się z tokenu get lub set po nim opcjonalne accessor_modifier i accessor_body.Each accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

Użycie accessor_modifiers podlega następującym ograniczeniom:The use of accessor_modifiers is governed by the following restrictions:

  • Nie można użyć accessor_modifier w interfejsie lub w jawnej implementacji elementu członkowskiego interfejsu.An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • Dla właściwości lub indeksatora, który nie ma modyfikatora override, accessor_modifier jest dozwolony tylko wtedy, gdy właściwość lub indeksator ma metodę dostępu get i set, a następnie jest dozwolony tylko w jednym z tych metod dostępu.For a property or indexer that has no override modifier, an accessor_modifier is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors.
  • Dla właściwości lub indeksatora, który zawiera modyfikator override, metoda dostępu musi być zgodna z accessor_modifier, jeśli istnieje, do zastąpienia metody dostępu.For a property or indexer that includes an override modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden.
  • Accessor_modifier musi deklarować dostępność, która jest ściśle bardziej restrykcyjna niż zadeklarowana dostępność właściwości lub indeksatora.The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. Aby precyzyjnie:To be precise:
    • Jeśli właściwość lub indeksator ma zadeklarowaną dostępność public, accessor_modifier może być protected internal, internal, protectedlub private.If the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • Jeśli właściwość lub indeksator ma zadeklarowaną dostępność protected internal, accessor_modifier może być internal, protectedlub private.If the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • Jeśli właściwość lub indeksator ma zadeklarowaną dostępność internal lub protected, accessor_modifier musi być private.If the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • Jeśli właściwość lub indeksator ma zadeklarowaną dostępność private, nie można używać accessor_modifier .If the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

Dla właściwości abstract i extern accessor_body dla każdego określonego akcesora jest po prostu średnikiem.For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. Nieabstrakcyjna Właściwość niebędąca elementem zewnętrznym może accessor_body być średnikiem, w takim przypadku jest automatycznie implementowaną właściwością (automatycznie implementowane właściwości).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). Automatycznie implementowana właściwość musi mieć co najmniej metodę dostępu get.An automatically implemented property must have at least a get accessor. W przypadku akcesorów dowolnej innej nieabstrakcyjnej właściwości nieextern accessor_body jest blok , który określa instrukcje, które mają zostać wykonane po wywołaniu odpowiedniej metody dostępu.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.

Metoda dostępu get odpowiada metodzie bez parametrów z wartością zwracaną typu właściwości.A get accessor corresponds to a parameterless method with a return value of the property type. Poza elementem docelowym przypisania, gdy właściwość jest przywoływana w wyrażeniu, metoda dostępu get właściwości jest wywoływana, aby obliczyć wartość właściwości (wartości wyrażeń).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). Treść metody dostępu get musi być zgodna z regułami dotyczącymi metod zwracających wartość opisaną w treści metod.The body of a get accessor must conform to the rules for value-returning methods described in Method body. W szczególności wszystkie instrukcje return w treści metody dostępu get muszą określać wyrażenie, które jest niejawnie konwertowane na typ właściwości.In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Ponadto punkt końcowy metody dostępu get nie może być osiągalny.Furthermore, the endpoint of a get accessor must not be reachable.

Metoda dostępu set odnosi się do metody z parametrem pojedynczego wartości typu właściwości i void typem zwracanym.A set accessor corresponds to a method with a single value parameter of the property type and a void return type. Niejawny parametr metody dostępu set ma zawsze nazwę value.The implicit parameter of a set accessor is always named value. Gdy właściwość jest przywoływana jako element docelowy przypisania (Operatory przypisania), lub jako operand ++ lub -- (Operatory przyrostu i zmniejszaniawartości prefiksu), metoda dostępu set jest wywoływana z argumentem (którego wartość jest po prawej stronie przypisania lub operandem operatora ++ lub --), który zapewnia nową wartość (przypisanie proste).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). Treść metody dostępu set musi być zgodna z regułami dla void metod opisanymi w treści metod.The body of a set accessor must conform to the rules for void methods described in Method body. W szczególności instrukcje return w treści metody dostępu set nie mogą określać wyrażenia.In particular, return statements in the set accessor body are not permitted to specify an expression. Ponieważ metoda dostępu set niejawnie ma parametr o nazwie value, jest to błąd czasu kompilacji dla zmiennej lokalnej lub deklaracji stałej w metodzie dostępu set.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.

W oparciu o obecność lub brak get i metod dostępu set, właściwość jest sklasyfikowana w następujący sposób:Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • Właściwość, która zawiera zarówno metodę dostępu get, jak i metodę dostępu set, jest określana jako właściwość do odczytu i zapisu .A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • Właściwość, która ma tylko metodę dostępu get, jest określana jako właściwość tylko do odczytu .A property that has only a get accessor is said to be a read-only property. Jest to błąd czasu kompilacji dla właściwości tylko do odczytu, aby być elementem docelowym przypisania.It is a compile-time error for a read-only property to be the target of an assignment.
  • Właściwość, która ma tylko metodę dostępu set, jest określana jako właściwość tylko do zapisu .A property that has only a set accessor is said to be a write-only property. Poza elementem docelowym przypisania jest to błąd czasu kompilacji, który odwołuje się do właściwości tylko do zapisu w wyrażeniu.Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

w przykładzieIn 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
    }
}

formant Button deklaruje publiczną właściwość Caption.the Button control declares a public Caption property. Metoda dostępu get właściwości Caption zwraca ciąg przechowywany w prywatnym polu caption.The get accessor of the Caption property returns the string stored in the private caption field. Metoda dostępu set sprawdza, czy nowa wartość różni się od bieżącej wartości, a jeśli tak, przechowuje nową wartość i odmaluje formant.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. Właściwości często są zgodne ze wzorcem pokazanym powyżej: metoda dostępu get po prostu zwraca wartość przechowywaną w polu prywatnym, a metoda dostępu set modyfikuje to pole prywatne, a następnie wykonuje wszelkie dodatkowe akcje wymagane do pełnej aktualizacji stanu obiektu.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.

W przypadku powyższej klasy Button poniżej przedstawiono przykład użycia właściwości Caption: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

W tym miejscu metoda dostępu set jest wywoływana przez przypisanie wartości do właściwości, a metoda dostępu get jest wywoływana przez odwołanie do właściwości w wyrażeniu.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.

Metody dostępu get i set właściwości nie są odrębnymi składowymi i nie można deklarować metod dostępu dla właściwości oddzielnie.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. W związku z tym nie jest możliwe, aby dwie metody dostępu do odczytu i zapisu miały różne ułatwienia dostępu.As such, it is not possible for the two accessors of a read-write property to have different accessibility. PrzykładThe 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; }
    }
}

nie deklaruje pojedynczej właściwości do odczytu i zapisu.does not declare a single read-write property. Zamiast tego deklaruje dwie właściwości o tej samej nazwie, jeden tylko do odczytu i jeden tylko do zapisu.Rather, it declares two properties with the same name, one read-only and one write-only. Ponieważ dwa elementy członkowskie zadeklarowane w tej samej klasie nie mogą mieć takiej samej nazwy, przykład powoduje błąd w czasie kompilacji.Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

Gdy Klasa pochodna deklaruje właściwość o takiej samej nazwie jak dziedziczona właściwość, właściwość pochodna ukrywa dziedziczonej właściwości w odniesieniu do odczytu i zapisu.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. w przykładzieIn the example

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

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

Właściwość P w B ukrywa właściwość P w A w odniesieniu do odczytu i zapisu.the P property in B hides the P property in A with respect to both reading and writing. W tym celu w instrukcjachThus, 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

przypisanie do b.P powoduje zgłoszenie błędu czasu kompilacji, ponieważ właściwość P tylko do odczytu w B ukrywa właściwość P tylko do zapisu w A.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. Należy jednak zauważyć, że rzutowanie może być używane w celu uzyskania dostępu do właściwości Hidden P.Note, however, that a cast can be used to access the hidden P property.

W przeciwieństwie do pól publicznych, właściwości zapewniają rozdzielenie między wewnętrznym stanem obiektu a jego interfejsem publicznym.Unlike public fields, properties provide a separation between an object's internal state and its public interface. Rozważmy przykład: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; }
    }
}

W tym miejscu Klasa Label używa dwóch int pól, x i ydo przechowywania jej lokalizacji.Here, the Label class uses two int fields, x and y, to store its location. Lokalizacja jest publicznie ujawniana zarówno jako X, jak i Właściwość Y i jako właściwość Location typu Point.The location is publicly exposed both as an X and a Y property and as a Location property of type Point. Jeśli w przyszłej wersji Labelbędzie bardziej wygodne przechowywanie lokalizacji jako Point wewnętrznie, zmiana może zostać wprowadzona bez wpływu na publiczny interfejs klasy: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; }
    }
}

Miało x i y zamiast tego zostały public readonly pola, nie byłoby możliwe dokonanie takiej zmiany w klasie Label.Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

Ujawnienie stanu za poorednictwem właściwości nie musi być mniej wydajne niż bezpośrednie udostępnianie pól.Exposing state through properties is not necessarily any less efficient than exposing fields directly. W szczególności, gdy właściwość nie jest wirtualna i zawiera tylko niewielką ilość kodu, środowisko wykonawcze może zastępować wywołania metod dostępu przy użyciu rzeczywistego kodu metod dostępu.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. Ten proces jest nazywany dekreśleniemi sprawia, że dostęp do właściwości jest skuteczny jako dostęp do pola, a jeszcze zachowuje zwiększoną elastyczność właściwości.This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Ponieważ wywoływanie metody dostępu get jest koncepcyjnie równoważne odczytywaniu wartości pola, jest uznawany za zły styl programowania dla metod dostępu get, aby mieć zauważalne efekty uboczne.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. w przykładzieIn the example

class Counter
{
    private int next;

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

wartość właściwości Next zależy od tego, ile razy uzyskano dostęp do właściwości.the value of the Next property depends on the number of times the property has previously been accessed. W efekcie uzyskanie dostępu do właściwości daje zauważalny efekt uboczny, a właściwość powinna zostać zaimplementowana jako metoda.Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

Konwencja "brak efektów ubocznych" dla metod dostępu get nie oznacza, że metody dostępu get powinny zawsze być zapisane, aby po prostu zwracały wartości przechowywane w polach.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. W rzeczywistości metody dostępu get często obliczają wartość właściwości poprzez dostęp do wielu pól lub wywoływanie metod.Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. Jednak prawidłowo zaprojektowany get metoda dostępu nie wykonuje żadnych działań, które powodują zauważalne zmiany stanu obiektu.However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

Właściwości można użyć, aby opóźnić inicjalizację zasobu do momentu, w którym jest ono najpierw przywoływane.Properties can be used to delay initialization of a resource until the moment it is first referenced. Na przykład: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;
        }
    }
}

Klasa Console zawiera trzy właściwości, In, Outi Error, które reprezentują odpowiednio standardowe dane wejściowe, wyjściowe i błędy.The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. Przez udostępnienie tych elementów członkowskich jako właściwości, Klasa Console może opóźnić ich inicjalizację do momentu ich faktycznego użycia.By exposing these members as properties, the Console class can delay their initialization until they are actually used. Na przykład po pierwszym odwołaniu się do właściwości Out, jak wFor example, upon first referencing the Out property, as in

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

zostanie utworzony podstawowy TextWriter urządzenia wyjściowego.the underlying TextWriter for the output device is created. Jeśli jednak aplikacja nie tworzy żadnych odwołań do właściwości In i Error, wówczas żadne obiekty nie są tworzone dla tych urządzeń.But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

Automatycznie implementowane właściwościAutomatically implemented properties

Automatycznie implementowana Właściwość (lub Właściwość automatyczna dla krótkich) jest nieabstrakcyjną właściwością nieextern z tylko średnikami.An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Funkcja autowłaściwości musi mieć metodę dostępu get i opcjonalnie może mieć metodę dostępu set.Auto-properties must have a get accessor and can optionally have a set accessor.

Gdy właściwość jest określona jako automatycznie implementowana właściwość, ukryte pole zapasowe jest automatycznie dostępne dla właściwości, a metody dostępu są implementowane w celu odczytu i zapisu w tym polu zapasowym.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. Jeśli właściwość autoproperty nie ma metody dostępu set, pole zapasowe jest uznawane za readonly (pola tylko do odczytu).If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Podobnie jak w przypadku pola readonly, metoda pobierająca, która jest jedyną właściwością, może również być przypisana do w treści konstruktora klasy otaczającej.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. Takie przypisanie przypisuje bezpośrednio do pola zapasowego tylko do odczytu właściwości.Such an assignment assigns directly to the readonly backing field of the property.

Właściwość autoproperty może opcjonalnie mieć property_initializer, która jest stosowana bezpośrednio do pola zapasowego jako variable_initializer (inicjatory zmiennych).An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

Poniższy przykład:The following example:

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

jest równoważne następującej deklaracji: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; } }
}

Poniższy przykład:The following example:

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

jest równoważne następującej deklaracji: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; }
}

Zwróć uwagę, że przypisania do pola tylko do odczytu są dozwolone, ponieważ występują w konstruktorze.Notice that the assignments to the readonly field are legal, because they occur within the constructor.

Ułatwienia dostępuAccessibility

Jeśli metoda dostępu ma accessor_modifier, domena dostępności (domeny dostępu) metody dostępu jest określana przy użyciu deklarowanej dostępności accessor_modifier.If an accessor has an accessor_modifier, the accessibility domain (Accessibility domains) of the accessor is determined using the declared accessibility of the accessor_modifier. Jeśli metoda dostępu nie ma accessor_modifier, domena dostępności metody dostępu jest określana na podstawie zadeklarowanej dostępności właściwości lub indeksatora.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.

Obecność accessor_modifier nie ma wpływu na wyszukiwanie elementów członkowskich (operatorów) ani rozpoznawanie przeciążenia (rozwiązanie przeciążenia).The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). Modyfikatory właściwości lub indeksatora zawsze określają, która właściwość lub indeksator jest powiązany, niezależnie od kontekstu dostępu.The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

Po wybraniu określonej właściwości lub indeksatora domeny dostępności konkretnych metod dostępu są używane do określenia, czy to użycie jest prawidłowe: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:

W poniższym przykładzie właściwość A.Text jest ukryta przez właściwość B.Text, nawet w kontekstach, w których wywoływana jest tylko metoda dostępu set.In the following example, the property A.Text is hidden by the property B.Text, even in contexts where only the set accessor is called. Z kolei Właściwość B.Count nie jest dostępna dla klasy M, więc zamiast niej zostanie użyta Właściwość dostępna A.Count.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
    }
}

Metoda dostępu użyta do zaimplementowania interfejsu może nie mieć accessor_modifier.An accessor that is used to implement an interface may not have an accessor_modifier. Jeśli do zaimplementowania interfejsu jest używana tylko jedna metoda dostępu, inne metody dostępu można zadeklarować za pomocą accessor_modifier:If only one accessor is used to implement an interface, the other accessor may be declared with an accessor_modifier:

public interface I
{
    string Prop { get; }
}

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

Metody dostępu do właściwości Virtual, Sealed, override i abstractVirtual, sealed, override, and abstract property accessors

Deklaracja właściwości virtual określa, że metody dostępu do właściwości są wirtualne.A virtual property declaration specifies that the accessors of the property are virtual. Modyfikator virtual ma zastosowanie do obu metod dostępu właściwości do odczytu i zapisu — nie jest to możliwe tylko w przypadku jednej metody dostępu do odczytu i zapisu, która ma być wirtualna.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.

Deklaracja właściwości abstract określa, że metody dostępu do właściwości są wirtualne, ale nie zapewniają rzeczywistej implementacji metod dostępu.An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. Zamiast tego nieabstrakcyjne klasy pochodne są wymagane do zapewnienia własnych implementacji dla metod dostępu poprzez Zastępowanie właściwości.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Ponieważ metoda dostępu dla deklaracji właściwości abstrakcyjnej nie oferuje rzeczywistej implementacji, jej accessor_body po prostu składa się z średnika.Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

Deklaracja właściwości, która zawiera zarówno Modyfikatory abstract, jak i override określa, że właściwość jest abstrakcyjna i przesłania Właściwość bazową.A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Metody dostępu takich właściwości są również abstrakcyjne.The accessors of such a property are also abstract.

Deklaracje właściwości abstrakcyjnych są dozwolone tylko w klasach abstrakcyjnych (klasach abstrakcyjnych). Metody dostępu dziedziczonej właściwości wirtualnej można zastąpić w klasie pochodnej przez dołączenie deklaracji właściwości, która określa override dyrektywę.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. Jest to tzw. zastępowanie deklaracji właściwości.This is known as an overriding property declaration. Zastępowanie deklaracji właściwości nie deklaruje nowej właściwości.An overriding property declaration does not declare a new property. Zamiast tego po prostu wyspecjalizowane są implementacje metod dostępu istniejącej właściwości wirtualnej.Instead, it simply specializes the implementations of the accessors of an existing virtual property.

Zastępowanie deklaracji właściwości musi określać dokładnie te same Modyfikatory dostępności, typ i nazwę, co Właściwość dziedziczona.An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. Jeśli dziedziczona właściwość ma tylko jedną metodę dostępu (tj., jeśli dziedziczona właściwość jest tylko do odczytu lub tylko do zapisu), właściwość zastępująca musi zawierać tylko ten akcesor.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. Jeśli dziedziczona Właściwość zawiera obie metody dostępu (tj., jeśli dziedziczona właściwość jest do odczytu i zapisu), zastępujący właściwość może zawierać jedną metodę dostępu lub obu akcesorów.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.

Zastępowanie deklaracji właściwości może zawierać modyfikator sealed.An overriding property declaration may include the sealed modifier. Użycie tego modyfikatora uniemożliwia klasie pochodnej bardziej Zastępowanie właściwości.Use of this modifier prevents a derived class from further overriding the property. Metody dostępu właściwości zapieczętowanej są również zapieczętowane.The accessors of a sealed property are also sealed.

Z wyjątkiem różnic w składni deklaracji i wywołania, Virtual, Sealed, override i abstract metody dostępu zachowują się dokładnie tak, jak wirtualne, zapieczętowane, przesłonięcie i abstrakcyjne.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Zgodnie z zasadami opisanymi w metodach wirtualnych, metodami przesłonięcia, metodami zapieczętowanymii metodami abstrakcyjnymi stosuje się tak, jakby metody dostępu były metodami odpowiedniej formy:Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • Metoda dostępu get odpowiada metodzie bez parametrów z wartością zwracaną typu właściwości i tymi samymi modyfikatorami co Właściwość zawierająca.A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • Metoda dostępu set odnosi się do metody z parametrem pojedynczego wartości typu właściwości, void typem zwracanym i tymi samymi modyfikatorami co Właściwość zawierająca.A set accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property.

w przykładzieIn 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 jest wirtualną właściwość tylko do odczytu, Y jest wirtualną właściwością odczytu i zapisu, a Z jest abstrakcyjną właściwością do odczytu i zapisu.X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. Ponieważ Z jest abstrakcyjna, A zawierającej klasy również musi być zadeklarowany jako abstract.Because Z is abstract, the containing class A must also be declared abstract.

Poniżej przedstawiono klasę, która dziedziczy z A:A class that derives from A is show below:

class B: A
{
    int z;

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

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

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

W tym miejscu deklaracje X, Yi Z zastępują deklaracje właściwości.Here, the declarations of X, Y, and Z are overriding property declarations. Każda deklaracja właściwości dokładnie dopasowuje Modyfikatory dostępności, typ i nazwę odpowiedniej dziedziczonej właściwości.Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. Metoda dostępu get X i metoda dostępu set Y używają słowa kluczowego base, aby uzyskać dostęp do dziedziczonych akcesorów.The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. Deklaracja Z przesłania zarówno abstrakcyjnych metod dostępu, w tym, że nie istnieją żadne oczekujące składowe funkcji abstrakcyjnych w B, a B może być klasą abstrakcyjną.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.

Gdy właściwość jest zadeklarowana jako override, wszystkie zastąpione metody dostępu muszą być dostępne dla zastępujący kod.When a property is declared as an override, any overridden accessors must be accessible to the overriding code. Ponadto zadeklarowana dostępność zarówno właściwości, jak i indeksatora, jak i metod dostępu musi być zgodna z przesłoniętą składową i dostępem.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. Na przykład: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
    }
}

ZdarzeniaEvents

Zdarzenie jest członkiem, który umożliwia obiektowi lub klasy udostępnianie powiadomień.An event is a member that enables an object or class to provide notifications. Klienci mogą dołączyć kod wykonywalny zdarzeń przez dostarczenie obsługi zdarzeń.Clients can attach executable code for events by supplying event handlers.

Zdarzenia są deklarowane przy użyciu event_declarations:Events are declared using event_declarations:

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

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

event_accessor_declarations
    : add_accessor_declaration remove_accessor_declaration
    | remove_accessor_declaration add_accessor_declaration
    ;

add_accessor_declaration
    : attributes? 'add' block
    ;

remove_accessor_declaration
    : attributes? 'remove' block
    ;

Event_declaration może zawierać zestaw atrybutów (atrybutów) i prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu), new (nowy modyfikator), static (Metodastatyczna i wystąpienia), virtual (metody wirtualne), override (metody zastępowania), sealed (zapieczętowane metody), abstract (metody abstrakcyjne) i extern (metodyzewnętrzne) modyfikatory.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.

Deklaracje zdarzeń podlegają tym samym regułom, które są deklaracjami metod (metodami) w odniesieniu do prawidłowych kombinacji modyfikatorów.Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Typ deklaracji zdarzenia musi być delegate_type (typy referencyjne), a delegate_type musi być co najmniej tak samo jak w przypadku samego zdarzenia (ograniczenia dostępności).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).

Deklaracja zdarzenia może zawierać event_accessor_declarations.An event declaration may include event_accessor_declarations. Jednakże jeśli nie, dla zdarzeń niezewnętrznych, kompilator dostarcza je automatycznie (zdarzenia podobne do pól); dla zdarzeń extern, metody dostępu są udostępniane zewnętrznie.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.

Deklaracja zdarzenia, która pomija event_accessor_declarations definiuje jedno lub więcej zdarzeń — jeden dla każdej variable_declarators.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarators. Atrybuty i Modyfikatory mają zastosowanie do wszystkich elementów członkowskich zadeklarowanych przez takie event_declaration.The attributes and modifiers apply to all of the members declared by such an event_declaration.

Jest to błąd czasu kompilacji dla event_declaration , aby uwzględnić zarówno modyfikator abstract, jak i rozdzieloną event_accessor_declarationsnawiasów klamrowych.It is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

Gdy deklaracja zdarzenia zawiera modyfikator extern, zdarzenie jest określane jako zdarzenie zewnętrzne.When an event declaration includes an extern modifier, the event is said to be an external event. Ponieważ deklaracja zdarzenia zewnętrznego nie oferuje rzeczywistej implementacji, jest to błąd, aby uwzględnić zarówno modyfikator extern, jak i event_accessor_declarations.Because an external event declaration provides no actual implementation, it is an error for it to include both the extern modifier and event_accessor_declarations.

Jest to błąd czasu kompilacji dla variable_declarator deklaracji zdarzenia z modyfikatorem abstract lub external w celu uwzględnienia variable_initializer.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.

Zdarzenia można użyć jako lewego operandu operatorów += i -= (przypisanie zdarzenia).An event can be used as the left-hand operand of the += and -= operators (Event assignment). Te operatory służą odpowiednio do dołączania obsługi zdarzeń do lub usuwania programów obsługi zdarzeń ze zdarzenia, a także Modyfikatory dostępu dla zdarzenia kontrolują konteksty, w których takie operacje są dozwolone.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.

Ponieważ += i -= są jedynymi operacjami, które są dozwolone dla zdarzenia poza typem, który deklaruje zdarzenie, kod zewnętrzny może dodawać i usuwać programy obsługi dla zdarzenia, ale nie może w żaden inny sposób uzyskać ani zmodyfikować podstawowej listy programów obsługi zdarzeń.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.

W operacji x += y lub x -= y, gdy x jest zdarzeniem, a odwołanie ma miejsce poza typem, który zawiera deklarację x, wynik operacji ma typ void (w przeciwieństwie do typu x, z wartością x po przypisaniu).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). Ta zasada zabrania kodowi zewnętrznemu przeanalizować bazowego delegata zdarzenia.This rule prohibits external code from indirectly examining the underlying delegate of an event.

Poniższy przykład pokazuje, jak programy obsługi zdarzeń są dołączone do wystąpień klasy 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
    }
}

W tym miejscu Konstruktor wystąpienia LoginDialog tworzy dwa wystąpienia Button i dołącza obsługę zdarzeń do zdarzeń Click.Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

Zdarzenia podobne do pólField-like events

W tekście programu klasy lub struktury, która zawiera deklarację zdarzenia, można użyć określonych zdarzeń, takich jak pola.Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. Do użycia w ten sposób, zdarzenie nie może być abstract ani externi nie może jawnie zawierać event_accessor_declarations.To be used in this way, an event must not be abstract or extern, and must not explicitly include event_accessor_declarations. Takiego zdarzenia można użyć w dowolnym kontekście, który zezwala na pole.Such an event can be used in any context that permits a field. Pole zawiera delegata (delegatów), który odwołuje się do listy programów obsługi zdarzeń, które zostały dodane do zdarzenia.The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. Jeśli nie dodano żadnych programów obsługi zdarzeń, pole zawiera null.If no event handlers have been added, the field contains null.

w przykładzieIn 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 jest używany jako pole w klasie Button.Click is used as a field within the Button class. Jak pokazano w przykładzie, pole może być badane, modyfikowane i używane w wyrażeniach delegatów wywołań.As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. Metoda OnClick w klasie Button "wywołuje" Click zdarzenie.The OnClick method in the Button class "raises" the Click event. Pojęcie związane z wywoływaniem zdarzenia jest dokładnie równoważne do wywołania delegata reprezentowanego przez zdarzenie, co oznacza, że nie istnieją żadne specjalne konstrukcje języka do wywoływania zdarzeń.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. Należy zauważyć, że wywołanie delegata jest poprzedzone przez sprawdzenie, czy delegat ma wartość różną od null.Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Poza deklaracją klasy Button, element członkowski Click może być używany tylko po lewej stronie operatorów += i -=, jak wOutside 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(...);

dołącza delegata do listy wywołań zdarzenia Click iwhich appends a delegate to the invocation list of the Click event, and

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

który usuwa delegata z listy wywołań zdarzenia Click.which removes a delegate from the invocation list of the Click event.

Podczas kompilowania zdarzenia przypominającego pole, kompilator automatycznie tworzy magazyn do przechowywania delegata i tworzy metody dostępu dla zdarzenia, które dodaje lub usuwa programy obsługi zdarzeń do pola delegat.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. Operacje dodawania i usuwania są bezpieczne dla wątków i mogą (ale nie muszą) być wykonywane podczas utrzymywania blokady (instrukcja Lock) na obiekcie zawierającym zdarzenie wystąpienia lub obiekt typu (wyrażenia tworzenia obiektów anonimowych) dla zdarzenia statycznego.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.

W rezultacie deklaracja zdarzenia wystąpienia formularza:Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

zostanie skompilowany do dowolnego elementu równoważnego: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 */
        }
    }
}

W ramach klasy Xodwołania do Ev po lewej stronie operatorów += i -= powodują wywoływanie metod dodawania i usuwania.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. Wszystkie inne odwołania do Ev są kompilowane w celu odwoływania się do pola ukrytego __Ev zamiast tego (dostęp do elementów członkowskich).All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). Nazwa "__Ev" jest arbitralna; pole ukryte może mieć dowolną nazwę lub Brak nazwy.The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

Metody dostępu zdarzeńEvent accessors

Deklaracje zdarzeń zwykle pomijają event_accessor_declarations, tak jak w powyższym przykładzie Button.Event declarations typically omit event_accessor_declarations, as in the Button example above. Jedną z sytuacji, w której należy to zrobić, jest sytuacja, w której koszt magazynowania jednego pola na zdarzenie nie jest akceptowalny.One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. W takich przypadkach Klasa może zawierać event_accessor_declarations i używać mechanizmu prywatnego do przechowywania listy programów obsługi zdarzeń.In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

Event_accessor_declarations zdarzenia określa instrukcje wykonywalne skojarzone z dodawaniem i usuwaniem obsługi zdarzeń.The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

Deklaracje metody dostępu składają się z add_accessor_declaration i remove_accessor_declaration.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Każda deklaracja metody dostępu składa się z tokenu add lub remove, po którym następuje blok.Each accessor declaration consists of the token add or remove followed by a block. Blok skojarzony z add_accessor_declaration określa instrukcje do wykonania po dodaniu programu obsługi zdarzeń, a blok skojarzony z remove_accessor_declaration określa instrukcje do wykonania po usunięciu programu obsługi zdarzeń.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.

Każda add_accessor_declaration i remove_accessor_declaration odnosi się do metody z parametrem pojedynczego wartości typu zdarzenia i void typem zwracanym.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. Niejawny parametr metody dostępu do zdarzenia nosi nazwę value.The implicit parameter of an event accessor is named value. Gdy zdarzenie jest używane w przypisaniu zdarzenia, zostanie użyta odpowiednia metoda dostępu do zdarzeń.When an event is used in an event assignment, the appropriate event accessor is used. W odróżnieniu od tego, czy operator przypisania jest +=, używana jest metoda Add akcesor, a jeśli operator przypisania jest -=, zostanie użyta metoda dostępu Remove.Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is -= then the remove accessor is used. W obu przypadkach jest używany operand z prawej strony operatora przypisania jako argument metody dostępu do zdarzeń.In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. Blok add_accessor_declaration lub remove_accessor_declaration musi być zgodny z regułami void metodami opisanymi w treści metody.The block of an add_accessor_declaration or a remove_accessor_declaration must conform to the rules for void methods described in Method body. W szczególności instrukcje return w takich blokach nie mogą określać wyrażenia.In particular, return statements in such a block are not permitted to specify an expression.

Ponieważ metoda dostępu do zdarzenia niejawnie ma parametr o nazwie value, jest to błąd czasu kompilacji dla zmiennej lokalnej lub stała zadeklarowana w metodzie dostępu do zdarzeń, która ma tę nazwę.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.

w przykładzieIn 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);
    }
}

Klasa Control implementuje mechanizm wewnętrznego magazynu dla zdarzeń.the Control class implements an internal storage mechanism for events. Metoda AddEventHandler kojarzy wartość delegata z kluczem, Metoda GetEventHandler zwraca delegata aktualnie skojarzony z kluczem, a metoda RemoveEventHandler usuwa delegata jako procedurę obsługi zdarzeń dla określonego zdarzenia.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. Zakładając, że podstawowy mechanizm magazynowania jest w taki sposób, że nie ma żadnego kosztu kojarzenia wartości delegata null z kluczem, a w związku z tym nieobsłużone zdarzenia nie zużywają magazynu.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.

Zdarzenia statyczne i wystąpieniaStatic and instance events

Gdy deklaracja zdarzenia zawiera modyfikator static, zdarzenie jest określane jako zdarzenie statyczne.When an event declaration includes a static modifier, the event is said to be a static event. Gdy static modyfikator nie jest obecny, zdarzenie jest określane jako zdarzenie wystąpienia.When no static modifier is present, the event is said to be an instance event.

Zdarzenie statyczne nie jest skojarzone z określonym wystąpieniem i jest błędem czasu kompilowania, który odwołuje się do this w obiektach dostępu do statycznego zdarzenia.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.

Zdarzenie wystąpienia jest skojarzone z danym wystąpieniem klasy i można uzyskać do niego dostęp jako this (ten dostęp) w odniesieniu do tego zdarzenia.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.

Gdy do zdarzenia odwołuje się member_access (dostęp do elementu członkowskiego) formularza E.M, jeśli M jest zdarzeniem statycznym, E musi zwrócić uwagę na typ zawierający M, a jeśli M jest zdarzenie wystąpienia, E musi zwrócić uwagę na wystąpienie typu zawierającego 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.

Różnice między elementami statycznymi a składowymi wystąpienia są omówione bardziej szczegółowo w elementach członkowskich static i instance.The differences between static and instance members are discussed further in Static and instance members.

Wirtualne, zapieczętowane, przesłonięcie i abstrakcyjne metody dostępu do zdarzeńVirtual, sealed, override, and abstract event accessors

Deklaracja zdarzenia virtual określa, że metody dostępu tego zdarzenia są wirtualne.A virtual event declaration specifies that the accessors of that event are virtual. Modyfikator virtual ma zastosowanie do obu metod dostępu zdarzenia.The virtual modifier applies to both accessors of an event.

Deklaracja zdarzenia abstract określa, że metody dostępu do zdarzenia są wirtualne, ale nie zapewniają rzeczywistej implementacji metod dostępu.An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. Zamiast tego nieabstrakcyjne klasy pochodne są wymagane do zapewnienia własnych implementacji dla metod dostępu przez zastąpienie zdarzenia.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Ze względu na to, że deklaracja zdarzenia abstrakcyjnego nie zawiera rzeczywistej implementacji, nie może zawierać event_accessor_declarationsrozdzielonych nawiasami klamrowymi.Because an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

Deklaracja zdarzenia, która zawiera zarówno Modyfikatory abstract, jak i override określa, że zdarzenie jest abstrakcyjne i przesłania zdarzenie podstawowe.An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. Metody dostępu takiego zdarzenia są również abstrakcyjne.The accessors of such an event are also abstract.

Deklaracje zdarzeń abstrakcyjnych są dozwolone tylko w klasach abstrakcyjnych (klasach abstrakcyjnych).Abstract event declarations are only permitted in abstract classes (Abstract classes).

Metody dostępu dziedziczonego zdarzenia wirtualnego można zastąpić w klasie pochodnej przez dołączenie deklaracji zdarzenia, która określa modyfikator 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. Jest to tzw. zastępowanie deklaracji zdarzenia.This is known as an overriding event declaration. Zastępowanie deklaracji zdarzenia nie deklaruje nowego zdarzenia.An overriding event declaration does not declare a new event. Zamiast tego po prostu wyspecjalizowane są implementacje metod dostępu istniejącego zdarzenia wirtualnego.Instead, it simply specializes the implementations of the accessors of an existing virtual event.

Zastępowanie deklaracji zdarzenia musi określać dokładnie te same Modyfikatory dostępności, typ i nazwę, co zastąpione zdarzenie.An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

Zastępowanie deklaracji zdarzenia może zawierać modyfikator sealed.An overriding event declaration may include the sealed modifier. Użycie tego modyfikatora zapobiega dalszemu zastępowaniu zdarzenia przez klasę pochodną.Use of this modifier prevents a derived class from further overriding the event. Metody dostępu zapieczętowanego zdarzenia są również zapieczętowane.The accessors of a sealed event are also sealed.

Jest to błąd czasu kompilacji dla zastępujący deklaracji zdarzenia w celu uwzględnienia modyfikatora new.It is a compile-time error for an overriding event declaration to include a new modifier.

Z wyjątkiem różnic w składni deklaracji i wywołania, Virtual, Sealed, override i abstract metody dostępu zachowują się dokładnie tak, jak wirtualne, zapieczętowane, przesłonięcie i abstrakcyjne.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Zgodnie z zasadami opisanymi w metodach wirtualnych, metodami przesłonięcia, metodami zapieczętowanymii metodami abstrakcyjnymi stosuje się tak, jakby metody dostępu były metodami odpowiedniego formularza.Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form. Każdy akcesor odnosi się do metody z parametrem jednowartościowym typu zdarzenia, void zwracanym typem i tego samego modyfikatora co zawiera zdarzenie.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.

IndeksatoryIndexers

Indeksator jest członkiem, który umożliwia indeksowanie obiektu w taki sam sposób jak tablica.An indexer is a member that enables an object to be indexed in the same way as an array. Indeksatory są deklarowane przy użyciu indexer_declarations:Indexers are declared using indexer_declarations:

indexer_declaration
    : attributes? indexer_modifier* indexer_declarator indexer_body
    ;

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

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

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

Indexer_declaration może zawierać zestaw atrybutów (atrybutów) i prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu), new (nowy modyfikator), virtual (metody wirtualne), override (Zastąp metody), sealed (zapieczętowane metody), abstract (metody abstrakcyjne) i extern (metody zewnętrzne) modyfikatory.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.

Deklaracje indeksatora podlegają tym samym zasadom, które są deklaracjami metod (metodami) w odniesieniu do prawidłowych kombinacji modyfikatorów, z wyjątkiem tego, że modyfikator static nie jest dozwolony w deklaracji indeksatora.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.

Modyfikatory virtual, overridei abstract wzajemnie się wykluczają, chyba że w jednym przypadku.The modifiers virtual, override, and abstract are mutually exclusive except in one case. Modyfikatory abstract i override mogą być używane razem, aby abstrakcyjny indeksator mógł zastąpić wirtualne.The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

Typ deklaracji indeksatora określa typ elementu indeksatora wprowadzonego przez deklarację.The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. Jeśli indeksator nie jest jawną implementacją składowej interfejsu, następuje słowo kluczowe this.Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. Dla jawnej implementacji elementu członkowskiego interfejsu Typ następuje interface_type, "." i słowa kluczowego this.For an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. W przeciwieństwie do innych elementów członkowskich indeksatory nie mają nazw zdefiniowanych przez użytkownika.Unlike other members, indexers do not have user-defined names.

Formal_parameter_list określa parametry indeksatora.The formal_parameter_list specifies the parameters of the indexer. Lista parametrów formalnych indeksatora odpowiada metodzie metody (parametrów metody), z tą różnicą, że musi być określony co najmniej jeden parametr, i że Modyfikatory parametrów ref i out są niedozwolone.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.

Typ indeksatora i każdy z typów, do których odwołuje się formal_parameter_list musi być co najmniej tak samo jak indeksator (ograniczenia dotyczące dostępności).The type of an indexer and each of the types referenced in the formal_parameter_list must be at least as accessible as the indexer itself (Accessibility constraints).

Indexer_body może składać się z treści metody dostępu lub treści wyrażenia.An indexer_body may either consist of an accessor body or an expression body. W treści metody dostępu accessor_declarations, która musi być ujęta w tokeny "{" i "}", zadeklaruj metody dostępu (Akcesory) właściwości.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Metody dostępu określają instrukcję wykonywalną skojarzoną z odczytem i zapisem właściwości.The accessors specify the executable statements associated with reading and writing the property.

Treść wyrażenia składająca się z "=>", po którym następuje wyrażenie E i średnik jest dokładnie równoważne z treścią instrukcji { get { return E; } }i można go użyć tylko do określenia indeksatorów tylko do pobierającego, w których wynik metody pobierającej jest określony przez pojedyncze wyrażenie.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.

Mimo że składnia uzyskiwania dostępu do elementu indeksatora jest taka sama jak dla elementu tablicy, element indeksatora nie jest klasyfikowany jako zmienna.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. Z tego względu nie jest możliwe przekazanie elementu indeksatora jako argumentu ref lub out.Thus, it is not possible to pass an indexer element as a ref or out argument.

Formalna lista parametrów indeksatora definiuje sygnaturę (sygnatury i Przeciążenie) indeksatora.The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. W celu podpisania indeksatora składa się z liczby i typów jego parametrów formalnych.Specifically, the signature of an indexer consists of the number and types of its formal parameters. Typ elementu i nazwy parametrów formalnych nie są częścią podpisu indeksatora.The element type and names of the formal parameters are not part of an indexer's signature.

Sygnatura indeksatora musi różnić się od sygnatur wszystkich innych indeksatorów zadeklarowanych w tej samej klasie.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Indeksatory i właściwości są bardzo podobne do koncepcji, ale różnią się w następujący sposób:Indexers and properties are very similar in concept, but differ in the following ways:

  • Właściwość jest identyfikowana za pomocą jej nazwy, podczas gdy indeksator jest identyfikowany przez jego sygnaturę.A property is identified by its name, whereas an indexer is identified by its signature.
  • Dostęp do właściwości uzyskuje się za pomocą simple_name (nazw prostych) lub member_access (dostęp do elementu członkowskiego), podczas gdy dostęp do elementu indeksatora uzyskuje się za pomocą element_access (dostęp do indeksatora).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).
  • Właściwość może być static składową, natomiast indeksator jest zawsze składową wystąpienia.A property can be a static member, whereas an indexer is always an instance member.
  • Metoda dostępu get właściwość odpowiada metodzie bez parametrów, podczas gdy metoda dostępu get indeksatora odpowiada metodzie z tą samą formalną listą parametrów, co indeksator.A get accessor of a property corresponds to a method with no parameters, whereas a get accessor of an indexer corresponds to a method with the same formal parameter list as the indexer.
  • Metoda dostępu set właściwość odpowiada metodzie z pojedynczym parametrem o nazwie value, podczas gdy set metoda dostępu indeksatora odpowiada metodzie z tą samą formalną listą parametrów, co indeksator, oraz dodatkowym parametrem o nazwie value.A set accessor of a property corresponds to a method with a single parameter named value, whereas a set accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named value.
  • Jest to błąd czasu kompilacji dla metody dostępu indeksatora, aby zadeklarować zmienną lokalną o takiej samej nazwie jak parametr indeksatora.It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • W deklaracji właściwości zastępują dostęp do właściwości dziedziczonej przy użyciu składni base.P, gdzie P jest nazwą właściwości.In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. W przypadku przesłaniania deklaracji indeksatora Dziedziczony indeksator jest dostępny przy użyciu składni base[E], gdzie E jest rozdzielaną przecinkami listą wyrażeń.In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • Nie ma koncepcji "automatycznie zaimplementowany indeksator".There is no concept of an "automatically implemented indexer". Wystąpił błąd nieabstrakcyjnego, niezewnętrznego indeksatora z dostępem średników.It is an error to have a non-abstract, non-external indexer with semicolon accessors.

Oprócz tych różnic wszystkie reguły zdefiniowane w metodach dostępu i automatycznie zaimplementowane właściwości mają zastosowanie do metod dostępu indeksatora oraz metod dostępu do właściwości.Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

Gdy deklaracja indeksatora zawiera modyfikator extern, indeksator jest uznawany za zewnętrzny indeksator.When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer. Ponieważ zewnętrzna deklaracja indeksatora nie zapewnia żadnej rzeczywistej implementacji, każda z jej accessor_declarations składa się z średnika.Because an external indexer declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Poniższy przykład deklaruje klasę BitArray, która implementuje indeksator do uzyskiwania dostępu do poszczególnych bitów w tablicy bitowej.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);
            }
        }
    }
}

Wystąpienie klasy BitArray zużywa znacznie mniej pamięci niż odpowiadające im bool[] (ponieważ każda wartość dawna zajmuje tylko jeden bit zamiast ostatniego z nich), ale umożliwia wykonywanie tych samych operacji co 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[].

W poniższej klasie CountPrimes używany jest BitArray i klasyczny algorytm "Sit", aby obliczyć liczbę wartości granicznych z przedziału od 1 do: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);
    }
}

Należy zauważyć, że Składnia służąca do uzyskiwania dostępu do elementów BitArray jest dokładnie taka sama jak w przypadku bool[].Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

W poniższym przykładzie przedstawiono klasę siatki 26 * 10, która ma indeksator z dwoma parametrami.The following example shows a 26 * 10 grid class that has an indexer with two parameters. Pierwszy parametr musi być wielką lub małą literą w zakresie A-Z, a drugi musi być liczbą całkowitą z zakresu 0-9.The first parameter is required to be an upper- or lowercase letter in the range A-Z, and the second is required to be an integer in the range 0-9.

using System;

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

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

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

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

Przeciążanie indeksatoraIndexer overloading

Reguły rozpoznawania przeciążenia indeksatora są opisane w wnioskach o typie.The indexer overload resolution rules are described in Type inference.

OperatoryOperators

Operator jest członkiem, który definiuje znaczenie operatora wyrażenia, który można zastosować do wystąpień klasy.An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. Operatory są deklarowane przy użyciu operator_declarations:Operators are declared using operator_declarations:

operator_declaration
    : attributes? operator_modifier+ operator_declarator operator_body
    ;

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

operator_declarator
    : unary_operator_declarator
    | binary_operator_declarator
    | conversion_operator_declarator
    ;

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

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

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

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

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

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

Istnieją trzy kategorie operatorów z możliwością przeciążenia: Jednoargumentowe operatory (operatory jednoargumentowe), operatory binarne (Operatory binarne) i operatory konwersji (Operatory konwersji).There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

Operator_body jest średnikiem, treścią instrukcji lub treścią wyrażenia.The operator_body is either a semicolon, a statement body or an expression body. Treść instrukcji składa się z bloku, który określa instrukcje do wykonania, gdy operator jest wywoływany.A statement body consists of a block, which specifies the statements to execute when the operator is invoked. Blok musi być zgodny z regułami dotyczącymi metod zwracających wartość opisaną w treści metody.The block must conform to the rules for value-returning methods described in Method body. Treść wyrażenia składa się z => po którym następuje wyrażenie i średnik, i oznacza pojedyncze wyrażenie, które ma zostać wykonane po wywołaniu operatora.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked.

Dla operatorów extern operator_body składa się po prostu z średnika.For extern operators, the operator_body consists simply of a semicolon. Dla wszystkich innych operatorów operator_body jest treścią bloku lub treścią wyrażenia.For all other operators, the operator_body is either a block body or an expression body.

Następujące reguły mają zastosowanie do wszystkich deklaracji operatora:The following rules apply to all operator declarations:

  • Deklaracja operatora musi zawierać zarówno modyfikator public, jak i static.An operator declaration must include both a public and a static modifier.
  • Parametry operatora muszą być parametrami wartości (Parametry wartości).The parameter(s) of an operator must be value parameters (Value parameters). Jest to błąd czasu kompilacji dla deklaracji operatora, aby określić parametry ref lub out.It is a compile-time error for an operator declaration to specify ref or out parameters.
  • Sygnatura operatora (operatory jednoargumentowe, Operatory binarne, Operatory konwersji) musi różnić się od sygnatur wszystkich innych operatorów zadeklarowanych w tej samej klasie.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.
  • Wszystkie typy, do których odwołuje się deklaracja operatora, muszą być co najmniej tak samo jak operator (ograniczenia dostępności).All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • Jest to błąd dla tego samego modyfikatora, który może występować wiele razy w deklaracji operatora.It is an error for the same modifier to appear multiple times in an operator declaration.

Każda kategoria operatora nakłada dodatkowe ograniczenia, zgodnie z opisem w poniższych sekcjach.Each operator category imposes additional restrictions, as described in the following sections.

Podobnie jak w przypadku innych składowych, operatory zadeklarowane w klasie bazowej są dziedziczone przez klasy pochodne.Like other members, operators declared in a base class are inherited by derived classes. Ponieważ deklaracje operatora zawsze wymagają klasy lub struktury, w której operator jest zadeklarowany do uczestniczenia w sygnaturze operatora, nie jest możliwe dla operatora zadeklarowanego w klasie pochodnej w celu ukrycia operatora zadeklarowanego w klasie bazowej.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. W związku z tym modyfikator new nigdy nie jest wymagany i w związku z tym nigdy nie jest dozwolony w deklaracji operatora.Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

Dodatkowe informacje na temat operatorów jednoargumentowych i binarnych można znaleźć w operatorach.Additional information on unary and binary operators can be found in Operators.

Dodatkowe informacje na temat operatorów konwersji można znaleźć w definicjach zdefiniowanych przez użytkownika.Additional information on conversion operators can be found in User-defined conversions.

Operatory jednoargumentoweUnary operators

Poniższe reguły dotyczą deklaracji operatora jednoargumentowego, gdzie T wskazuje typ wystąpienia klasy lub struktury, która zawiera deklarację operatora:The following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Operatory jednoargumentowe +, -, !lub ~ muszą przyjmować jeden parametr typu T lub T? i mogą zwracać dowolny typ.A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • Operatory jednoargumentowe ++ lub -- muszą przyjmować jeden parametr typu T lub T? i muszą zwracać ten sam typ lub typ pochodny.A unary ++ or -- operator must take a single parameter of type T or T? and must return that same type or a type derived from it.
  • Operatory jednoargumentowe true lub false muszą przyjmować jeden parametr typu T lub T? i muszą zwracać typ bool.A unary true or false operator must take a single parameter of type T or T? and must return type bool.

Sygnatura jednoargumentowego operatora składa się z tokenu operatora (+, -, !, ~, ++, --, truelub false) i typu pojedynczego parametru formalnego.The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. Zwracany typ nie jest częścią podpisu operatora jednoargumentowego ani nie jest nazwą parametru formalnego.The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

Operatory true i false jednoargumentowe wymagają deklaracji pary.The true and false unary operators require pair-wise declaration. Błąd czasu kompilacji występuje, gdy Klasa deklaruje jeden z tych operatorów bez deklarowania innych.A compile-time error occurs if a class declares one of these operators without also declaring the other. Operatory true i false są opisane bardziej szczegółowo w warunkowych operatory logiczne i wyrażeniachlogicznych.The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

W poniższym przykładzie przedstawiono implementację i kolejne użycie operator ++ dla klasy wektorów liczb całkowitych: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
    }
}

Zwróć uwagę, jak Metoda operatora zwraca wartość wygenerowaną przez dodanie 1 do operandu, podobnie jak Operatory przyrostka i zmniejszanie (Operatory przyrostka i zmniejszenie) oraz operatory przyrostu i zmniejszania prefiksu (Operatory przyrostka izmniejszenie).Note how the operator method returns the value produced by adding 1 to the operand, just like the postfix increment and decrement operators (Postfix increment and decrement operators), and the prefix increment and decrement operators (Prefix increment and decrement operators). W przeciwieństwie C++do programu w przypadku tej metody nie należy modyfikować wartości operandu bezpośrednio.Unlike in C++, this method need not modify the value of its operand directly. W rzeczywistości modyfikacja wartości operandu spowoduje naruszenie standardowej semantyki operatora przyrostu przyrostkowego.In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

Operatory binarneBinary operators

Następujące reguły dotyczą deklaracji operatora binarnego, gdzie T określa typ wystąpienia klasy lub struktury, która zawiera deklarację operatora:The following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Binarny operator nie przesunięcia musi przyjmować dwa parametry, co najmniej jeden z nich musi mieć typ T lub T?i może zwracać dowolny typ.A binary non-shift operator must take two parameters, at least one of which must have type T or T?, and can return any type.
  • Binarny operator << lub >> musi przyjmować dwa parametry, z których pierwszy musi mieć typ T lub T?, a drugi, który musi mieć typ int lub int?, i może zwracać dowolny typ.A binary << or >> operator must take two parameters, the first of which must have type T or T? and the second of which must have type int or int?, and can return any type.

Podpis operatora Binary składa się z tokenu operatora (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=lub <=) i typów dwóch parametrów formalnych.The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. Typ zwracany i nazwy parametrów formalnych nie są częścią podpisu operatora binarnego.The return type and the names of the formal parameters are not part of a binary operator's signature.

Niektóre operatory binarne wymagają deklaracji pary.Certain binary operators require pair-wise declaration. Dla każdej deklaracji jednego z operatorów para, musi istnieć zgodna deklaracja innego operatora pary.For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Deklaracje dwóch operatorów są zgodne, gdy mają ten sam typ zwracany i ten sam typ dla każdego parametru.Two operator declarations match when they have the same return type and the same type for each parameter. Następujące operatory wymagają deklaracji pary:The following operators require pair-wise declaration:

  • operator == i operator !=operator == and operator !=
  • operator > i operator <operator > and operator <
  • operator >= i operator <=operator >= and operator <=

Operatory konwersjiConversion operators

Deklaracja operatora konwersji wprowadza konwersję zdefiniowaną przez użytkownika (konwersje zdefiniowane przez użytkownika), która rozszerza wstępnie zdefiniowane konwersje niejawne i jawne.A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

Deklaracja operatora konwersji, która zawiera implicit słowo kluczowe wprowadza zdefiniowaną przez użytkownika konwersję niejawną.A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Konwersje niejawne mogą odbywać się w różnych sytuacjach, w tym wywołaniach elementów członkowskich funkcji, wyrażeniach rzutowania i przypisaniach.Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. Jest to bardziej szczegółowo opisane w konwersji niejawnych.This is described further in Implicit conversions.

Deklaracja operatora konwersji, która zawiera explicit słowo kluczowe wprowadza zdefiniowaną przez użytkownika konwersję jawną.A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Jawne konwersje mogą wystąpić w wyrażeniach rzutowania i są opisane bardziej szczegółowo w przypadku konwersji jawnych.Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

Operator konwersji wykonuje konwersję z typu źródłowego, wskazywanego przez typ parametru operatora konwersji do typu docelowego, wskazywanego przez zwracany typ operatora konwersji.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.

Dla danego typu źródła S i typu docelowego T, jeśli S lub T są typami dopuszczających wartości null, pozwól S0 i T0 odnoszą się do ich typów podstawowych, w przeciwnym razie S0 i T0 są równe S i T odpowiednio.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. Klasa lub struktura może deklarować konwersję z typu źródłowego S do typu docelowego T tylko wtedy, gdy spełnione są wszystkie następujące warunki: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 i T0 są różnymi typami.S0 and T0 are different types.
  • S0 lub T0 jest klasą lub typem struktury, w której odbywa się deklaracja operatora.Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • Ani S0 ani T0 jest interface_typeem.Neither S0 nor T0 is an interface_type.
  • Oprócz konwersji zdefiniowanych przez użytkownika konwersja nie istnieje z S do T lub T do S.Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

Na potrzeby tych zasad wszystkie parametry typu skojarzone z S lub T są uznawane za unikatowe typy, które nie mają relacji dziedziczenia z innymi typami, i wszelkie ograniczenia dotyczące tych parametrów typu są ignorowane.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.

w przykładzieIn 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
}

są dozwolone pierwsze dwie deklaracje operatora, ponieważ na potrzeby .3,T i int i string są uznawane za unikatowe typy bez relacji.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. Jednak trzeci operator jest błędem, ponieważ C<T> jest klasą bazową D<T>.However, the third operator is an error because C<T> is the base class of D<T>.

Z drugiej reguły należy wykonać konwersję operatora konwersji do lub z klasy lub typu struktury, w którym jest zadeklarowany operator.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. Na przykład, istnieje możliwość, że Klasa lub typ struktury C definiować konwersję z C do int oraz z int do C, ale nie z 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.

Nie można bezpośrednio zdefiniować wstępnie zdefiniowanej konwersji.It is not possible to directly redefine a pre-defined conversion. W związku z tym operatory konwersji nie mogą konwertować z lub do object, ponieważ niejawne i jawne konwersje już istnieją między object i innymi typami.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. Podobnie ani źródłowe, ani docelowe typy konwersji nie mogą być typu podstawowego drugiego, ponieważ konwersja już istnieje.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.

Można jednak zadeklarować operatory na typach ogólnych, które dla konkretnych argumentów typu określają konwersje, które już istniały jako wstępnie zdefiniowane konwersje.However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. w przykładzieIn the example

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

gdy typ object jest określony jako argument typu dla T, drugi operator deklaruje konwersję, która już istnieje (niejawną, a w związku z tym również jawne, konwersja istnieje z dowolnego typu do typu 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).

W przypadkach, gdy istnieje wstępnie zdefiniowana konwersja między dwoma typami, wszystkie konwersje zdefiniowane przez użytkownika między tymi typami są ignorowane.In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. OpracowanySpecifically:

  • Jeśli wstępnie zdefiniowana niejawna konwersja (niejawne konwersje) istnieje z typu S do typu T, wszystkie konwersje zdefiniowane przez użytkownika (niejawne lub jawne) z S do T są ignorowane.If a pre-defined implicit conversion (Implicit conversions) exists from type S to type T, all user-defined conversions (implicit or explicit) from S to T are ignored.
  • Jeśli wstępnie zdefiniowana konwersja jawna (jawne konwersje) istnieje z typu S do typu T, wszystkie jawne konwersje zdefiniowane przez użytkownika z S do T są ignorowane.If a pre-defined explicit conversion (Explicit conversions) exists from type S to type T, any user-defined explicit conversions from S to T are ignored. WięcejFurthermore:

Jeśli T jest typem interfejsu, zdefiniowane przez użytkownika Konwersje niejawne z S do T są ignorowane.If T is an interface type, user-defined implicit conversions from S to T are ignored.

W przeciwnym razie niejawne konwersje zdefiniowane przez użytkownika z S do T nadal są brane pod uwagę.Otherwise, user-defined implicit conversions from S to T are still considered.

Dla wszystkich typów, ale objectoperatory zadeklarowane przez typ Convertible<T> powyżej nie powodują konfliktu ze wstępnie zdefiniowanymi konwersjemi.For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. Na przykład: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
}

Jednak w przypadku typu objectwstępnie zdefiniowane konwersje ukrywają konwersje zdefiniowane przez użytkownika we wszystkich przypadkach, ale jeden: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
}

Konwersje zdefiniowane przez użytkownika nie mogą konwertować z lub na INTERFACE_TYPEs.User-defined conversions are not allowed to convert from or to interface_types. W szczególności to ograniczenie gwarantuje, że podczas konwertowania na INTERFACE_TYPEnie występują żadne przekształcenia zdefiniowane przez użytkownika, a konwersja na INTERFACE_TYPE powiodła się tylko wtedy, gdy konwertowany obiekt rzeczywiście implementuje określony INTERFACE_TYPE.In particular, this restriction ensures that no user-defined transformations occur when converting to an interface_type, and that a conversion to an interface_type succeeds only if the object being converted actually implements the specified interface_type.

Sygnatura operatora konwersji składa się z typu źródłowego i typu docelowego.The signature of a conversion operator consists of the source type and the target type. (Należy zauważyć, że jest to jedyna forma elementu członkowskiego, dla którego typ zwracany uczestniczy w podpisie). Klasyfikacja implicit lub explicit operatora konwersji nie jest częścią podpisu operatora.(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. W ten sposób Klasa lub struktura nie może deklarować zarówno implicit, jak i operatora konwersji explicit z tymi samymi typami źródłowymi i docelowymi.Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

Ogólnie rzecz biorąc, zdefiniowane przez użytkownika Konwersje niejawne powinny być zaprojektowane tak, aby nigdy nie generować wyjątków i nigdy nie tracić informacji.In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. Jeśli zdefiniowana przez użytkownika konwersja może prowadzić do wyjątków (na przykład ponieważ argument źródłowy znajduje się poza zakresem) lub utracisz informacje (np. odrzucanie bitów o wysokim stopniu kolejności), konwersja powinna być zdefiniowana jako konwersja jawna.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.

w przykładzieIn 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);
    }
}

Konwersja z Digit na byte jest niejawna, ponieważ nigdy nie zgłasza wyjątków lub utraci informacje, ale konwersja z byte do Digit jest jawna, ponieważ Digit może reprezentować tylko podzestaw możliwych wartości 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.

Konstruktory wystąpieńInstance constructors

Konstruktor wystąpienia jest członkiem, który implementuje akcje wymagane do zainicjowania wystąpienia klasy.An instance constructor is a member that implements the actions required to initialize an instance of a class. Konstruktory wystąpień są deklarowane przy użyciu constructor_declarations:Instance constructors are declared using constructor_declarations:

constructor_declaration
    : attributes? constructor_modifier* constructor_declarator constructor_body
    ;

constructor_modifier
    : 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'extern'
    | constructor_modifier_unsafe
    ;

constructor_declarator
    : identifier '(' formal_parameter_list? ')' constructor_initializer?
    ;

constructor_initializer
    : ':' 'base' '(' argument_list? ')'
    | ':' 'this' '(' argument_list? ')'
    ;

constructor_body
    : block
    | ';'
    ;

Constructor_declaration może zawierać zestaw atrybutów (atrybutów), prawidłową kombinację czterech modyfikatorów dostępu (Modyfikatory dostępu) oraz modyfikator extern (metody zewnętrzne).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. Deklaracja konstruktora nie może zawierać tego samego modyfikatora wiele razy.A constructor declaration is not permitted to include the same modifier multiple times.

Identyfikator constructor_declarator musi mieć nazwę klasy, w której jest zadeklarowany Konstruktor wystąpień.The identifier of a constructor_declarator must name the class in which the instance constructor is declared. Jeśli określona jest jakakolwiek inna nazwa, wystąpi błąd w czasie kompilacji.If any other name is specified, a compile-time error occurs.

Opcjonalne formal_parameter_list konstruktora wystąpienia podlegają tym samym regułom, co Formal_parameter_list metody (metody).The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (Methods). Formalna lista parametrów definiuje sygnaturę (sygnatury i Przeciążenie) konstruktora wystąpienia i zarządza procesem, zgodnie z którym Rozpoznanie przeciążenia (wnioskowanie o typie) wybiera określony Konstruktor wystąpienia w wywołaniu.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.

Każdy typ, do którego odwołuje się formal_parameter_list konstruktora wystąpienia, musi być co najmniej tak samo samo jak Konstruktor (ograniczenia dostępności).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).

Opcjonalny constructor_initializer określa inny Konstruktor wystąpienia do wywołania przed wykonaniem instrukcji podanym w constructor_body tego konstruktora wystąpień.The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. Jest to bardziej szczegółowo opisane w inicjatorach konstruktora.This is described further in Constructor initializers.

Gdy deklaracja konstruktora zawiera modyfikator extern, Konstruktor jest nazywany konstruktorem zewnętrznym.When a constructor declaration includes an extern modifier, the constructor is said to be an external constructor. Ponieważ deklaracja konstruktora zewnętrznego nie oferuje rzeczywistej implementacji, jej constructor_body składa się z średnika.Because an external constructor declaration provides no actual implementation, its constructor_body consists of a semicolon. Dla wszystkich innych konstruktorów constructor_body składa się z bloku , który określa instrukcje inicjowania nowego wystąpienia klasy.For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. Odpowiada to dokładnie na blok metody wystąpienia z typem zwracanym void (treść metody).This corresponds exactly to the block of an instance method with a void return type (Method body).

Konstruktory wystąpień nie są dziedziczone.Instance constructors are not inherited. W ten sposób Klasa nie ma konstruktorów wystąpień innych niż zadeklarowane w klasie.Thus, a class has no instance constructors other than those actually declared in the class. Jeśli Klasa nie zawiera deklaracji konstruktora wystąpienia, zostanie automatycznie udostępniony Konstruktor wystąpienia domyślnego (konstruktory domyślne).If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

Konstruktory wystąpień są wywoływane przez object_creation_expressions (wyrażenia tworzenia obiektów) i constructor_initializers.Instance constructors are invoked by object_creation_expressions (Object creation expressions) and through constructor_initializers.

Inicjatory konstruktoraConstructor initializers

Wszystkie konstruktory wystąpień (z wyjątkiem tych dla klasy object) niejawnie zawierają wywołanie innego konstruktora wystąpienia bezpośrednio przed constructor_body.All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. Konstruktor niejawnie wywoływany jest określany przez constructor_initializer:The constructor to implicitly invoke is determined by the constructor_initializer:

  • Inicjator konstruktora wystąpień w formularzu base(argument_list) lub base() powoduje wywołanie konstruktora wystąpienia z bezpośredniej klasy podstawowej.An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. Ten konstruktor jest wybierany przy użyciu argument_list , jeśli jest obecny, i reguł rozpoznawania przeciążenia.That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. Zestaw konstruktorów wystąpień kandydatów składa się ze wszystkich dostępnych konstruktorów wystąpień zawartych w bezpośredniej klasie podstawowej lub konstruktora domyślnego (konstruktory domyślne), jeśli nie są zadeklarowane konstruktory wystąpień w bezpośredniej klasie podstawowej.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. Jeśli ten zestaw jest pusty lub nie można zidentyfikować jednego najlepszego konstruktora wystąpienia, wystąpi błąd w czasie kompilacji.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • Inicjator konstruktora wystąpień w formularzu this(argument-list) lub this() powoduje wywołanie konstruktora wystąpienia z samej klasy.An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. Konstruktor jest wybierany przy użyciu argument_list , jeśli jest obecny, i reguł rozdzielczości przeciążenia.The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. Zestaw konstruktorów wystąpień kandydatów składa się ze wszystkich dostępnych konstruktorów wystąpień zadeklarowanych w samej klasie.The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. Jeśli ten zestaw jest pusty lub nie można zidentyfikować jednego najlepszego konstruktora wystąpienia, wystąpi błąd w czasie kompilacji.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. Jeśli deklaracja konstruktora wystąpienia zawiera inicjator konstruktora, który wywołuje samego konstruktora, wystąpi błąd czasu kompilacji.If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

Jeśli Konstruktor wystąpienia nie ma inicjatora konstruktora, niejawnie podany zostanie inicjator konstruktora w formularzu base().If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. W rezultacie deklaracja konstruktora wystąpienia formularzaThus, an instance constructor declaration of the form

C(...) {...}

jest dokładnie równoważneis exactly equivalent to

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

Zakres parametrów przyznanych przez formal_parameter_list deklaracji konstruktora wystąpień zawiera inicjator konstruktora tej deklaracji.The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. W ten sposób inicjator konstruktora jest uprawniony do uzyskiwania dostępu do parametrów konstruktora.Thus, a constructor initializer is permitted to access the parameters of the constructor. Na przykład:For example:

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

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

Inicjator konstruktora wystąpień nie może uzyskać dostępu do tworzonego wystąpienia.An instance constructor initializer cannot access the instance being created. W związku z tym jest to błąd czasu kompilacji, aby odwołać się do this w wyrażeniu argumentu inicjatora konstruktora, podobnie jak w przypadku błędu czasu kompilacji dla wyrażenia argumentu, aby odwołać się do dowolnego elementu członkowskiego wystąpienia za pomocą simple_name.Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple_name.

Inicjatory zmiennych wystąpieniaInstance variable initializers

Gdy Konstruktor wystąpienia nie ma inicjatora konstruktora lub ma inicjatora konstruktora base(...), ten Konstruktor niejawnie wykonuje inicjalizacje określone przez variable_initializers pól wystąpienia zadeklarowanych w swojej klasie.When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable_initializers of the instance fields declared in its class. Odnosi się to do sekwencji przypisań, które są wykonywane natychmiast po wpisie do konstruktora i przed niejawnym wywołaniem konstruktora klasy bazowej bezpośredniego.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. Inicjatory zmiennych są wykonywane w kolejności tekstowej, w której pojawiają się w deklaracji klasy.The variable initializers are executed in the textual order in which they appear in the class declaration.

Wykonanie konstruktoraConstructor execution

Inicjatory zmiennych są przekształcane w instrukcje przypisania i te instrukcje przypisania są wykonywane przed wywołaniem konstruktora wystąpienia klasy podstawowej.Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. Takie porządkowanie gwarantuje, że wszystkie pola wystąpienia są inicjowane przez ich inicjatory zmiennych przed wykonaniem jakichkolwiek instrukcji, które mają dostęp do tego wystąpienia.This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

Zamieszczono przykładGiven 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);
    }
}

gdy new B() jest używany do tworzenia wystąpienia B, generowane są następujące dane wyjściowe:when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

Wartość x jest równa 1, ponieważ inicjator zmiennej jest wykonywany przed wywołaniem konstruktora wystąpienia klasy bazowej.The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. Jednak wartość y jest równa 0 (wartość domyślna int), ponieważ przypisanie do y nie jest wykonywane, dopóki nie zostanie zwrócony Konstruktor klasy bazowej.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.

Warto traktować inicjatory zmiennych wystąpień i inicjatory konstruktora jako instrukcje, które są automatycznie wstawiane przed constructor_body.It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. PrzykładThe 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;
    }
}

zawiera kilka inicjatorów zmiennych; zawiera również inicjatory konstruktora obu formularzy (base i this).contains several variable initializers; it also contains constructor initializers of both forms (base and this). Przykład odnosi się do kodu pokazanego poniżej, gdzie każdy komentarz wskazuje automatycznie wstawioną instrukcję (składnia używana dla automatycznie wstawionego konstruktora wywołania jest nieprawidłowa, ale tylko służy do zilustrowania mechanizmu).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;
    }
}

Konstruktory domyślneDefault constructors

Jeśli Klasa nie zawiera deklaracji konstruktora wystąpienia, zostanie automatycznie udostępniony Konstruktor wystąpienia domyślnego.If a class contains no instance constructor declarations, a default instance constructor is automatically provided. Ten konstruktor domyślny po prostu wywołuje konstruktora bez parametrów bezpośredniej klasy podstawowej.That default constructor simply invokes the parameterless constructor of the direct base class. Jeśli klasa jest abstrakcyjna, zadeklarowana dostępność dla domyślnego konstruktora jest chroniona.If the class is abstract then the declared accessibility for the default constructor is protected. W przeciwnym razie zadeklarowana dostępność dla domyślnego konstruktora jest publiczna.Otherwise, the declared accessibility for the default constructor is public. W ten sposób Konstruktor domyślny ma zawsze postaćThus, the default constructor is always of the form

protected C(): base() {}

lubor

public C(): base() {}

gdzie C jest nazwą klasy.where C is the name of the class. Jeśli rozwiązanie przeciążenia nie jest w stanie ustalić unikatowego najlepszego kandydata dla inicjatora konstruktora klasy bazowej, wystąpi błąd czasu kompilacji.If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

w przykładzieIn the example

class Message
{
    object sender;
    string text;
}

podano Konstruktor domyślny, ponieważ Klasa nie zawiera deklaracji konstruktora wystąpień.a default constructor is provided because the class contains no instance constructor declarations. W tym celu przykład jest dokładnie równoważny zThus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

Konstruktory prywatnePrivate constructors

Gdy Klasa T deklaruje tylko konstruktorów wystąpień prywatnych, nie jest możliwe w przypadku klas spoza tekstu programu T, które pochodzą z T lub bezpośrednio tworzą wystąpienia 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. W takim przypadku, jeśli Klasa zawiera tylko statyczne elementy członkowskie i nie jest przeznaczona do wystąpienia, dodanie pustego konstruktora wystąpienia prywatnego uniemożliwi utworzenie wystąpienia.Thus, if a class contains only static members and isn't intended to be instantiated, adding an empty private instance constructor will prevent instantiation. Na przykład: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) {...}
}

Klasy Trig są powiązane z metodami i stałymi, ale nie są przeznaczone do tworzenia wystąpień.The Trig class groups related methods and constants, but is not intended to be instantiated. W związku z tym deklaruje on jeden pusty Konstruktor wystąpienia prywatnego.Therefore it declares a single empty private instance constructor. Należy zadeklarować co najmniej jeden Konstruktor wystąpienia, aby pominąć automatyczne generowanie domyślnego konstruktora.At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

Opcjonalne parametry konstruktora wystąpieńOptional instance constructor parameters

this(...)a forma inicjatora konstruktora jest często używana w połączeniu z przeciążeniem w celu zaimplementowania opcjonalnych parametrów konstruktora wystąpień.The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. w przykładzieIn 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
    }
}

pierwsze dwa konstruktory wystąpień jedynie zawierają wartości domyślne dla brakujących argumentów.the first two instance constructors merely provide the default values for the missing arguments. Użyj inicjatora konstruktora this(...), aby wywołać trzeci Konstruktor wystąpienia, który faktycznie wykonuje zadania inicjujące nowe wystąpienie.Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. Efektem jest to, że opcjonalne parametry konstruktora: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");

Konstruktory statyczneStatic constructors

Statyczny Konstruktor jest członkiem, który implementuje akcje wymagane do zainicjowania typu zamkniętej klasy.A static constructor is a member that implements the actions required to initialize a closed class type. Konstruktory statyczne są deklarowane przy użyciu static_constructor_declarations:Static constructors are declared using static_constructor_declarations:

static_constructor_declaration
    : attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body
    ;

static_constructor_modifiers
    : 'extern'? 'static'
    | 'static' 'extern'?
    | static_constructor_modifiers_unsafe
    ;

static_constructor_body
    : block
    | ';'
    ;

Static_constructor_declaration może zawierać zestaw atrybutów (atrybutów) i modyfikator extern (metody zewnętrzne).A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

Identyfikator static_constructor_declaration musi należeć do klasy, w której zadeklarowany jest Konstruktor statyczny.The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. Jeśli określona jest jakakolwiek inna nazwa, wystąpi błąd w czasie kompilacji.If any other name is specified, a compile-time error occurs.

Gdy deklaracja konstruktora statycznego zawiera modyfikator extern, Konstruktor statyczny jest uznawany za zewnętrzny Konstruktor statyczny.When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Ponieważ deklaracja zewnętrznego konstruktora statycznego nie oferuje rzeczywistej implementacji, jej static_constructor_body składa się z średnika.Because an external static constructor declaration provides no actual implementation, its static_constructor_body consists of a semicolon. Dla wszystkich innych deklaracji statycznego konstruktora static_constructor_body składa się z bloku , który określa instrukcje do wykonania w celu zainicjowania klasy.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. Odpowiada to dokładnie method_body statycznej metody z typem zwracanym void (treść metody).This corresponds exactly to the method_body of a static method with a void return type (Method body).

Konstruktory statyczne nie są dziedziczone i nie mogą być wywoływane bezpośrednio.Static constructors are not inherited, and cannot be called directly.

Statyczny Konstruktor dla typu zamkniętej klasy jest wykonywany co najwyżej raz w danej domenie aplikacji.The static constructor for a closed class type executes at most once in a given application domain. Wykonanie konstruktora statycznego jest wyzwalane przez pierwsze z następujących zdarzeń, które mają być wykonywane w domenie aplikacji:The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • Tworzone jest wystąpienie typu klasy.An instance of the class type is created.
  • Wszystkie statyczne elementy członkowskie typu klasy są przywoływane.Any of the static members of the class type are referenced.

Jeśli Klasa zawiera metodę Main (Uruchamianie aplikacji), w której rozpoczyna się wykonywanie, Konstruktor statyczny dla tej klasy jest wykonywany przed wywołaniem metody Main.If a class contains the Main method (Application Startup) in which execution begins, the static constructor for that class executes before the Main method is called.

Aby zainicjować nowy typ klasy zamkniętej, najpierw tworzony jest nowy zestaw pól statycznych (pola statyczne i wystąpienia) dla danego typu zamkniętego.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. Każde z pól statycznych jest inicjowane z wartością domyślną (wartości domyślne).Each of the static fields is initialized to its default value (Default values). Następnie elementy statyczne inicjatory pola (Inicjalizacja pola statycznego) są wykonywane dla tych pól statycznych.Next, the static field initializers (Static field initialization) are executed for those static fields. Na koniec jest wykonywany Konstruktor statyczny.Finally, the static constructor is executed.

PrzykładThe 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");
    }
}

musi generować dane wyjściowe:must produce the output:

Init A
A.F
Init B
B.F

ponieważ wykonywanie Astatycznego konstruktora jest wyzwalane przez wywołanie do A.F, a wykonywanie Bgo konstruktora statycznego jest wyzwalane przez wywołanie do 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.

Istnieje możliwość skonstruowania zależności cyklicznych, które zezwalają na przestrzeganie pól statycznych z inicjatorami zmiennych w stanie wartości domyślnych.It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

PrzykładThe 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);
    }
}

tworzy dane wyjścioweproduces the output

X = 1, Y = 2

Aby wykonać metodę Main, system najpierw uruchamia inicjator dla B.Y, przed konstruktorem statycznym klasy B.To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Inicjator Ypowoduje uruchomienie Anego konstruktora statycznego, ponieważ istnieje odwołanie do wartości A.X.Y's initializer causes A's static constructor to be run because the value of A.X is referenced. Statyczny Konstruktor A z kolei będzie kontynuował Obliczanie wartości Xi w ten sposób Pobiera wartość domyślną Y, która jest równa zero.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 jest w ten sposób inicjowany do 1.A.X is thus initialized to 1. Proces uruchamiania inicjatorów pól statycznych Ai konstruktora statycznego, a następnie zakończenia, zwracając do obliczenia wartości początkowej Y, wynik, który zostanie ustawiony na 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.

Ponieważ statyczny Konstruktor jest wykonywany dokładnie jeden raz dla każdego zamkniętego typu klasy konstruowanej, jest to wygodne miejsce do wymuszania sprawdzania w czasie wykonywania na parametrze typu, który nie może być sprawdzany w czasie kompilacji za pośrednictwem ograniczeń (ograniczenia parametru typu).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). Na przykład następujący typ używa konstruktora statycznego, aby wymusić, że argument typu jest wyliczeniem: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");
        }
    }
}

DestruktoryDestructors

Destruktor jest członkiem, który implementuje akcje wymagane do zadestruktorowania wystąpienia klasy.A destructor is a member that implements the actions required to destruct an instance of a class. Destruktor jest zadeklarowany przy użyciu destructor_declaration:A destructor is declared using a destructor_declaration:

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

destructor_body
    : block
    | ';'
    ;

Destructor_declaration może zawierać zestaw atrybutów (atrybutów).A destructor_declaration may include a set of attributes (Attributes).

Identyfikator destructor_declaration musi należeć do klasy, w której jest zadeklarowany destruktor.The identifier of a destructor_declaration must name the class in which the destructor is declared. Jeśli określona jest jakakolwiek inna nazwa, wystąpi błąd w czasie kompilacji.If any other name is specified, a compile-time error occurs.

Gdy deklaracja destruktora zawiera modyfikator extern, destruktor jest nazywany destruktorem zewnętrznym.When a destructor declaration includes an extern modifier, the destructor is said to be an external destructor. Ponieważ deklaracja zewnętrznego destruktora nie zapewnia rzeczywistej implementacji, jej destructor_body składa się z średnika.Because an external destructor declaration provides no actual implementation, its destructor_body consists of a semicolon. Dla wszystkich innych destruktorów destructor_body składa się z bloku, który określa instrukcje do wykonania w celu zablokowania wystąpienia klasy.For all other destructors, the destructor_body consists of a block which specifies the statements to execute in order to destruct an instance of the class. Destructor_body odpowiada dokładnie method_body metody wystąpienia z typem zwracanym void (treść metody).A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

Destruktory nie są dziedziczone.Destructors are not inherited. W ten sposób Klasa nie ma destruktorów innych niż ten, który może być zadeklarowany w tej klasie.Thus, a class has no destructors other than the one which may be declared in that class.

Ponieważ destruktor jest wymagany do braku parametrów, nie może być przeciążony, więc Klasa może mieć co najwyżej jeden destruktor.Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

Destruktory są wywoływane automatycznie i nie mogą być wywoływane jawnie.Destructors are invoked automatically, and cannot be invoked explicitly. Wystąpienie kwalifikuje się do zniszczenia, gdy nie jest już możliwe do użycia w żadnym kodzie.An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. Wykonywanie destruktora dla wystąpienia może wystąpić w dowolnym momencie, gdy wystąpienie kwalifikuje się do zniszczenia.Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. Gdy wystąpienie jest destruktorem, destruktory w łańcuchu dziedziczenia tego wystąpienia są wywoływane, w kolejności, od najbardziej pochodnych do najmniej pochodnych.When an instance is destructed, the destructors in that instance's inheritance chain are called, in order, from most derived to least derived. Destruktor może być wykonywany na dowolnym wątku.A destructor may be executed on any thread. Aby uzyskać dalsze Omówienie zasad decydujących o sposobie i sposobach wykonywania destruktora, zobacz Automatyczne zarządzanie pamięcią.For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

Dane wyjściowe przykładuThe 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

ponieważ destruktory w łańcuchu dziedziczenia są wywoływane w kolejności, od najbardziej pochodnych do najmniej pochodnych.since destructors in an inheritance chain are called in order, from most derived to least derived.

Destruktory są implementowane przez zastąpienie metody wirtualnej Finalize w System.Object.Destructors are implemented by overriding the virtual method Finalize on System.Object. C#programy nie mogą przesłonić tej metody lub wywołać jej bezpośrednio (lub przesłonięć).C# programs are not permitted to override this method or call it (or overrides of it) directly. Na przykład programFor instance, the program

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

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

zawiera dwa błędy.contains two errors.

Kompilator zachowuje się tak, jakby ta metoda i przesłonięcia go nie istnieją.The compiler behaves as if this method, and overrides of it, do not exist at all. W tym programie:Thus, this program:

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

jest prawidłowy, a pokazana Metoda ukrywa metodę Finalize System.Object.is valid, and the method shown hides System.Object's Finalize method.

Aby zapoznać się z omówieniem zachowania, gdy wyjątek jest zgłaszany przez destruktor, zobacz jak wyjątki są obsługiwane.For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

IteratoryIterators

Element członkowski funkcji (składowe funkcji) zaimplementowany przy użyciu bloku iteratora (Blocks) jest nazywany iteratorem.A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

Blok iteratora może być używany jako treść składowej funkcji, tak długo, jak typ zwracany odpowiadającego elementu członkowskiego funkcji jest jednym z interfejsów modułu wyliczającego (moduły wyliczające) lub jeden z wyliczalnych interfejsów (wyliczalne interfejsy).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). Może wystąpić jako method_body, operator_body lub accessor_body, podczas gdy zdarzenia, konstruktory wystąpień, konstruktory statyczne i destruktory nie mogą być implementowane jako Iteratory.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.

Gdy element członkowski funkcji jest implementowany przy użyciu bloku iteratora, jest to błąd czasu kompilacji dla formalnej listy parametrów elementu członkowskiego funkcji, aby określić parametry ref lub out.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.

Interfejsy modułu wyliczającegoEnumerator interfaces

Interfejsy modułu wyliczającego są interfejsami nieogólnymi System.Collections.IEnumerator i wszystkimi wystąpieniami System.Collections.Generic.IEnumerator<T>interfejsu ogólnego.The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. Ze względu na zwięzłości w tym rozdziale te interfejsy są przywoływane odpowiednio IEnumerator i IEnumerator<T>.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

Wyliczalne interfejsyEnumerable interfaces

Wyliczalne interfejsy są interfejsem nieogólnym System.Collections.IEnumerable i wszystkimi wystąpieniami System.Collections.Generic.IEnumerable<T>interfejsu ogólnego.The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. Ze względu na zwięzłości w tym rozdziale te interfejsy są przywoływane odpowiednio IEnumerable i IEnumerable<T>.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Typ YieldYield type

Iterator tworzy sekwencję wartości, tego samego typu.An iterator produces a sequence of values, all of the same type. Ten typ jest nazywany typem Yield iteratora.This type is called the yield type of the iterator.

  • Należy objecttyp Yield iteratora zwracającego IEnumerator lub IEnumerable.The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • Należy Ttyp Yield iteratora zwracającego IEnumerator<T> lub IEnumerable<T>.The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

Obiekty modułu wyliczającegoEnumerator objects

Gdy element członkowski funkcji zwracający typ interfejsu modułu wyliczającego jest zaimplementowany przy użyciu bloku iteratora, wywołanie elementu członkowskiego funkcji nie powoduje natychmiastowego wykonania kodu w bloku iteratora.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. Zamiast tego obiekt modułu wyliczającego jest tworzony i zwracany.Instead, an enumerator object is created and returned. Ten obiekt hermetyzuje kod określony w bloku iteratora i wykonywanie kodu w bloku iteratora występuje, gdy wywoływana jest metoda MoveNext obiektu modułu wyliczającego.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. Obiekt modułu wyliczającego ma następującą charakterystykę:An enumerator object has the following characteristics:

  • Implementuje IEnumerator i IEnumerator<T>, gdzie T jest typem Yield iteratora.It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • Implementuje System.IDisposable.It implements System.IDisposable.
  • Jest on inicjowany z kopią wartości argumentów (jeśli istnieją) i wartością wystąpienia przekazaną do elementu członkowskiego funkcji.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • Ma cztery potencjalne Stany, wcześniej, uruchomione, zawieszonei ponich, a początkowo jest w stanie przed .It has four potential states, before, running, suspended, and after, and is initially in the before state.

Obiekt modułu wyliczającego jest zwykle wystąpieniem klasy modułu wyliczającego wygenerowanego przez kompilator, która hermetyzuje kod w bloku iteratora i implementuje interfejsy modułu wyliczającego, ale możliwe są inne metody implementacji.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. Jeśli klasa modułu wyliczającego jest generowana przez kompilator, ta klasa będzie zagnieżdżona, bezpośrednio lub pośrednio, w klasie zawierającej element członkowski funkcji będzie mieć prywatną dostępność i będzie miała nazwę zarezerwowaną dla użycia kompilatora (identyfikatory).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).

Obiekt modułu wyliczającego może zaimplementować więcej interfejsów niż określono powyżej.An enumerator object may implement more interfaces than those specified above.

W poniższych sekcjach opisano dokładne zachowanie MoveNext, Currenti Dispose członków implementacji interfejsu IEnumerable i IEnumerable<T> dostarczonych przez obiekt modułu wyliczającego.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.

Należy zauważyć, że obiekty modułu wyliczającego nie obsługują metody IEnumerator.Reset.Note that enumerator objects do not support the IEnumerator.Reset method. Wywołanie tej metody powoduje zgłoszenie System.NotSupportedException.Invoking this method causes a System.NotSupportedException to be thrown.

MoveNext — MetodaThe MoveNext method

Metoda MoveNext obiektu modułu wyliczającego hermetyzuje kod bloku iteratora.The MoveNext method of an enumerator object encapsulates the code of an iterator block. Wywoływanie metody MoveNext wykonuje kod w bloku iteratora i ustawia właściwość Current obiektu Enumerator odpowiednio do potrzeb.Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. Precyzyjne działanie wykonywane przez MoveNext zależy od stanu obiektu modułu wyliczającego, gdy MoveNext jest wywoływana:The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • Jeśli stan obiektu modułu wyliczającego jest wcześniejszy, wywoływanie MoveNext:If the state of the enumerator object is before, invoking MoveNext:
    • Zmienia stan na uruchomiony.Changes the state to running.
    • Inicjuje parametry (w tym this) bloku iteratora do wartości argumentów i wartości wystąpienia zapisanego podczas inicjowania obiektu modułu wyliczającego.Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • Wykonuje blok iterator od początku do momentu przerwania wykonywania (zgodnie z poniższym opisem).Executes the iterator block from the beginning until execution is interrupted (as described below).
  • Jeśli stan obiektu modułu wyliczającego jest uruchomiony, wynik wywoływania MoveNext jest nieokreślony.If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • Jeśli stan obiektu modułu wyliczającego jest zawieszony, wywoływanie MoveNext:If the state of the enumerator object is suspended, invoking MoveNext:
    • Zmienia stan na uruchomiony.Changes the state to running.
    • Przywraca wartości wszystkich zmiennych lokalnych i parametrów (włącznie z tym) do wartości zapisanych podczas ostatniego wstrzymania wykonywania bloku iteratora.Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Należy zauważyć, że zawartość wszelkich obiektów, do których odwołuje się te zmienne, mogła ulec zmianie od czasu poprzedniego wywołania elementu MoveNext.Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • Wznawia wykonywanie bloku iteratora bezpośrednio po instrukcji yield return, która spowodowała zawieszenie wykonywania i jest kontynuowane do momentu przerwania wykonywania (zgodnie z poniższym opisem).Resumes execution of the iterator block immediately following the yield return statement that caused the suspension of execution and continues until execution is interrupted (as described below).
  • Jeśli stan obiektu modułu wyliczającego jest po, wywołanie MoveNext zwraca false.If the state of the enumerator object is after, invoking MoveNext returns false.

Gdy MoveNext wykonuje blok iterator, wykonywanie może zostać przerwane na cztery sposoby: przez instrukcję yield return, przez instrukcję yield break, przez napotkanie końca bloku iteratora i wyjątek zgłaszany i propagowany z bloku iteratora.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.

  • Po napotkaniu instrukcji yield return (instrukcja Yield):When a yield return statement is encountered (The yield statement):
    • Wyrażenie określone w instrukcji jest oceniane, niejawnie konwertowane na typ Yield i przypisane do właściwości Current obiektu modułu wyliczającego.The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • Wykonywanie treści iteratora zostało wstrzymane.Execution of the iterator body is suspended. Wartości wszystkich lokalnych zmiennych i parametrów (w tym this) są zapisywane, podobnie jak lokalizacja tej instrukcji yield return.The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. Jeśli instrukcja yield return znajduje się w obrębie jednego lub kilku bloków try, skojarzone bloki finally nie są wykonywane w tym momencie.If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • Stan obiektu modułu wyliczającego został zmieniony na wstrzymany.The state of the enumerator object is changed to suspended.
    • Metoda MoveNext zwraca true do obiektu wywołującego, co wskazuje, że iteracja została pomyślnie zaawansowana do następnej wartości.The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • Po napotkaniu instrukcji yield break (instrukcja Yield):When a yield break statement is encountered (The yield statement):
    • Jeśli instrukcja yield break znajduje się w obrębie jednego lub kilku bloków try, skojarzone bloki finally są wykonywane.If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • Stan obiektu modułu wyliczającego jest zmieniany na po.The state of the enumerator object is changed to after.
    • Metoda MoveNext zwraca false do obiektu wywołującego, wskazując, że iteracja została zakończona.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Gdy zostanie osiągnięty koniec treści iteratora:When the end of the iterator body is encountered:
    • Stan obiektu modułu wyliczającego jest zmieniany na po.The state of the enumerator object is changed to after.
    • Metoda MoveNext zwraca false do obiektu wywołującego, wskazując, że iteracja została zakończona.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Gdy wyjątek jest zgłaszany i propagowany z bloku iteratora:When an exception is thrown and propagated out of the iterator block:
    • Odpowiednie bloki finally w treści iteratora zostaną wykonane przez propagację wyjątku.Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • Stan obiektu modułu wyliczającego jest zmieniany na po.The state of the enumerator object is changed to after.
    • Propagacja wyjątku kontynuuje proces wywołujący metody MoveNext.The exception propagation continues to the caller of the MoveNext method.

Bieżąca WłaściwośćThe Current property

Na właściwości Current obiektu modułu wyliczającego wpływają instrukcje yield return w bloku iteratora.An enumerator object's Current property is affected by yield return statements in the iterator block.

Gdy obiekt modułu wyliczającego jest w stanie wstrzymania , wartość Current jest wartością ustawioną przez poprzednie wywołanie do MoveNext.When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. Gdy obiekt modułu wyliczającego znajduje się w stanie sprzed, uruchomionalub after , wynik uzyskania dostępu Current nie zostanie określony.When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified.

Dla iteratora z typem Yield innym niż object, wynik uzyskania dostępu Current przez implementację IEnumerable obiektu modułu wyliczającego odnosi się do dostępu Current przez implementację IEnumerator<T> obiektu modułu wyliczającego i rzutowanie wyniku na 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.

Metoda DisposeThe Dispose method

Metoda Dispose służy do czyszczenia iteracji przez przełączenie obiektu Enumerator do stanu po .The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • Jeśli stan obiektu modułu wyliczającego jest wcześniejszy, wywoływanie Dispose zmienia stan na po.If the state of the enumerator object is before, invoking Dispose changes the state to after.
  • Jeśli stan obiektu modułu wyliczającego jest uruchomiony, wynik wywoływania Dispose jest nieokreślony.If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • Jeśli stan obiektu modułu wyliczającego jest zawieszony, wywoływanie Dispose:If the state of the enumerator object is suspended, invoking Dispose:
    • Zmienia stan na uruchomiony.Changes the state to running.
    • Wykonuje wszystkie bloki finally tak, jakby Ostatnia wykonana instrukcja yield return była instrukcją yield break.Executes any finally blocks as if the last executed yield return statement were a yield break statement. Jeśli spowoduje to wyjątek, który ma zostać wygenerowany i rozpropagowany z treści iteratora, stan obiektu modułu wyliczającego jest ustawiony na po , a wyjątek jest propagowany do obiektu wywołującego metody Dispose.If this causes an exception to be thrown and propagated out of the iterator body, the state of the enumerator object is set to after and the exception is propagated to the caller of the Dispose method.
    • Zmienia stan na po.Changes the state to after.
  • Jeśli stan obiektu modułu wyliczającego jest po, wywołanie Dispose nie ma wpływu.If the state of the enumerator object is after, invoking Dispose has no affect.

Wyliczalne obiektyEnumerable objects

Gdy element członkowski funkcji zwracający wyliczalny typ interfejsu jest implementowany przy użyciu bloku iteratora, wywołanie elementu członkowskiego funkcji nie powoduje natychmiastowego wykonania kodu w bloku iteratora.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. Zamiast tego zostanie utworzony i zwrócony wyliczalny obiekt .Instead, an enumerable object is created and returned. Metoda GetEnumerator obiektu wyliczeniowego zwraca obiekt modułu wyliczającego, który hermetyzuje kod określony w bloku iterator i wykonywanie kodu w bloku iteratora występuje, gdy wywoływana jest metoda MoveNext obiektu modułu wyliczającego.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. Wyliczalny obiekt ma następującą charakterystykę:An enumerable object has the following characteristics:

  • Implementuje IEnumerable i IEnumerable<T>, gdzie T jest typem Yield iteratora.It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • Jest on inicjowany z kopią wartości argumentów (jeśli istnieją) i wartością wystąpienia przekazaną do elementu członkowskiego funkcji.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

Wyliczalny obiekt jest zwykle wystąpieniem klasy wyliczalnej generowanej przez kompilator, która hermetyzuje kod w bloku iteratora i implementuje wyliczalne interfejsy, ale inne metody implementacji są możliwe.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. Jeśli wyliczalna Klasa jest generowana przez kompilator, ta klasa będzie zagnieżdżona, bezpośrednio lub pośrednio, w klasie zawierającej element członkowski funkcji będzie mieć prywatną dostępność i będzie miała nazwę zarezerwowaną dla użycia kompilatora (identyfikatory).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).

Wyliczalny obiekt może zaimplementować więcej interfejsów niż określono powyżej.An enumerable object may implement more interfaces than those specified above. W szczególności, wyliczalny obiekt może również zaimplementować IEnumerator i IEnumerator<T>, co umożliwia jego pełnienie jako wyliczalny i moduł wyliczający.In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. W tym typie implementacji przy pierwszym wywołaniu metody GetEnumerator obiektu wyliczalnego jest zwracany wyliczalny obiekt.In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. Kolejne wywołania GetEnumeratorwyliczalnego obiektu, jeśli takie istnieją, zwracają kopię wyliczalnego obiektu.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. W ten sposób każdy zwrócony moduł wyliczający ma swój własny stan, a zmiany w jednym wyliczającym nie wpłyną na inne.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

GetEnumerator — metodaThe GetEnumerator method

Obiekt przeliczalny zapewnia implementację metod GetEnumerator interfejsów IEnumerable i IEnumerable<T>.An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. Dwie GetEnumerator metody współdzielą wspólną implementację, która uzyskuje i zwraca dostępny obiekt modułu wyliczającego.The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. Obiekt modułu wyliczającego jest inicjowany przy użyciu wartości argumentów i wartości wystąpienia zapisywanych, gdy wyliczalny obiekt został zainicjowany, ale w przeciwnym razie obiekt Enumerator działa zgodnie z opisem w obiekcie Enumerator.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.

Przykład implementacjiImplementation example

W tej sekcji opisano możliwe implementacje iteratorów w postaci standardowych C# konstrukcji.This section describes a possible implementation of iterators in terms of standard C# constructs. Implementacja opisana tutaj jest oparta na tych samych zasadach, które są używane przez C# kompilator firmy Microsoft, ale nie oznacza to, że jest to dozwolone wdrożenie ani tylko jeden z nich.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.

Następująca Klasa Stack<T> implementuje metodę GetEnumerator przy użyciu iteratora.The following Stack<T> class implements its GetEnumerator method using an iterator. Iterator wylicza elementy stosu w kolejności od góry do dołu.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];
    }
}

Metodę GetEnumerator można przetłumaczyć na wystąpienie klasy modułu wyliczającego wygenerowanego przez kompilator, która hermetyzuje kod w bloku iteratora, jak pokazano w poniższym przykładzie.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();
        }
    }
}

W poprzednim tłumaczeniu kod w bloku iteratora jest przekształcany w komputer stanu i umieszczany w metodzie MoveNext klasy Enumerator.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. Ponadto zmienna lokalna i jest przełączana do pola w obiekcie modułu wyliczającego, dzięki czemu może nadal istnieć między wywołaniami 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.

Poniższy przykład drukuje prostą tabelę mnożenia liczb całkowitych od 1 do 10.The following example prints a simple multiplication table of the integers 1 through 10. Metoda FromTo w przykładzie zwraca wyliczalny obiekt i jest implementowana przy użyciu iteratora.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();
        }
    }
}

Metodę FromTo można przetłumaczyć na wystąpienie klasy wyliczalnej kompilatora, która hermetyzuje kod w bloku iteratora, jak pokazano w poniższym przykładzie.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();
        }
    }
}

Klasa wyliczalna implementuje zarówno wyliczalne interfejsy, jak i interfejsy modułu wyliczającego, umożliwiając obsługę zarówno jako wyliczalnego, jak i modułu wyliczającego.The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. Przy pierwszym wywołaniu metody GetEnumerator zostanie zwrócony wyliczalny obiekt.The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Kolejne wywołania GetEnumeratorwyliczalnego obiektu, jeśli takie istnieją, zwracają kopię wyliczalnego obiektu.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. W ten sposób każdy zwrócony moduł wyliczający ma swój własny stan, a zmiany w jednym wyliczającym nie wpłyną na inne.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. Metoda Interlocked.CompareExchange służy do zapewnienia bezpiecznego wątkowo operacji.The Interlocked.CompareExchange method is used to ensure thread-safe operation.

Parametry from i to są przełączane do pól w klasie wyliczalnej.The from and to parameters are turned into fields in the enumerable class. Ponieważ from jest modyfikowany w bloku iteratora, jest wprowadzane dodatkowe pole __from do przechowywania wartości początkowej from w każdym wyliczeniu.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.

Metoda MoveNext zgłasza InvalidOperationException, jeśli jest wywoływana, gdy __state jest 0.The MoveNext method throws an InvalidOperationException if it is called when __state is 0. Chroni to przed użyciem wyliczalnego obiektu jako obiektu modułu wyliczającego bez uprzedniego wywołania GetEnumerator.This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

Poniższy przykład pokazuje prostą klasę drzewa.The following example shows a simple tree class. Klasa Tree<T> implementuje metodę GetEnumerator przy użyciu iteratora.The Tree<T> class implements its GetEnumerator method using an iterator. Iterator wylicza elementy drzewa w kolejności wrostkowe.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();
    }
}

Metodę GetEnumerator można przetłumaczyć na wystąpienie klasy modułu wyliczającego wygenerowanego przez kompilator, która hermetyzuje kod w bloku iteratora, jak pokazano w poniższym przykładzie.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();
        }
    }
}

Kompilator wygenerował obiekty tymczasowe używany w instrukcjach foreachch zostaje zniesiony do pól __left i __right obiektu modułu wyliczającego.The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. Wartość pola __state obiektu wyliczającego jest dokładnie aktualizowana, tak aby poprawna Dispose() Metoda zostanie wywołana prawidłowo, jeśli wystąpi wyjątek.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. Należy zauważyć, że nie jest możliwe zapisanie przetłumaczonego kodu przy użyciu prostych instrukcji foreach.Note that it is not possible to write the translated code with simple foreach statements.

Funkcje asynchroniczneAsync functions

Metoda (metody) lub funkcja anonimowa (wyrażenia funkcji anonimowej) z modyfikatorem async jest nazywana funkcją asynchroniczną.A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an async function. Ogólnie rzecz biorąc termin Async jest używany do opisywania dowolnego rodzaju funkcji, która ma modyfikator async.In general, the term async is used to describe any kind of function that has the async modifier.

Jest to błąd czasu kompilacji dla formalnej listy parametrów funkcji asynchronicznej, aby określić parametry ref lub out.It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

Return_type metody asynchronicznej musi być albo void lub typem zadania.The return_type of an async method must be either void or a task type. Typy zadań są System.Threading.Tasks.Task i typy skonstruowane z System.Threading.Tasks.Task<T>.The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. Ze względu na zwięzłości w tym rozdziale te typy są przywoływane odpowiednio Task i Task<T>.For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. Metoda asynchroniczna zwracająca typ zadania jest określana jako zadanie zwracające zadania.An async method returning a task type is said to be task-returning.

Dokładna definicja typów zadań jest definiowana przez implementację, ale z punktu widzenia języka, typ zadania jest w jednym z Stanów nieukończonych, zakończonych powodzeniem lub błędem.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. Zadanie z błędami rejestruje odpowiedni wyjątek.A faulted task records a pertinent exception. Pomyślnie Task<T> rejestruje wynik typu T.A succeeded Task<T> records a result of type T. Typy zadań są oczekiwane i dlatego mogą być operandami wyrażeń Await (Expressions).Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

Wywołanie funkcji asynchronicznej ma możliwość zawieszenia oceny przy użyciu wyrażeń Await (wyrażenie await) w swojej treści.An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. Ocenę można później wznowić w punkcie wstrzymania wyrażenia await przy użyciu delegata wznawiania.Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. Delegat wznawiania jest typu System.Actioni gdy jest wywoływany, oszacowanie wywołania funkcji asynchronicznej zostanie wznowione z wyrażenia await, gdzie pozostawione.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. Bieżącym obiektem wywołującym wywołania funkcji asynchronicznej jest oryginalny obiekt wywołujący, jeśli wywołanie funkcji nigdy nie zostało zawieszone lub ostatni obiekt wywołujący delegata wznawiania w przeciwnym razie.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.

Obliczanie funkcji asynchronicznej zwracającej zadaniaEvaluation of a task-returning async function

Wywołanie funkcji asynchronicznej zwracającej zadanie powoduje wygenerowanie wystąpienia zwracanego typu zadania.Invocation of a task-returning async function causes an instance of the returned task type to be generated. Jest to nazywane zadaniem zwrotnym funkcji asynchronicznej.This is called the return task of the async function. Zadanie jest początkowo w stanie niepełnym.The task is initially in an incomplete state.

Treść funkcji asynchronicznej jest następnie Szacowana do momentu, aż zostanie zawieszona (przez osiągnięcie wyrażenia await) lub przerwania, w którym formant punktu jest zwracany do obiektu wywołującego, wraz z zadaniem zwrotnym.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.

Gdy treść funkcji asynchronicznej zostanie zakończona, zadanie powrotu zostanie przeniesione z niekompletnego stanu:When the body of the async function terminates, the return task is moved out of the incomplete state:

  • Jeśli treść funkcji kończy się jako wynik osiągnięcia instrukcji return lub końca treści, każda wartość wyniku zostanie zarejestrowana w zadaniu zwrotnym, która jest przesunięta w stan powodzenie.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.
  • Jeśli treść funkcji kończy się jako wynik nieprzechwyconego wyjątku (Instrukcja throw), wyjątek jest rejestrowany w zadaniu zwrotnym, które jest umieszczane w stanie awarii.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.

Obliczanie funkcji asynchronicznej zwracającej wartość pustąEvaluation of a void-returning async function

Jeśli zwracany typ funkcji asynchronicznej jest void, Ocena różni się od powyższych w następujący sposób: ponieważ żadne zadanie nie jest zwracane, funkcja zamiast tego komunikuje zakończenie i wyjątki z kontekstem synchronizacjibieżącego wątku.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. Dokładna definicja kontekstu synchronizacji jest zależna od implementacji, ale jest reprezentacją "gdzie" bieżącego wątku jest uruchomiony.The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. Kontekst synchronizacji jest powiadamiany, gdy zostanie rozpoczęta Ocena funkcji asynchronicznej zwracającej wartość void, została zakończona pomyślnie, lub spowoduje zgłoszenie nieprzechwyconego wyjątku.The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

Pozwala to na śledzenie liczby uruchomionych w nim funkcji asynchronicznych zwracających wartość pustą oraz decydowanie o sposobie propagowania wyjątków.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.