ClassesClasses

Une classe est une structure de données qui peut contenir des données membres (constantes et champs), des fonctions membres (méthodes, propriétés, événements, indexeurs, opérateurs, constructeurs d’instance, destructeurs et constructeurs statiques) et des types imbriqués.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. Les types de classe prennent en charge l’héritage, mécanisme par lequel une classe dérivée peut étendre et spécialiser une classe de base.Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

Déclarations de classeClass declarations

Un class_declaration est un type_declaration (déclarations de type) qui déclare une nouvelle classe.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 ';'?
    ;

Un class_declaration se compose d’un ensemble facultatif d' attributs (attributs), suivi d’un ensemble facultatif de class_modifiers (modificateurs de classe), suivi d’un modificateur facultatif partial, suivi du mot clé. class et un identificateur qui nomme la classe, suivis d’un type_parameter_list facultatif (paramètres de type), suivi d’une spécification Class_base facultative (spécification debase de classe), suivie de un ensemble facultatif de type_parameter_constraints_clauses (contraintes de paramètre de type), suivi d’un class_body (corps de classe), éventuellement suivi d’un point-virgule.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.

Une déclaration de classe ne peut pas fournir type_parameter_constraints_clause, sauf si elle fournit également un type_parameter_list.A class declaration cannot supply type_parameter_constraints_clauses unless it also supplies a type_parameter_list.

Une déclaration de classe qui fournit un type_parameter_list est une déclaration de classe générique.A class declaration that supplies a type_parameter_list is a generic class declaration. En outre, toute classe imbriquée dans une déclaration de classe générique ou une déclaration de struct générique est elle-même une déclaration de classe générique, car les paramètres de type pour le type conteneur doivent être fournis pour créer un type construit.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.

Modificateurs de classeClass modifiers

Un class_declaration peut éventuellement inclure une séquence de modificateurs de classe :A class_declaration may optionally include a sequence of class modifiers:

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

Il s’agit d’une erreur de compilation pour que le même modificateur apparaisse plusieurs fois dans une déclaration de classe.It is a compile-time error for the same modifier to appear multiple times in a class declaration.

Le new modificateur est autorisé sur les classes imbriquées.The new modifier is permitted on nested classes. Il spécifie que la classe masque un membre hérité du même nom, comme décrit dans le nouveau modificateur.It specifies that the class hides an inherited member by the same name, as described in The new modifier. Il s’agit d’une erreur de compilation pour new que le modificateur apparaisse sur une déclaration de classe qui n’est pas une déclaration de classe imbriquée.It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

Les publicmodificateurs internal, protected, etprivate contrôlent l’accessibilité de la classe.The public, protected, internal, and private modifiers control the accessibility of the class. Selon le contexte dans lequel la déclaration de classe se produit, certains de ces modificateurs peuvent ne pas être autorisés (accessibilité déclarée).Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

Les abstractmodificateurs static , sealed et sont décrits dans les sections suivantes.The abstract, sealed and static modifiers are discussed in the following sections.

Classes abstraitesAbstract classes

Le abstract modificateur est utilisé pour indiquer qu’une classe est incomplète et qu’elle est destinée à être utilisée uniquement comme classe de base.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. Une classe abstraite diffère d’une classe non abstraite des façons suivantes :An abstract class differs from a non-abstract class in the following ways:

  • Une classe abstraite ne peut pas être instanciée directement, et il s’agit d’une erreur au new moment de la compilation pour utiliser l’opérateur sur une classe abstraite.An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. Bien qu’il soit possible d’avoir des variables et des valeurs dont les types au moment de la compilation sont abstraits, ces null variables et valeurs doivent être ou contenir des références à des instances de classes non abstraites dérivées des types abstraits.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.
  • Une classe abstraite est autorisée (mais pas obligatoire) à contenir des membres abstraits.An abstract class is permitted (but not required) to contain abstract members.
  • Une classe abstraite ne peut pas être sealed.An abstract class cannot be sealed.

Lorsqu’une classe non abstraite est dérivée d’une classe abstraite, la classe non abstraite doit inclure des implémentations réelles de tous les membres abstraits hérités, remplaçant ainsi ces membres abstraits.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. Dans l’exempleIn 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
    }
}

la classe A abstraite introduit une méthode Fabstraite.the abstract class A introduces an abstract method F. La B classe introduit une méthode Gsupplémentaire, mais puisqu’elle ne fournit pas d' Fimplémentation B de, doit également être déclarée comme abstract.Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. La C classeF se substitue à et fournit une implémentation réelle.Class C overrides F and provides an actual implementation. Étant donné qu’il n’y a Caucun C membre abstrait dans, est autorisé (mais pas obligatoire) à être non abstrait.Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

Classes sealedSealed classes

Le sealed modificateur est utilisé pour empêcher la dérivation à partir d’une classe.The sealed modifier is used to prevent derivation from a class. Une erreur de compilation se produit si une classe sealed est spécifiée en tant que classe de base d’une autre classe.A compile-time error occurs if a sealed class is specified as the base class of another class.

Une classe sealed ne peut pas également être une classe abstraite.A sealed class cannot also be an abstract class.

Le sealed modificateur est principalement utilisé pour empêcher la dérivation involontaire, mais il permet également certaines optimisations au moment de l’exécution.The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. En particulier, étant donné qu’une classe sealed est connue pour ne jamais avoir de classes dérivées, il est possible de transformer des appels de membre de fonction virtuelle sur des instances de classe sealed en appels non virtuels.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.

Classes statiquesStatic classes

Le static modificateur est utilisé pour marquer la classe déclarée comme une classe statique.The static modifier is used to mark the class being declared as a static class. Une classe statique ne peut pas être instanciée, elle ne peut pas être utilisée en tant que type et ne peut contenir que des membres statiques.A static class cannot be instantiated, cannot be used as a type and can contain only static members. Seule une classe statique peut contenir des déclarations de méthodes d’extension (méthodes d’extension).Only a static class can contain declarations of extension methods (Extension methods).

Une déclaration de classe statique est soumise aux restrictions suivantes :A static class declaration is subject to the following restrictions:

  • Une classe statique ne peut pas inclure sealed un abstract modificateur ou.A static class may not include a sealed or abstract modifier. Notez, toutefois, qu’étant donné qu’une classe statique ne peut pas être instanciée ou dérivée de, elle se comporte comme si elle était à la fois sealed et abstract.Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • Une classe statique ne peut pas inclure une spécification class_base (spécification de base de classe) et ne peut pas spécifier explicitement une classe de base ou une liste d’interfaces implémentées.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. Une classe statique hérite implicitement du type object.A static class implicitly inherits from type object.
  • Une classe statique ne peut contenir que des membres statiques (membres statiques et d’instance).A static class can only contain static members (Static and instance members). Notez que les constantes et les types imbriqués sont classés en tant que membres statiques.Note that constants and nested types are classified as static members.
  • Une classe statique ne peut pas avoir protected de protected internal membres avec ou d’accessibilité déclarée.A static class cannot have members with protected or protected internal declared accessibility.

Il s’agit d’une erreur au moment de la compilation pour enfreindre l’une de ces restrictions.It is a compile-time error to violate any of these restrictions.

Une classe statique n’a pas de constructeurs d’instance.A static class has no instance constructors. Il n’est pas possible de déclarer un constructeur d’instance dans une classe statique, et aucun constructeur d’instance par défaut (constructeurs par défaut) n’est fourni pour une classe statique.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.

Les membres d’une classe statique ne sont pas automatiquement statiques, et les déclarations de membre doivent static inclure explicitement un modificateur (à l’exception des constantes et des types imbriqués).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). Quand une classe est imbriquée dans une classe externe statique, la classe imbriquée n’est pas une classe statique, sauf si elle static comprend explicitement un modificateur.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.

Référencement des types de classes statiquesReferencing static class types

Un namespace_or_type_name (espace de noms et nom de type) est autorisé à faire référence à une classe statique siA namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • Namespace_or_type_name est le T dans un namespace_or_type_name de la forme T.I, ouThe namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • Namespace_or_type_name est le T dans un typeof_expression (liste d’arguments1) de la forme typeof(T).The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

Un primary_expression (fonction members) est autorisé à référencer une classe statique siA primary_expression (Function members) is permitted to reference a static class if

Dans tout autre contexte, il s’agit d’une erreur de compilation pour référencer une classe statique.In any other context it is a compile-time error to reference a static class. Par exemple, il s’agit d’une erreur pour une classe statique à utiliser comme classe de base, un type constitutif (types imbriqués) d’un membre, un argument de type générique ou une contrainte de paramètre de type.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. De même, une classe statique ne peut pas être utilisée dans un type de tableau, un new type pointeur, une expression, une is expression de Cast as , une expression sizeof , une expression, une expression ou une expression de valeur par défaut.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.

Modificateur partielPartial modifier

Le modificateur partial est utilisé pour indiquer que ce class_declaration est une déclaration de type partielle.The partial modifier is used to indicate that this class_declaration is a partial type declaration. Plusieurs déclarations de type partiel portant le même nom dans un espace de noms englobant ou une déclaration de type sont combinées pour former une déclaration de type, suivant les règles spécifiées dans les types partiels.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.

La déclaration d’une classe distribuée sur des segments distincts de texte de programme peut être utile si ces segments sont produits ou maintenus dans des contextes différents.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. Par exemple, une partie d’une déclaration de classe peut être générée par l’ordinateur, tandis que l’autre est créée manuellement.For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. La séparation du texte des deux empêche les mises à jour par l’une d’un conflit avec les mises à jour par l’autre.Textual separation of the two prevents updates by one from conflicting with updates by the other.

Paramètres de typeType parameters

Un paramètre de type est un identificateur simple qui désigne un espace réservé pour un argument de type fourni pour créer un type construit.A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. Un paramètre de type est un espace réservé formel pour un type qui sera fourni ultérieurement.A type parameter is a formal placeholder for a type that will be supplied later. En revanche, un argument de type (arguments de type) est le type réel qui remplace le paramètre de type lors de la création d’un type construit.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
    ;

Chaque paramètre de type dans une déclaration de classe définit un nom dans l’espace de déclaration (déclarations) de cette classe.Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. Par conséquent, il ne peut pas avoir le même nom qu’un autre paramètre de type ou un membre déclaré dans cette classe.Thus, it cannot have the same name as another type parameter or a member declared in that class. Un paramètre de type ne peut pas avoir le même nom que le type lui-même.A type parameter cannot have the same name as the type itself.

Spécification de base de classeClass base specification

Une déclaration de classe peut inclure une spécification class_base , qui définit la classe de base directe de la classe et les interfaces (interfaces) implémentées directement par la classe.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)*
    ;

La classe de base spécifiée dans une déclaration de classe peut être un type de classe construit (types construits).The base class specified in a class declaration can be a constructed class type (Constructed types). Une classe de base ne peut pas être un paramètre de type seul, bien qu’il puisse impliquer les paramètres de type qui se trouvent dans la portée.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

Classes de baseBase classes

Quand un class_type est inclus dans le class_base, il spécifie la classe de base directe de la classe déclarée.When a class_type is included in the class_base, it specifies the direct base class of the class being declared. Si une déclaration de classe n’a pas de class_base, ou si le class_base répertorie uniquement les types d’interface, la classe de base directe est supposée être 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. Une classe hérite des membres de sa classe de base directe, comme décrit dans héritage.A class inherits members from its direct base class, as described in Inheritance.

Dans l’exempleIn the example

class A {}

class B: A {}

la A classe est considérée comme la classe de base directe Bde et B est dite dérivée Ade.class A is said to be the direct base class of B, and B is said to be derived from A. Étant A donné que ne spécifie pas explicitement une classe de base directe, sa classe de objectbase directe est implicitement.Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

Pour un type de classe construit, si une classe de base est spécifiée dans la déclaration de classe générique, la classe de base du type construit est obtenue en substituant, pour chaque type_parameter dans la déclaration de la classe de base, la type_argument correspondante du type construit.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. Compte tenu des déclarations de classes génériquesGiven the generic class declarations

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

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

la classe de base du type G<int> construit est. B<string,int[]>the base class of the constructed type G<int> would be B<string,int[]>.

La classe de base directe d’un type de classe doit être au moins aussi accessible que le type de classe lui-même (domaines d’accessibilité).The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). Par exemple, il s’agit d’une erreur de compilation pour public qu’une classe dérive private d' internal une classe ou.For example, it is a compile-time error for a public class to derive from a private or internal class.

La classe de base directe d’un type de classe ne doit pas être de l’un System.Arraydes System.Delegatetypes suivants System.Enum:, System.ValueType, System.MulticastDelegate, ou.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. En outre, une déclaration de classe générique System.Attribute ne peut pas être utilisée en tant que classe de base directe ou indirecte.Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

Tout en déterminant la A signification de la spécification directe de la classe de base d’une classe B, la B classe de base directe de objectest temporairement supposée être.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. Intuitivement, cela garantit que la signification d’une spécification de classe de base ne peut pas dépendre de lui-même de manière récursive.Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. L’exemple suivant :The example:

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

class C : A<C.B> {}

A<C.B> est erroné object, car dans la spécification de la classe de base, la C classe de base directe de est considérée comme étant, et par conséquent (par les règles des noms de type et d’espace de noms) C n’est pas considérée comme ayant un membre 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.

Les classes de base d’un type de classe sont la classe de base directe et ses classes de base.The base classes of a class type are the direct base class and its base classes. En d’autres termes, l’ensemble de classes de base est la fermeture transitive de la relation de la classe de base directe.In other words, the set of base classes is the transitive closure of the direct base class relationship. En vous référant à l’exemple ci-dessus, B les A classes objectde base de sont et.Referring to the example above, the base classes of B are A and object. Dans l’exempleIn the example

class A {...}

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

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

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

les classes de base D<int> de C<int[]>sont B<IComparable<int[]>>, A, et object.the base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

À l’exception objectde la classe, chaque type de classe a une seule classe de base directe.Except for class object, every class type has exactly one direct base class. La object classe n’a pas de classe de base directe et est la classe de base fondamentale de toutes les autres classes.The object class has no direct base class and is the ultimate base class of all other classes.

Quand une classe B dérive d’une classe A, il s’agit d’une Berreur de compilation A pour que dépende de.When a class B derives from a class A, it is a compile-time error for A to depend on B. Une classe dépend directement de sa classe de base directe (le cas échéant) et dépend directement de la classe dans laquelle elle est immédiatement imbriquée (le cas échéant).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). À partir de cette définition, l’ensemble complet des classes dont dépend une classe est la fermeture réflexive et transitive de la relation directement dépend de la relation.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.

L’exempleThe example

class A: A {}

est erroné, car la classe dépend elle-même.is erroneous because the class depends on itself. De même, l’exempleLikewise, the example

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

est erroné, car les classes dépendent d’elles-mêmes de façon circulaire.is in error because the classes circularly depend on themselves. Enfin, l’exempleFinally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

génère une erreur au moment de la compilation A , car B.C dépend de (sa classe de base directe), B qui dépend de (sa Aclasse englobante immédiate), qui dépend de façon circulaire.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.

Notez qu’une classe ne dépend pas des classes qui sont imbriquées dans celle-ci.Note that a class does not depend on the classes that are nested within it. Dans l’exempleIn the example

class A
{
    class B: A {}
}

Bdépend de A (car A est à la fois sa classe de base directe et sa classe englobante immédiate A ), B mais ne dépend pas B de (étant donné qu’il ne s’agit ni d’une classe de base, ni d’une classe englobante de 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). Ainsi, l’exemple est valide.Thus, the example is valid.

Il n’est pas possible de dériver sealed d’une classe.It is not possible to derive from a sealed class. Dans l’exempleIn the example

sealed class A {}

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

la B classe est erronée, car elle tente de dériver Ade la sealed classe.class B is in error because it attempts to derive from the sealed class A.

Implémentations d'interfacesInterface implementations

Une spécification class_base peut inclure une liste de types d’interfaces, auquel cas la classe est dite qui implémente directement les types d’interfaces donnés.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. Les implémentations d’interface sont abordées plus en détail dans implémentations d’interface.Interface implementations are discussed further in Interface implementations.

Contraintes de paramètre de typeType parameter constraints

Les déclarations de type et de méthode génériques peuvent éventuellement spécifier des contraintes de paramètre de type en incluant 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' '(' ')'
    ;

Chaque type_parameter_constraints_clause se compose du jeton where, suivi du nom d’un paramètre de type, suivi d’un signe deux-points et de la liste de contraintes pour ce paramètre de type.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. Il peut y avoir au plus where une clause pour chaque paramètre de type, where et les clauses peuvent être listées dans n’importe quel ordre.There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. À l' get instar des jetons et set dans un accesseur where de propriété, le jeton n’est pas un mot clé.Like the get and set tokens in a property accessor, the where token is not a keyword.

La liste de contraintes donnée dans une where clause peut inclure l’un des composants suivants, dans cet ordre : une seule contrainte primaire, une ou plusieurs contraintes secondaires et la contrainte de constructeur, 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().

Une contrainte principale peut être un type de classe ou la contrainte de type référence class ou la contrainte structde type valeur.A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. Une contrainte secondaire peut être type_parameter ou INTERFACE_TYPE.A secondary constraint can be a type_parameter or interface_type.

La contrainte de type référence spécifie qu’un argument de type utilisé pour le paramètre de type doit être un type référence.The reference type constraint specifies that a type argument used for the type parameter must be a reference type. Tous les types de classe, d’interface, de délégué, de tableau et de type connu comme étant un type référence (tel que défini ci-dessous) répondent à cette contrainte.All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

La contrainte de type valeur spécifie qu’un argument de type utilisé pour le paramètre de type doit être un type valeur n’acceptant pas les valeurs NULL.The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. Tous les types struct non Nullable, les types ENUM et les paramètres de type ayant la contrainte de type valeur satisfont cette contrainte.All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Notez que, bien qu’il soit classifié comme un type valeur, un type Nullable (types Nullable) ne satisfait pas la contrainte de type valeur.Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. Un paramètre de type ayant la contrainte de type valeur ne peut pas également avoir le constructor_constraint.A type parameter having the value type constraint cannot also have the constructor_constraint.

Les types de pointeurs ne sont jamais autorisés à être des arguments de type et ne sont pas considérés comme répondant aux contraintes de type référence ou de type valeur.Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

Si une contrainte est un type de classe, un type d’interface ou un paramètre de type, ce type spécifie un « type de base » minimal que chaque argument de type utilisé pour ce paramètre de type doit prendre en charge.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. Chaque fois qu’un type construit ou une méthode générique est utilisé, l’argument de type est comparé aux contraintes sur le paramètre de type au moment de la compilation.Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. L’argument de type fourni doit satisfaire aux conditions décrites dans satisfaire les contraintes.The type argument supplied must satisfy the conditions described in Satisfying constraints.

Une contrainte class_type doit respecter les règles suivantes :A class_type constraint must satisfy the following rules:

  • Le type doit être un type de classe.The type must be a class type.
  • Le type ne doit pas sealedêtre.The type must not be sealed.
  • Le type ne doit pas être l’un des types suivants System.Array: System.Delegate, System.Enum, ou System.ValueType.The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • Le type ne doit pas objectêtre.The type must not be object. Étant donné que tous les objecttypes dérivent de, une telle contrainte n’aura aucun effet si elle était autorisée.Because all types derive from object, such a constraint would have no effect if it were permitted.
  • Au plus une contrainte pour un paramètre de type donné peut être un type de classe.At most one constraint for a given type parameter can be a class type.

Un type spécifié en tant que contrainte INTERFACE_TYPE doit respecter les règles suivantes :A type specified as an interface_type constraint must satisfy the following rules:

  • Le type doit être un type d’interface.The type must be an interface type.
  • Un type ne doit pas être spécifié plus d’une fois dans where une clause donnée.A type must not be specified more than once in a given where clause.

Dans les deux cas, la contrainte peut impliquer l’un des paramètres de type du type ou de la déclaration de méthode associés dans le cadre d’un type construit et peut impliquer le type déclaré.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.

Tout type de classe ou d’interface spécifié en tant que contrainte de paramètre de type doit être au moins aussi accessible (contraintes d’accessibilité) que le type ou la méthode générique déclaré (e).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.

Un type spécifié en tant que contrainte type_parameter doit respecter les règles suivantes :A type specified as a type_parameter constraint must satisfy the following rules:

  • Le type doit être un paramètre de type.The type must be a type parameter.
  • Un type ne doit pas être spécifié plus d’une fois dans where une clause donnée.A type must not be specified more than once in a given where clause.

En outre, il ne doit y avoir aucun cycle dans le graphique de dépendance des paramètres de type, où la dépendance est une relation transitive définie par :In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

  • Si un T paramètre de type est utilisé en tant que contrainte pour le S paramètre S de type, dépend de T.If a type parameter T is used as a constraint for type parameter S then S depends on T.
  • Si un paramètre S de type dépend d’un paramètre T de T type et dépend d’un U paramètre S de type, dépend de 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.

Compte tenu de cette relation, il s’agit d’une erreur de compilation pour qu’un paramètre de type dépende lui-même (directement ou indirectement).Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Toutes les contraintes doivent être cohérentes entre les paramètres de type dépendant.Any constraints must be consistent among dependent type parameters. Si le paramètre S de type dépend du T paramètre de type, alors :If type parameter S depends on type parameter T then:

  • Tne doit pas avoir la contrainte de type valeur.T must not have the value type constraint. Dans le T cas contraire, est S effectivement sealed, ce qui signifie qu’il Tdoit être du même type que, ce qui élimine le besoin de deux paramètres de type.Otherwise, T is effectively sealed so S would be forced to be the same type as T, eliminating the need for two type parameters.
  • Si S a la contrainte de type valeur, T ne doit pas avoir de contrainte class_type .If S has the value type constraint then T must not have a class_type constraint.
  • Si S a une contrainte class_type A et T a une contrainte class_type B, il doit y avoir une conversion d’identité ou une conversion de référence implicite de A à B ou une conversion de référence implicite de B à 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.
  • Si S dépend également du paramètre de type U et U a une contrainte class_type A et T a une contrainte class_type B, il doit y avoir une conversion d’identité ou une conversion de référence implicite de A pour B ou une conversion de référence implicite de 0 à 1.If S also depends on type parameter U and U has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

La contrainte de type S valeur et T la contrainte de type référence sont valides.It is valid for S to have the value type constraint and T to have the reference type constraint. Cela limite T en fait les types System.Object, System.ValueType System.Enum, et n’importe quel type d’interface.Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

Si la where clause d’un paramètre de type inclut une contrainte de constructeur (qui a new()la forme), il est possible d' new utiliser l’opérateur pour créer des instances du type (expressions de création d’objet).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). Tout argument de type utilisé pour un paramètre de type avec une contrainte de constructeur doit avoir un constructeur sans paramètre public (ce constructeur existe implicitement pour tout type valeur) ou être un paramètre de type ayant la contrainte de type valeur ou la contrainte de constructeur (consultez Contraintes de paramètre de type pour plus d’informations).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).

Voici quelques exemples de contraintes :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()
{
    ...
}

L’exemple suivant est une erreur, car elle provoque une circularité dans le graphique de dépendance des paramètres de type :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
{
    ...
}

Les exemples suivants illustrent des situations non valides supplémentaires :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
{
    ...
}

La classe de base effective d’un paramètre T de type est définie comme suit :The effective base class of a type parameter T is defined as follows:

  • Si T n’a pas de contraintes primaires ni de contraintes de paramètre de type, objectsa classe de base effective est.If T has no primary constraints or type parameter constraints, its effective base class is object.
  • Si T a la contrainte de type valeur, sa classe de base System.ValueTypeeffective est.If T has the value type constraint, its effective base class is System.ValueType.
  • Si T a une contrainte class_type C mais pas de contraintes type_parameter , sa classe de base effective est C.If T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • Si T n’a pas de contrainte class_type mais possède une ou plusieurs contraintes type_parameter , sa classe de base effective est le type le plus englobé (lesopérateurs de conversion levés) dans l’ensemble des classes de base effectives de son TYPE_ contraintes de paramètres.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. Les règles de cohérence garantissent qu’il existe un type le plus englobé.The consistency rules ensure that such a most encompassed type exists.
  • Si T a à la fois une contrainte class_type et une ou plusieurs contraintes type_parameter , sa classe de base effective est le type le plus englobé (lesopérateurs de conversion levés) dans le jeu constitué du class_type contrainte de T et des classes de base effectives de ses contraintes 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. Les règles de cohérence garantissent qu’il existe un type le plus englobé.The consistency rules ensure that such a most encompassed type exists.
  • Si T a la contrainte de type référence, mais pas de contraintes class_type , sa classe de base effective est object.If T has the reference type constraint but no class_type constraints, its effective base class is object.

Dans le cadre de ces règles, si T a une contrainte V qui est un Value_type, utilisez à la place le type de base le plus spécifique de V qui est un 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. Cela ne peut jamais se produire dans une contrainte donnée explicitement, mais peut se produire lorsque les contraintes d’une méthode générique sont implicitement héritées par une déclaration de méthode de substitution ou une implémentation explicite d’une méthode d’interface.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.

Ces règles garantissent que la classe de base effective est toujours un class_type.These rules ensure that the effective base class is always a class_type.

L' ensemble d’interfaces effectif d’un paramètre T de type est défini comme suit :The effective interface set of a type parameter T is defined as follows:

  • Si T n’a pas de secondary_constraints, son ensemble d’interfaces effectif est vide.If T has no secondary_constraints, its effective interface set is empty.
  • Si T a des contraintes INTERFACE_TYPE mais aucune contrainte type_parameter , son ensemble d’interfaces effectif est son ensemble de contraintes INTERFACE_TYPE .If T has interface_type constraints but no type_parameter constraints, its effective interface set is its set of interface_type constraints.
  • Si T n’a pas de contraintes INTERFACE_TYPE mais a des contraintes type_parameter , son ensemble d’interfaces effectif est l’Union des ensembles d’interfaces effectifs de ses contraintes 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.
  • Si T a des contraintes INTERFACE_TYPE et type_parameter , son ensemble d’interfaces effectif est l’Union de son ensemble de contraintes INTERFACE_TYPE et des ensembles d’interfaces effectifs de ses type_parameter contraintes.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.

Un paramètre de type est connu comme un type référence s’il a la contrainte de type référence ou sa classe de base effective object n' System.ValueTypeest pas ou.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.

Les valeurs d’un type de paramètre de type restreint peuvent être utilisées pour accéder aux membres de l’instance implicite par les contraintes.Values of a constrained type parameter type can be used to access the instance members implied by the constraints. Dans l’exempleIn the example

interface IPrintable
{
    void Print();
}

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

les méthodes de IPrintable peuvent être appelées directement sur x , T car est restreinte pour toujours implémenter IPrintable.the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Corps de la classeClass body

Le class_body d’une classe définit les membres de cette classe.The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

Types partielsPartial types

Une déclaration de type peut être fractionnée entre plusieurs déclarations de type partiel.A type declaration can be split across multiple partial type declarations. La déclaration de type est construite à partir de ses parties en suivant les règles de cette section, après quoi elle est traitée comme une déclaration unique pendant le reste du traitement au moment de la compilation et de l’exécution du programme.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.

Un class_declaration, struct_declaration ou interface_declaration représente une déclaration de type partiel s’il comprend un modificateur partial.A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partialn’est pas un mot clé et agit uniquement comme un modificateur s’il apparaît immédiatement avant l’un des classmots struct clés interface , ou dans une déclaration de type, ou void avant le type dans une déclaration de méthode.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. Dans d’autres contextes, il peut être utilisé comme un identificateur normal.In other contexts it can be used as a normal identifier.

Chaque partie d’une déclaration de type partiel doit inclure partial un modificateur.Each part of a partial type declaration must include a partial modifier. Elle doit avoir le même nom et être déclarée dans le même espace de noms ou déclaration de type que les autres parties.It must have the same name and be declared in the same namespace or type declaration as the other parts. Le partial modificateur indique que des parties supplémentaires de la déclaration de type peuvent exister ailleurs, mais que l’existence de ces parties supplémentaires n’est pas une exigence ; il est valide pour un type avec une déclaration partial unique pour inclure le modificateur.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.

Toutes les parties d’un type partiel doivent être compilées ensemble afin que les parties puissent être fusionnées au moment de la compilation dans une seule déclaration de type.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. En particulier, les types partiels n’autorisent pas l’extension des types déjà compilés.Partial types specifically do not allow already compiled types to be extended.

Les types imbriqués peuvent être déclarés dans plusieurs parties à partial l’aide du modificateur.Nested types may be declared in multiple parts by using the partial modifier. En règle générale, le type conteneur est partial déclaré à l’aide de également et chaque partie du type imbriqué est déclarée dans une autre partie du type conteneur.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.

Le partial modificateur n’est pas autorisé sur les déclarations de délégué ou d’énumération.The partial modifier is not permitted on delegate or enum declarations.

AttributsAttributes

Les attributs d’un type partiel sont déterminés en combinant, dans un ordre non spécifié, les attributs de chacune des parties.The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. Si un attribut est placé sur plusieurs parties, il équivaut à spécifier l’attribut plusieurs fois sur le type.If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. Par exemple, les deux parties :For example, the two parts:

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

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

équivalent à une déclaration telle que :are equivalent to a declaration such as:

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

Les attributs sur les paramètres de type sont combinés de manière similaire.Attributes on type parameters combine in a similar fashion.

ModificateursModifiers

Lorsqu’une déclaration de type partiel inclut une spécification d’accessibilité public(les internalmodificateurs private ,, et), protectedelle doit accepter toutes les autres parties qui incluent une spécification d’accessibilité.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. Si aucune partie d’un type partiel n’intègre une spécification d’accessibilité, le type reçoit l’accessibilité par défaut appropriée (accessibilité déclarée).If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

Si une ou plusieurs déclarations partielles d’un type imbriqué incluent un new modificateur, aucun avertissement n’est signalé si le type imbriqué masque un membre hérité (masquage par héritage).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).

Si une ou plusieurs déclarations partielles d’une classe incluent abstract un modificateur, la classe est considérée comme abstraite (classes abstraites).If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). Dans le cas contraire, la classe est considérée comme non abstraite.Otherwise, the class is considered non-abstract.

Si une ou plusieurs déclarations partielles d’une classe incluent sealed un modificateur, la classe est considérée comme sealed (classes sealed).If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). Dans le cas contraire, la classe est considérée comme non scellée.Otherwise, the class is considered unsealed.

Notez qu’une classe ne peut pas être à la fois abstract et sealed.Note that a class cannot be both abstract and sealed.

Quand le unsafe modificateur est utilisé sur une déclaration de type partiel, seul ce composant particulier est considéré comme un contexte non sécurisé (contextes non sécurisés).When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (Unsafe contexts).

Paramètres de type et contraintesType parameters and constraints

Si un type générique est déclaré dans plusieurs parties, chaque partie doit indiquer les paramètres de type.If a generic type is declared in multiple parts, each part must state the type parameters. Chaque partie doit avoir le même nombre de paramètres de type et le même nom pour chaque paramètre de type, dans l’ordre.Each part must have the same number of type parameters, and the same name for each type parameter, in order.

Lorsqu’une déclaration de type générique partielle inclut deswhere contraintes (clauses), les contraintes doivent être conformes à toutes les autres parties qui incluent des contraintes.When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. Plus précisément, chaque partie qui comprend des contraintes doit avoir des contraintes pour le même jeu de paramètres de type, et pour chaque paramètre de type, les ensembles de contraintes primaires, secondaires et de constructeurs doivent être équivalents.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. Deux ensembles de contraintes sont équivalents s’ils contiennent les mêmes membres.Two sets of constraints are equivalent if they contain the same members. Si aucune partie d’un type générique partiel ne spécifie des contraintes de paramètre de type, les paramètres de type sont considérés comme non restreints.If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

L’exempleThe 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>
{
    ...
}

est correct, car les parties qui incluent des contraintes (les deux premières) spécifient effectivement le même jeu de contraintes primaires, secondaires et de constructeur pour le même jeu de paramètres de type, respectivement.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.

Classe de baseBase class

Lorsqu’une déclaration de classe partielle inclut une spécification de classe de base, elle doit accepter toutes les autres parties qui incluent une spécification de classe de base.When a partial class declaration includes a base class specification it must agree with all other parts that include a base class specification. Si aucune partie d’une classe partielle n’intègre une spécification de classe de base, la System.Object classe de base devient (classes de base).If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

Interfaces de baseBase interfaces

L’ensemble d’interfaces de base pour un type déclaré en plusieurs parties est l’Union des interfaces de base spécifiées sur chaque composant.The set of base interfaces for a type declared in multiple parts is the union of the base interfaces specified on each part. Une interface de base particulière ne peut être nommée qu’une seule fois sur chaque partie, mais elle est autorisée pour plusieurs parties à nommer les mêmes interfaces de base.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). Il ne doit exister qu’une seule implémentation des membres d’une interface de base donnée.There must only be one implementation of the members of any given base interface.

Dans l’exempleIn the example

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

partial class C: IC {...}

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

l’ensemble d’interfaces de base pour C la IAclasse IBest, ICet.the set of base interfaces for class C is IA, IB, and IC.

En règle générale, chaque partie fournit une implémentation de l’interface déclarée sur cette partie ; Toutefois, cela n’est pas obligatoire.Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. Un composant peut fournir l’implémentation d’une interface déclarée sur une autre partie :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
{
    ...
}

MembresMembers

À l’exception des méthodes partielles (méthodes partielles), le jeu de membres d’un type déclaré en plusieurs parties est simplement l’Union de l’ensemble des membres déclarés dans chaque partie.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. Les corps de toutes les parties de la déclaration de type partagent le même espace de déclaration (déclarations) et l’étendue de chaque membre (portées) s’étend aux corps de toutes les parties.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. Le domaine d’accessibilité de n’importe quel membre comprend toujours toutes les parties du type englobant ; un private membre déclaré dans une partie est librement accessible à partir d’une autre partie.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. Il s’agit d’une erreur au moment de la compilation pour déclarer le même membre dans plusieurs parties du type, sauf si ce membre est un type partial avec le modificateur.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;
    }
}

L’ordonnancement des membres dans un type est rarement significatif C# pour le code, mais peut être significatif lors de l’interfaçage avec d’autres langages et environnements.The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. Dans ce cas, l’ordre des membres dans un type déclaré dans plusieurs parties n’est pas défini.In these cases, the ordering of members within a type declared in multiple parts is undefined.

Méthodes partiellesPartial methods

Les méthodes partielles peuvent être définies dans une partie d’une déclaration de type et implémentées dans une autre.Partial methods can be defined in one part of a type declaration and implemented in another. L’implémentation est facultative ; Si aucune partie n’implémente la méthode partielle, la déclaration de méthode partielle et tous les appels à celle-ci sont supprimés de la déclaration de type résultant de la combinaison des parties.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.

Les méthodes partielles ne peuvent pas définir de modificateurs d' privateaccès, mais sont implicitement.Partial methods cannot define access modifiers, but are implicitly private. Leur type de retour doit voidêtre, et leurs paramètres ne peuvent out pas avoir le modificateur.Their return type must be void, and their parameters cannot have the out modifier. L’identificateur partial est reconnu comme un mot clé spécial dans une déclaration de méthode uniquement s’il apparaît juste avant void le type ; sinon, il peut être utilisé comme un identificateur normal.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. Une méthode partielle ne peut pas implémenter explicitement des méthodes d’interface.A partial method cannot explicitly implement interface methods.

Il existe deux types de déclarations de méthode partielles : Si le corps de la déclaration de méthode est un point-virgule, la déclaration est dite « définition de déclaration de méthode partielle».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. Si le corps est fourni en tant que bloc, la déclaration est considérée comme une déclaration de méthode partielle d’implémentation.If the body is given as a block, the declaration is said to be an implementing partial method declaration. Dans les parties d’une déclaration de type, il ne peut y avoir qu’une seule déclaration de méthode partielle avec une signature donnée, et il ne peut y avoir qu’une seule déclaration de méthode partielle implémentée avec une signature donnée.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. Si une déclaration de méthode partielle d’implémentation est fournie, une déclaration de méthode partielle de définition correspondante doit exister et les déclarations doivent correspondre comme indiqué dans l’exemple suivant :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:

  • Les déclarations doivent avoir les mêmes modificateurs (mais pas nécessairement dans le même ordre), le nom de la méthode, le nombre de paramètres de type et le nombre de paramètres.The declarations must have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters.
  • Les paramètres correspondants dans les déclarations doivent avoir les mêmes modificateurs (mais pas nécessairement dans le même ordre) et les mêmes types (différences modulo dans les noms de paramètres de type).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).
  • Les paramètres de type correspondants dans les déclarations doivent avoir les mêmes contraintes (différences modulo dans les noms de paramètre de type).Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

Une déclaration de méthode partielle d’implémentation peut apparaître dans la même partie que la déclaration de méthode partielle de définition correspondante.An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

Seule une méthode partielle de définition participe à la résolution de surcharge.Only a defining partial method participates in overload resolution. Par conséquent, qu’une déclaration d’implémentation soit donnée ou non, les expressions d’appel peuvent être résolues en appels de la méthode partielle.Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Comme une méthode partielle retourne voidtoujours, ces expressions d’appel sont toujours des instructions d’expression.Because a partial method always returns void, such invocation expressions will always be expression statements. En outre, étant donné qu’une méthode partielle privateest implicitement, ces instructions sont toujours exécutées dans l’une des parties de la déclaration de type dans laquelle la méthode partielle est déclarée.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.

Si aucune partie d’une déclaration de type partiel ne contient une déclaration d’implémentation pour une méthode partielle donnée, toute instruction d’expression qui l’appelle est simplement supprimée de la déclaration de type combiné.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. Ainsi, l’expression d’appel, y compris toutes les expressions constitutives, n’a aucun effet au moment de l’exécution.Thus the invocation expression, including any constituent expressions, has no effect at run-time. La méthode partielle elle-même est également supprimée et ne sera pas membre de la déclaration de type combiné.The partial method itself is also removed and will not be a member of the combined type declaration.

Si une déclaration d’implémentation existe pour une méthode partielle donnée, les appels des méthodes partielles sont conservés.If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. La méthode partielle donne lieu à une déclaration de méthode similaire à la déclaration de méthode partielle d’implémentation, à l’exception des éléments suivants :The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • Le partial modificateur n’est pas inclusThe partial modifier is not included
  • Les attributs de la déclaration de méthode résultante sont les attributs combinés de la définition et de la déclaration de méthode partielle d’implémentation dans un ordre non spécifié.The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. Les doublons ne sont pas supprimés.Duplicates are not removed.
  • Les attributs sur les paramètres de la déclaration de méthode résultante sont les attributs combinés des paramètres correspondants de la définition et de la déclaration de méthode partielle d’implémentation dans un ordre non spécifié.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. Les doublons ne sont pas supprimés.Duplicates are not removed.

Si une déclaration de définition, mais pas une déclaration d’implémentation, est fournie pour une méthode partielle M, les restrictions suivantes s’appliquent :If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

Les méthodes partielles sont utiles pour permettre à une partie d’une déclaration de type de personnaliser le comportement d’une autre partie, par exemple, celle qui est générée par un outil.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. Considérons la déclaration de classe partielle suivante :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();
}

Si cette classe est compilée sans autre partie, les déclarations de méthode partielle de définition et leurs appels seront supprimés, et la déclaration de classe combinée résultante sera équivalente à ce qui suit :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; }
    }
}

Supposons qu’une autre partie est fournie, toutefois, qui fournit des déclarations d’implémentation des méthodes partielles :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);
    }
}

La déclaration de classe combinée résultante est alors équivalente à ce qui suit :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);
    }
}

Liaison de nomName binding

Bien que chaque partie d’un type extensible doive être déclarée dans le même espace de noms, les parties sont généralement écrites dans des déclarations d’espaces de noms différentes.Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. Ainsi, différentes using directives (directives using) peuvent être présentes pour chaque partie.Thus, different using directives (Using directives) may be present for each part. Lors de l’interprétation de noms simples (inférence de type) dans une seule using partie, seules les directives de la ou des déclarations d’espace de noms englobant cette partie sont prises en compte.When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. Cela peut entraîner un même identificateur ayant des significations différentes dans différentes parties :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
    }
}

Membres de classeClass members

Les membres d’une classe se composent des membres introduits par ses class_member_declarations et des membres hérités de la classe de base directe.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
    ;

Les membres d’un type de classe sont répartis dans les catégories suivantes :The members of a class type are divided into the following categories:

  • Les constantes, qui représentent des valeurs constantes associées à la classe (constantes).Constants, which represent constant values associated with the class (Constants).
  • Les champs, qui sont les variables de la classe (champs).Fields, which are the variables of the class (Fields).
  • Les méthodes qui implémentent les calculs et les actions qui peuvent être effectuées par la classe (méthodes).Methods, which implement the computations and actions that can be performed by the class (Methods).
  • Propriétés, qui définissent les caractéristiques nommées et les actions associées à la lecture et à l’écriture de ces caractéristiques (Propriétés).Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • Les événements, qui définissent des notifications qui peuvent être générées par la classe (événements).Events, which define notifications that can be generated by the class (Events).
  • Les indexeurs, qui autorisent l’indexation des instances de la classe de la même façon (syntaxiquement) en tant que tableaux (indexeurs).Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • Les opérateurs, qui définissent les opérateurs d’expression qui peuvent être appliqués aux instances de la classe (opérateurs).Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • Constructeurs d’instance, qui implémentent les actions requises pour initialiser les instances de la classe (constructeurs d’instance)Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • Les destructeurs, qui implémentent les actions à effectuer avant que les instances de la classe soient définitivement ignorées (destructeurs).Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • Constructeurs statiques, qui implémentent les actions requises pour initialiser la classe elle-même (constructeurs statiques).Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • Types, qui représentent les types qui sont locaux pour la classe (types imbriqués).Types, which represent the types that are local to the class (Nested types).

Les membres qui peuvent contenir du code exécutable sont collectivement appelés membres de fonction du type de classe.Members that can contain executable code are collectively known as the function members of the class type. Les fonctions membres d’un type de classe sont les méthodes, les propriétés, les événements, les indexeurs, les opérateurs, les constructeurs d’instance, les destructeurs et les constructeurs statiques de ce type de classe.The function members of a class type are the methods, properties, events, indexers, operators, instance constructors, destructors, and static constructors of that class type.

Un class_declaration crée un nouvel espace de déclaration (déclarations) et le class_member_declarations contenu immédiatement dans le class_declaration introduit de nouveaux membres dans cet espace de déclaration.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. Les règles suivantes s’appliquent à class_member_declarations :The following rules apply to class_member_declarations:

  • Les constructeurs d’instance, les destructeurs et les constructeurs statiques doivent avoir le même nom que la classe immédiatement englobante.Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. Tous les autres membres doivent avoir des noms qui diffèrent du nom de la classe englobante immédiatement.All other members must have names that differ from the name of the immediately enclosing class.
  • Le nom d’une constante, d’un champ, d’une propriété, d’un événement ou d’un type doit différer des noms de tous les autres membres déclarés dans la même classe.The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • Le nom d’une méthode doit être différent des noms de toutes les autres méthodes non déclarées dans la même classe.The name of a method must differ from the names of all other non-methods declared in the same class. En outre, la signature (signatures et surcharge) d’une méthode doit être différente des signatures de toutes les autres méthodes déclarées dans la même classe, et deux méthodes déclarées dans la même classe ne peuvent pas avoir de signatures qui ref diffèrent uniquement par et 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.
  • La signature d’un constructeur d’instance doit être différente des signatures de tous les autres constructeurs d’instance déclarés dans la même classe, et deux constructeurs déclarés dans la même classe ne peuvent pas avoir de ref signatures outqui diffèrent uniquement par et.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.
  • La signature d’un indexeur doit être différente des signatures de tous les autres indexeurs déclarés dans la même classe.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • La signature d’un opérateur doit être différente des signatures de tous les autres opérateurs déclarés dans la même classe.The signature of an operator must differ from the signatures of all other operators declared in the same class.

Les membres hérités d’un type de classe (héritage) ne font pas partie de l’espace de déclaration d’une classe.The inherited members of a class type (Inheritance) are not part of the declaration space of a class. Par conséquent, une classe dérivée est autorisée à déclarer un membre avec le même nom ou la même signature qu’un membre hérité (qui, en effet, masque le membre hérité).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).

Type d’instanceThe instance type

Chaque déclaration de classe a un type dépendant associé (types liés et indépendants), le type d’instance.Each class declaration has an associated bound type (Bound and unbound types), the instance type. Pour une déclaration de classe générique, le type d’instance est formé en créant un type construit (types construits) à partir de la déclaration de type, avec chacun des arguments de type fournis étant le paramètre de type correspondant.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. Étant donné que le type d’instance utilise les paramètres de type, il ne peut être utilisé que lorsque les paramètres de type sont dans la portée ; autrement dit, à l’intérieur de la déclaration de classe.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. Le type d’instance est le type this de pour le code écrit à l’intérieur de la déclaration de classe.The instance type is the type of this for code written inside the class declaration. Pour les classes non génériques, le type d’instance est simplement la classe déclarée.For non-generic classes, the instance type is simply the declared class. L’exemple suivant illustre plusieurs déclarations de classe avec leurs types d’instance :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

Membres des types construitsMembers of constructed types

Les membres non hérités d’un type construit sont obtenus en substituant, pour chaque type_parameter dans la déclaration de membre, les type_argument correspondants du type construit.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. Le processus de substitution est basé sur la signification sémantique des déclarations de type et n’est pas simplement une substitution textuelle.The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

Par exemple, à partir de la déclaration de classe génériqueFor 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) {...}
}

le type Gen<int[],IComparable<string>> construit contient les membres suivants :the constructed type Gen<int[],IComparable<string>> has the following members:

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

a Le type du membre dans la Déclaration Gen de classe générique est « tableau à deux dimensions de T». par conséquent, le type du a membre dans le type construit ci-dessus est «tableau à deux dimensions d’un tableau unidimensionnel de int"ou 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[,][].

Dans les membres de fonction d’instance, this le type de est le type d’instance (type d’instance) de la déclaration conteneur.Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

Tous les membres d’une classe générique peuvent utiliser des paramètres de type de n’importe quelle classe englobante, directement ou dans le cadre d’un type construit.All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. Lorsqu’un type construit fermé particulier (types ouverts et fermés) est utilisé au moment de l’exécution, chaque utilisation d’un paramètre de type est remplacée par l’argument de type réel fourni au type construit.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. Exemple :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
    }
}

HéritageInheritance

Une classe hérite des membres de son type de classe de base directe.A class inherits the members of its direct base class type. L’héritage signifie qu’une classe contient implicitement tous les membres de son type de classe de base directe, à l’exception des constructeurs d’instance, des destructeurs et des constructeurs statiques de la classe de base.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. Voici quelques aspects importants de l’héritage :Some important aspects of inheritance are:

  • L’héritage est transitif.Inheritance is transitive. Si C est dérivé de B, et B est dérivé de A, C hérite des membres déclarés dans B , ainsi que des membres déclarés Adans.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.
  • Une classe dérivée étend sa classe de base directe.A derived class extends its direct base class. Une classe dérivée peut ajouter des membres hérités, mais ne peut pas supprimer la définition d’un membre hérité.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • Les constructeurs d’instance, les destructeurs et les constructeurs statiques ne sont pas hérités, mais tous les autres membres sont, indépendamment de leur accessibilité déclarée (accès aux membres).Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). Toutefois, selon l’accessibilité déclarée, les membres hérités peuvent ne pas être accessibles dans une classe dérivée.However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • Une classe dérivée peut Masquer les membres hérités (masquage par héritage) en déclarant de nouveaux membres portant le même nom ou la même signature.A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. Notez cependant que le masquage d’un membre hérité ne supprime pas ce membre ; il rend simplement ce membre inaccessible directement via la classe dérivée.Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • Une instance d’une classe contient un ensemble de tous les champs d’instance déclarés dans la classe et ses classes de base, et une conversion implicite (conversions de référence implicites) existe d’un type de classe dérivée vers l’un de ses types de classe de base.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. Ainsi, une référence à une instance d’une classe dérivée peut être traitée comme une référence à une instance de l’une de ses classes de base.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.
  • Une classe peut déclarer des méthodes, des propriétés et des indexeurs virtuels, et les classes dérivées peuvent substituer l’implémentation de ces membres de fonction.A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. Cela permet aux classes d’exposer un comportement polymorphe dans lequel les actions effectuées par un appel de membre de fonction varient en fonction du type d’exécution de l’instance par le biais duquel cette fonction membre est appelée.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.

Le membre hérité d’un type de classe construit sont les membres du type de classe de base immédiat (classes de base), qui est trouvé en substituant les arguments de type du type construit pour chaque occurrence des paramètres de type correspondants dans le spécification 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. Ces membres, à leur tour, sont transformés en substituant, pour chaque type_parameter dans la déclaration de membre, les type_argument correspondants de la spécification 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) {...}
}

Dans l’exemple ci-dessus, le D<int> type construit a un membre public int G(string s) non hérité obtenu en substituant l’argument int de type pour le Tparamètre de type.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>a également un membre hérité de la Déclaration Bde classe.D<int> also has an inherited member from the class declaration B. Ce membre hérité est déterminé par la première B<int[]> détermination du type de classe de base de int D<int> en T remplaçant dans la spécification B<T[]>de la classe de base.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[]>. Ensuite, en tant qu’argument de Btype int[] pour, est substitué U à public U F(long index)dans, ce qui donne le public int[] F(long index)membre hérité.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).

Le nouveau modificateurThe new modifier

Un class_member_declaration est autorisé à déclarer un membre avec le même nom ou la même signature qu’un membre hérité.A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. Dans ce cas, le membre de la classe dérivée est dit de Masquer le membre de la classe de base.When this occurs, the derived class member is said to hide the base class member. Le masquage d’un membre hérité n’est pas considéré comme une erreur, mais il entraîne l’émission d’un avertissement par le compilateur.Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. Pour supprimer l’avertissement, la déclaration du membre de classe dérivée peut inclure new un modificateur pour indiquer que le membre dérivé est destiné à masquer le membre de base.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. Cette rubrique est traitée plus en détail dans Masquer par héritage.This topic is discussed further in Hiding through inheritance.

Si un new modificateur est inclus dans une déclaration qui ne masque pas un membre hérité, un avertissement est émis à cet effet.If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. Cet avertissement est supprimé en supprimant le new modificateur.This warning is suppressed by removing the new modifier.

Modificateurs d’accèsAccess modifiers

Un class_member_declaration peut avoir l’un des cinq types possibles d’accessibilité déclarée (accessibilité déclarée) : public, protected internal, protected, internal ou 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. À l’exception protected internal de la combinaison, il s’agit d’une erreur de compilation pour spécifier plusieurs modificateurs d’accès.Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. Quand un class_member_declaration n’inclut pas de modificateur d’accès, private est utilisé.When a class_member_declaration does not include any access modifiers, private is assumed.

Types constitutifsConstituent types

Les types qui sont utilisés dans la déclaration d’un membre sont appelés types constitutifs de ce membre.Types that are used in the declaration of a member are called the constituent types of that member. Les types constitutifs possibles sont le type d’une constante, d’un champ, d’une propriété, d’un événement ou d’un indexeur, le type de retour d’une méthode ou d’un opérateur, ainsi que les types de paramètres d’une méthode, d’un indexeur, d’un opérateur ou d’un constructeur d’instance.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. Les types constitutifs d’un membre doivent être au moins aussi accessibles que ce membre lui-même (contraintes d’accessibilité).The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

Membres statiques et d’instanceStatic and instance members

Les membres d’une classe sont soit des membres statiques , soit des membres d’instance.Members of a class are either static members or instance members. En général, il est utile de considérer que les membres statiques appartiennent à des types de classe et des membres d’instance comme appartenant à des objets (instances de types de classe).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).

Lorsqu’une déclaration de champ, de méthode, de propriété, d’événement, d’opérateur static ou de constructeur comprend un modificateur, elle déclare un membre statique.When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. En outre, une déclaration de constante ou de type déclare implicitement un membre statique.In addition, a constant or type declaration implicitly declares a static member. Les membres statiques présentent les caractéristiques suivantes :Static members have the following characteristics:

  • Lorsqu’un membre statique M est référencé dans un member_access (accès aux membres) de la forme E.M, E doit désigner un type contenant 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. Il s’agit d’une erreur au moment E de la compilation pour qu’une instance soit dénotée.It is a compile-time error for E to denote an instance.
  • Un champ statique identifie exactement un emplacement de stockage à partager par toutes les instances d’un type de classe fermé donné.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. Quel que soit le nombre d’instances d’un type de classe fermé donné, il n’existe qu’une seule copie d’un champ statique.No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.
  • Un membre de fonction statique (méthode, propriété, événement, opérateur ou constructeur) ne fonctionne pas sur une instance spécifique, et il s’agit d’une erreur de compilation pour faire this référence à dans ce type de membre de fonction.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.

Quand un champ, une méthode, une propriété, un événement, un indexeur, un constructeur ou une déclaration de static destructeur n’inclut pas de modificateur, il déclare un membre d’instance.When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (Un membre d’instance est parfois appelé membre non statique.) Les membres d’instance présentent les caractéristiques suivantes :(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • Lorsqu’un membre d’instance M est référencé dans un member_access (accès aux membres) de la forme E.M, E doit désigner une instance d’un type contenant 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. Il s’agit d’une erreur de liaison E au moment de la liaison pour indiquer un type.It is a binding-time error for E to denote a type.
  • Chaque instance d’une classe contient un ensemble distinct de tous les champs d’instance de la classe.Every instance of a class contains a separate set of all instance fields of the class.
  • Un membre de fonction d’instance (méthode, propriété, indexeur, constructeur d’instance ou destructeur) opère sur une instance donnée de la classe, et cette instance est accessible en this tant que (cet accès).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).

L’exemple suivant illustre les règles d’accès aux membres statiques et d’instance :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
    }
}

La méthode F montre que dans une fonction membre d’instance, un simple_name (noms simples) peut être utilisé pour accéder aux membres d’instance et aux membres statiques.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. La méthode G montre que dans une fonction membre statique, il s’agit d’une erreur de compilation pour accéder à un membre d’instance via un 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. La méthode Main indique que dans un member_access (accès aux membres), les membres d’instance doivent être accessibles via des instances et les membres statiques doivent être accessibles par le biais de types.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.

Types imbriquésNested types

Un type déclaré dans une déclaration de classe ou de struct est appelé un type imbriqué.A type declared within a class or struct declaration is called a nested type. Un type déclaré dans une unité de compilation ou un espace de noms est appelé type non imbriqué.A type that is declared within a compilation unit or namespace is called a non-nested type.

Dans l’exempleIn the example

using System;

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

la B classe est un type imbriqué, car elle est déclarée Adans la classe A , et la classe est un type non imbriqué, car elle est déclarée dans une unité de compilation.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.

Nom completFully qualified name

Le nom qualifié complet (noms qualifiés complets) pour un type imbriqué est S.NS est le nom qualifié complet du type dans lequel le type N est déclaré.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.

Accessibilité déclaréeDeclared accessibility

Les types non imbriqués peuvent avoir public ou internal déclarés l’accessibilité internal et avoir déclaré l’accessibilité par défaut.Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. Les types imbriqués peuvent également avoir ces formes d’accessibilité déclarée, plus une ou plusieurs formes supplémentaires d’accessibilité déclarée, selon que le type conteneur est une classe ou un struct :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:

  • Un type imbriqué qui est déclaré dans une classe peut avoir l’une des cinq formes d’accessibilité déclaréepublic( protected internal, protected, internal, ou private) et, comme les autres membres de classe, la private valeur par défaut est déclarée aux.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.
  • Un type imbriqué qui est déclaré dans un struct peut avoir l’une des trois formes d’accessibilité déclaréepublic( internal, ou private) et, private comme les autres membres de struct, a comme valeur par défaut l’accessibilité déclarée.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.

L’exempleThe 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 {...} }
}

déclare une classe Nodeimbriquée privée.declares a private nested class Node.

MasquageHiding

Un type imbriqué peut masquer (nom masquant) un membre de base.A nested type may hide (Name hiding) a base member. Le new modificateur est autorisé sur les déclarations de type imbriqué afin que le masquage puisse être exprimé explicitement.The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. L’exempleThe 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();
    }
}

affiche une classe M imbriquée qui masque la méthode M définie dans Base.shows a nested class M that hides the method M defined in Base.

Cet accèsthis access

Un type imbriqué et son type conteneur n’ont pas de relation spéciale en ce qui concerne This_Access (cet accès).A nested type and its containing type do not have a special relationship with regard to this_access (This access). En particulier this , dans un type imbriqué ne peut pas être utilisé pour faire référence aux membres d’instance du type conteneur.Specifically, this within a nested type cannot be used to refer to instance members of the containing type. Dans les cas où un type imbriqué doit accéder aux membres de l’instance de son type conteneur, l’accès peut être fourni en this fournissant le pour l’instance du type conteneur comme argument de constructeur pour le type imbriqué.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. L’exemple suivantThe 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();
    }
}

illustre cette technique.shows this technique. Une instance de C crée une instance de Nested et passe son propre this au Nestedconstructeur de pour fournir l’accès ultérieur aux Cmembres d’instance de.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.

Accès aux membres privés et protégés du type conteneurAccess to private and protected members of the containing type

Un type imbriqué a accès à tous les membres qui sont accessibles à son type conteneur, y compris les membres du type conteneur qui ont private et protected l’accessibilité déclarée.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. L’exempleThe 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();
    }
}

affiche une classe C qui contient une classe Nestedimbriquée.shows a class C that contains a nested class Nested. Dans Nested, la méthode G appelle la méthode F statique définie dans Cet F a une accessibilité déclarée privée.Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

Un type imbriqué peut également accéder à des membres protégés définis dans un type de base de son type conteneur.A nested type also may access protected members defined in a base type of its containing type. Dans l’exempleIn 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();
    }
}

la Derived.Nested classe imbriquée accède à la méthode Derived F protégée définie Basedans la classe de base de,, en appelant par le Derivedbiais d’une instance de.the nested class Derived.Nested accesses the protected method F defined in Derived's base class, Base, by calling through an instance of Derived.

Types imbriqués dans les classes génériquesNested types in generic classes

Une déclaration de classe générique peut contenir des déclarations de type imbriqué.A generic class declaration can contain nested type declarations. Les paramètres de type de la classe englobante peuvent être utilisés dans les types imbriqués.The type parameters of the enclosing class can be used within the nested types. Une déclaration de type imbriquée peut contenir des paramètres de type supplémentaires qui s’appliquent uniquement au type imbriqué.A nested type declaration can contain additional type parameters that apply only to the nested type.

Chaque déclaration de type contenue dans une déclaration de classe générique est implicitement une déclaration de type générique.Every type declaration contained within a generic class declaration is implicitly a generic type declaration. Lors de l’écriture d’une référence à un type imbriqué dans un type générique, le type construit contenant, y compris ses arguments de type, doit être nommé.When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. Toutefois, à partir de la classe externe, le type imbriqué peut être utilisé sans qualification ; le type d’instance de la classe externe peut être utilisé implicitement lors de la construction du type imbriqué.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. L’exemple suivant montre trois façons correctes de faire référence à un type construit créé Innerà partir de ; les deux premiers sont équivalents :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
    }
}

Bien qu’il s’agisse d’un style de programmation incorrect, un paramètre de type dans un type imbriqué peut masquer un membre ou un paramètre de type déclaré dans le type externe :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
    }
}

Noms de membres réservésReserved member names

Pour faciliter l’implémentation C# sous-jacente au moment de l’exécution, pour chaque déclaration de membre source qui est une propriété, un événement ou un indexeur, l’implémentation doit réserver deux signatures de méthode selon le type de la déclaration de membre, son nom et son type.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. Il s’agit d’une erreur de compilation pour qu’un programme déclare un membre dont la signature correspond à l’une de ces signatures réservées, même si l’implémentation au moment de l’exécution sous-jacente n’utilise pas ces réservations.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.

Les noms réservés n’introduisent pas de déclarations, donc ils ne participent pas à la recherche de membres.The reserved names do not introduce declarations, thus they do not participate in member lookup. Toutefois, les signatures de méthode réservée associées à une déclaration participent à l’héritage (héritage) et peuvent être masquées avec le new modificateur (le nouveau modificateur).However, a declaration's associated reserved method signatures do participate in inheritance (Inheritance), and can be hidden with the new modifier (The new modifier).

La réservation de ces noms remplit trois fonctions :The reservation of these names serves three purposes:

  • Pour permettre à l’implémentation sous-jacente d’utiliser un identificateur ordinaire comme nom de méthode pour obtenir ou définir l' C# accès à la fonctionnalité de langage.To allow the underlying implementation to use an ordinary identifier as a method name for get or set access to the C# language feature.
  • Pour permettre à d’autres langages d’interagir à l’aide d’un identificateur ordinaire comme nom de méthode pour obtenir ou C# définir l’accès à la fonctionnalité de langage.To allow other languages to interoperate using an ordinary identifier as a method name for get or set access to the C# language feature.
  • Pour garantir que la source acceptée par un compilateur conforme est acceptée par un autre, en rendant les détails des noms de membres réservés cohérents dans toutes les C# implémentations.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.

La déclaration d’un destructeur (destructeurs) entraîne également la réservation d’une signature (noms de membres réservés aux destructeurs).The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

Noms de membres réservés pour les propriétésMember names reserved for properties

Pour une propriété P (Propriétés) de type T, les signatures suivantes sont réservées :For a property P (Properties) of type T, the following signatures are reserved:

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

Les deux signatures sont réservées, même si la propriété est en lecture seule ou en écriture seule.Both signatures are reserved, even if the property is read-only or write-only.

Dans l’exempleIn 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());
    }
}

une classe A définit une propriété Pen lecture seule, en réservant des signatures get_P pour set_P les méthodes et.a class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. Une classe B dérive A de et masque ces deux signatures réservées.A class B derives from A and hides both of these reserved signatures. L’exemple produit la sortie :The example produces the output:

123
123
456

Noms de membres réservés pour les événementsMember names reserved for events

Pour un événement E (événements) de type Tdélégué, les signatures suivantes sont réservées :For an event E (Events) of delegate type T, the following signatures are reserved:

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

Noms de membres réservés pour les indexeursMember names reserved for indexers

Pour un indexeur (indexeurs) de type T avec Parameter-List L, les signatures suivantes sont réservées :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);

Les deux signatures sont réservées, même si l’indexeur est en lecture seule ou en écriture seule.Both signatures are reserved, even if the indexer is read-only or write-only.

En outre, le Item nom du membre est réservé.Furthermore the member name Item is reserved.

Noms de membres réservés pour les destructeursMember names reserved for destructors

Pour une classe contenant un destructeur (destructeurs), la signature suivante est réservée :For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

ConstantesConstants

Une constante est un membre de classe qui représente une valeur de constante : valeur qui peut être calculée au moment de la compilation.A constant is a class member that represents a constant value: a value that can be computed at compile-time. Un constant_declaration introduit une ou plusieurs constantes d’un type donné.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
    ;

Un constant_declaration peut inclure un ensemble d' attributs (attributs), un modificateur new (le nouveau modificateur) et une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès).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). Les attributs et les modificateurs s’appliquent à tous les membres déclarés par constant_declaration.The attributes and modifiers apply to all of the members declared by the constant_declaration. Bien que les constantes soient considérées comme des membres statiques, un constant_declaration ne requiert pas et n’autorise pas un modificateur static.Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. Il s’agit d’une erreur pour que le même modificateur apparaisse plusieurs fois dans une déclaration de constante.It is an error for the same modifier to appear multiple times in a constant declaration.

Le type d’un constant_declaration spécifie le type des membres introduits par la déclaration.The type of a constant_declaration specifies the type of the members introduced by the declaration. Le type est suivi d’une liste de constant_declarators, chacun d’entre eux introduisant un nouveau membre.The type is followed by a list of constant_declarators, each of which introduces a new member. Un constant_declarator se compose d’un identificateur qui nomme le membre, suivi d’un jeton « = », suivi d’un constant_expression (expressions constantes) qui donne la valeur du membre.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.

Le type spécifié dans une déclaration de constante doit être sbyte, byte, short, ushort, int, uint, long, ulong, char, 0, 1, 2, 3, 4, enum_typeou reference_ tapez.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. Chaque constant_expression doit produire une valeur du type cible ou d’un type qui peut être converti vers le type cible par une conversion implicite (conversions implicites).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).

Le type d’une constante doit être au moins aussi accessible que la constante elle-même (contraintes d’accessibilité).The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

La valeur d’une constante est obtenue dans une expression à l’aide d’un simple_name (noms simples) ou d’un member_access (accès aux membres).The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

Une constante peut elle-même participer à un constant_expression.A constant can itself participate in a constant_expression. Par conséquent, une constante peut être utilisée dans n’importe quelle construction qui requiert un constant_expression.Thus, a constant may be used in any construct that requires a constant_expression. Les exemples de ces constructions incluent case les étiquettes goto case , les enum instructions, les déclarations de membre, les attributs et d’autres déclarations de constante.Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

Comme décrit dans expressions constantes, un constant_expression est une expression qui peut être complètement évaluée au moment de la compilation.As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. Étant donné que la seule façon de créer une valeur non null d’un reference_type autre que string consiste à appliquer l’opérateur new, et puisque l’opérateur new n’est pas autorisé dans un constant_expression, la seule valeur possible pour les constantes de les reference_typesautres que string sont 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.

Lorsqu’un nom symbolique pour une valeur constante est souhaité, mais lorsque le type de cette valeur n’est pas autorisé dans une déclaration de constante, ou lorsque la valeur ne peut pas être calculée au moment de la compilation par un constant_expression, un champ readonly (champs en lecture seule) peut à utiliser à la place.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.

Une déclaration de constante qui déclare plusieurs constantes équivaut à plusieurs déclarations de constantes uniques avec les mêmes attributs, modificateurs et type.A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Exemple :For example

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

est équivalent àis equivalent to

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

Les constantes sont autorisées à dépendre d’autres constantes au sein du même programme tant que les dépendances ne sont pas de nature circulaire.Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. Le compilateur s’arrange automatiquement pour évaluer les déclarations de constantes dans l’ordre approprié.The compiler automatically arranges to evaluate the constant declarations in the appropriate order. Dans l’exempleIn 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;
}

le compilateur A.Yévalue, puis B.Zévalue et évalue A.XEnfin, en produisant les valeurs 10, 11et 12.the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. Les déclarations de constante peuvent dépendre de constantes d’autres programmes, mais ces dépendances ne sont possibles que dans une seule direction.Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. En A B B.Z B.Z vousréférantàl'A.Yexemple ci-dessus, si et ont été déclarés dans des programmes distincts, il serait possible quedépendede,maisnedépendaitpassimultanémentde.A.XReferring 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.

ChampsFields

Un champ est un membre qui représente une variable associée à un objet ou une classe.A field is a member that represents a variable associated with an object or class. Un field_declaration introduit un ou plusieurs champs d’un type donné.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
    ;

Un field_declaration peut inclure un ensemble d' attributs (attributs), un modificateur new (le nouveau modificateur), une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès) et un modificateur static ( Champs d’instance et statiques).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). En outre, un field_declaration peut inclure un modificateur readonly (champs ReadOnly) ou un modificateur volatile (champs volatiles), mais pas les deux.In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. Les attributs et les modificateurs s’appliquent à tous les membres déclarés par field_declaration.The attributes and modifiers apply to all of the members declared by the field_declaration. Il s’agit d’une erreur pour que le même modificateur apparaisse plusieurs fois dans une déclaration de champ.It is an error for the same modifier to appear multiple times in a field declaration.

Le type d’un field_declaration spécifie le type des membres introduits par la déclaration.The type of a field_declaration specifies the type of the members introduced by the declaration. Le type est suivi d’une liste de variable_declarators, chacun d’entre eux introduisant un nouveau membre.The type is followed by a list of variable_declarators, each of which introduces a new member. Un variable_declarator se compose d’un identificateur qui nomme ce membre, éventuellement suivi d’un jeton « = » et d’un variable_initializer (initialiseurs de variable) qui donne la valeur initiale de ce membre.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.

Le type d’un champ doit être au moins aussi accessible que le champ lui-même (contraintes d’accessibilité).The type of a field must be at least as accessible as the field itself (Accessibility constraints).

La valeur d’un champ est obtenue dans une expression à l’aide d’un simple_name (noms simples) ou d’un member_access (accès aux membres).The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). La valeur d’un champ qui n’est pas en lecture seule est modifiée à l’aide d’une assignation (opérateurs d’affectation).The value of a non-readonly field is modified using an assignment (Assignment operators). La valeur d’un champ non readonly peut être obtenue et modifiée à l’aide des opérateurs suffixés d’incrémentation et de décrémentation (opérateurs suffixés d’incrémentation et de décrémentation) et des opérateurs de préfixe et d’incrémentation (incrément et décrémentation de préfixe) opérateurs).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).

Une déclaration de champ qui déclare plusieurs champs équivaut à plusieurs déclarations de champs uniques avec les mêmes attributs, modificateurs et type.A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. Exemple :For example

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

est équivalent àis equivalent to

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

Champs d’instance et statiquesStatic and instance fields

Lorsqu’une déclaration de champ comprend static un modificateur, les champs introduits par la déclaration sont des champs statiques.When a field declaration includes a static modifier, the fields introduced by the declaration are static fields. Si aucun static modificateur n’est présent, les champs introduits par la déclaration sont des champs d’instance.When no static modifier is present, the fields introduced by the declaration are instance fields. Les champs statiques et les champs d’instance sont deux des différents typesde variables (variables C#) pris en charge par, et parfois appelés variables statiques et variables d’instance, respectivement.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.

Un champ statique ne fait pas partie d’une instance spécifique ; au lieu de cela, il est partagé entre toutes les instances d’un type fermé (types ouverts et fermés).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). Quel que soit le nombre d’instances d’un type de classe fermé, il n’existe qu’une seule copie d’un champ statique pour le domaine d’application associé.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.

Exemple :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
    }
}

Un champ d’instance appartient à une instance.An instance field belongs to an instance. Plus précisément, chaque instance d’une classe contient un ensemble distinct de tous les champs d’instance de cette classe.Specifically, every instance of a class contains a separate set of all the instance fields of that class.

Lorsqu’un champ est référencé dans un member_access (accès aux membres) de la forme E.M, si M est un champ statique, E doit désigner un type contenant M, et si M est un champ d’instance, E doit désigner une instance d’un type contenant 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.

Les différences entre les membres statiques et les membres d’instance sont abordées plus en détail dans les membres statiques et d’instance.The differences between static and instance members are discussed further in Static and instance members.

Champs en lecture seuleReadonly fields

Lorsqu’un field_declaration comprend un modificateur readonly, les champs introduits par la déclaration sont des champs en lecture seule.When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Les assignations directes aux champs ReadOnly ne peuvent se produire que dans le cadre de cette déclaration ou dans un constructeur d’instance ou un constructeur statique dans la même classe.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. (Un champ readonly peut être affecté à plusieurs fois dans ces contextes.) Plus précisément, les assignations directes à un readonly champ sont autorisées uniquement dans les contextes suivants :(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:

  • Dans le variable_declarator qui introduit le champ (en incluant un variable_initializer dans la déclaration).In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • Pour un champ d’instance, dans les constructeurs d’instance de la classe qui contient la déclaration de champ ; pour un champ statique, dans le constructeur statique de la classe qui contient la déclaration de champ.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. Il s’agit également des seuls contextes dans lesquels il est possible de passer readonly un champ out en tant ref que paramètre ou.These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Toute tentative d’assignation à readonly un champ ou de transmission out en tant ref que paramètre ou dans un autre contexte est une erreur au moment de la compilation.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.

Utilisation de champs ReadOnly statiques pour les constantesUsing static readonly fields for constants

Un static readonly champ est utile lorsqu’un nom symbolique pour une valeur constante est souhaité, mais lorsque le type de la valeur n’est pas autorisé dans const une déclaration, ou lorsque la valeur ne peut pas être calculée au moment de la compilation.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. Dans l’exempleIn 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;
    }
}

les Blackmembres White,, Red, const et neBlue peuvent pas être déclarés comme membres, car leurs valeurs ne peuvent pas être calculées au moment de la compilation. Greenthe Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. Toutefois, leur static readonly déclaration à la place a un effet très similaire.However, declaring them static readonly instead has much the same effect.

Contrôle de version des constantes et des champs ReadOnly statiquesVersioning of constants and static readonly fields

Les constantes et les champs ReadOnly ont une sémantique de version binaire différente.Constants and readonly fields have different binary versioning semantics. Lorsqu’une expression fait référence à une constante, la valeur de la constante est obtenue au moment de la compilation, mais lorsqu’une expression fait référence à un champ ReadOnly, la valeur du champ n’est obtenue qu’au moment de l’exécution.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. Prenons l’exemple d’une application qui se compose de deux programmes distincts :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);
        }
    }
}

Les Program1 espaces Program2 de noms et désignent deux programmes qui sont compilés séparément.The Program1 and Program2 namespaces denote two programs that are compiled separately. Étant Program1.Utils.X donné que est déclaré en tant que champ readonly statique, la valeur Console.WriteLine générée par l’instruction n’est pas connue au moment de la compilation, mais est plutôt obtenue au moment de l’exécution.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. Ainsi, si la valeur de X est modifiée et Program1 est recompilée, Console.WriteLine l’instruction génère la nouvelle valeur même si Program2 n’est pas recompilé.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. Toutefois, avait X été une constante, la valeur de X aurait été obtenue au moment Program2 de la compilation Program1 et ne serait pas affectée par les modifications apportées Program2 à jusqu’à la recompilation.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.

Champs volatilesVolatile fields

Quand un field_declaration comprend un modificateur volatile, les champs introduits par cette déclaration sont des champs volatiles.When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

Pour les champs non volatiles, les techniques d’optimisation qui réorganisent les instructions peuvent entraîner des résultats inattendus et imprévisibles dans les programmes multithread qui accèdent aux champs sans synchronisation, comme celui fourni par lock_statement (le instruction 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). Ces optimisations peuvent être effectuées par le compilateur, par le système d’exécution ou par le matériel.These optimizations can be performed by the compiler, by the run-time system, or by hardware. Pour les champs volatils, ces optimisations de réorganisation sont restreintes :For volatile fields, such reordering optimizations are restricted:

  • La lecture d’un champ volatile est appelée une lecture volatile.A read of a volatile field is called a volatile read. Une lecture volatile a une « sémantique d’acquisition »; autrement dit, il est garanti qu’il se produise avant toute référence à la mémoire qui se trouve après dans la séquence d’instructions.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.
  • L’écriture d’un champ volatile est appelée une écriture volatile.A write of a volatile field is called a volatile write. Une écriture volatile a une « sémantique de version »; autrement dit, il est garanti qu’il se produira après toute référence de mémoire avant l’instruction d’écriture dans la séquence d’instructions.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.

Ces restrictions garantissent que tous les threads observent les écritures volatiles effectuées par un autre thread dans l’ordre dans lequel elles ont été effectuées.These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. Une implémentation conforme n’est pas requise pour fournir un classement total unique des écritures volatiles, comme vu par tous les threads d’exécution.A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. Le type d’un champ volatile doit être l’un des suivants :The type of a volatile field must be one of the following:

  • Reference_type.A reference_type.
  • byteType ,char ,,System.UIntPtr,,,, ,System.IntPtr, ou. bool float sbyte short ushort int uintThe type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • Enum_type dont le type de base enum est byte, sbyte, short, ushort, int ou uint.An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

L’exempleThe 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;
            }
        }
    }
}

génère cette sortie :produces the output:

result = 143

Dans cet exemple, la méthode Main démarre un nouveau thread qui exécute la méthode Thread2.In this example, the method Main starts a new thread that runs the method Thread2. Cette méthode stocke une valeur dans un champ non volatile appelé result, puis stocke true dans le champ finishedvolatile.This method stores a value into a non-volatile field called result, then stores true in the volatile field finished. Le thread principal attend que le champ finished soit défini sur true, puis lit le champ result.The main thread waits for the field finished to be set to true, then reads the field result. Étant finished donné que a volatileété déclaré, le thread principal doit lire 143 la valeur du resultchamp.Since finished has been declared volatile, the main thread must read the value 143 from the field result. Si le champ finished n’a pas été volatiledéclaré, il est possible que le magasin result soit visible par le thread principal après le stockage finished, et par conséquent, pour que le thread principal Lise la valeur 0 à partir de la champ 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. La Déclaration finished volatile en tant que champ empêche toute incohérence.Declaring finished as a volatile field prevents any such inconsistency.

Initialisation de champField initialization

La valeur initiale d’un champ, qu’il s’agisse d’un champ statique ou d’un champ d’instance, est la valeur par défaut (valeurs par défaut) du type du champ.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. Il n’est pas possible d’observer la valeur d’un champ avant l’initialisation par défaut, et un champ n’est donc jamais « non initialisé ».It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". L’exempleThe 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);
    }
}

génère la sortieproduces the output

b = False, i = 0

étant b donné i que et sont initialisés automatiquement avec les valeurs par défaut.because b and i are both automatically initialized to default values.

Initialiseurs de variablesVariable initializers

Les déclarations de champ peuvent inclure des variable_initializers.Field declarations may include variable_initializers. Pour les champs statiques, les initialiseurs de variable correspondent aux instructions d’assignation qui sont exécutées pendant l’initialisation de la classe.For static fields, variable initializers correspond to assignment statements that are executed during class initialization. Pour les champs d’instance, les initialiseurs de variable correspondent aux instructions d’assignation qui sont exécutées lors de la création d’une instance de la classe.For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

L’exempleThe 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);
    }
}

génère la sortieproduces the output

x = 1.4142135623731, i = 100, s = Hello

parce qu’une assignation x à se produit lorsque des initialiseurs de champs statiques s exécutent des assignations à et se produisent lorsque les initialiseurs de champ d' i instance s’exécutent.because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

L’initialisation de la valeur par défaut décrite dans initialisation de champ se produit pour tous les champs, y compris les champs qui ont des initialiseurs de variable.The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. Ainsi, lorsqu’une classe est initialisée, tous les champs statiques de cette classe sont d’abord initialisés à leurs valeurs par défaut, puis les initialiseurs de champs statiques sont exécutés dans l’ordre textuel.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. De même, lorsqu’une instance d’une classe est créée, tous les champs d’instance dans cette instance sont d’abord initialisés à leurs valeurs par défaut, puis les initialiseurs de champ d’instance sont exécutés dans l’ordre textuel.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.

Il est possible que les champs statiques avec des initialiseurs de variable soient observés dans leur état de valeur par défaut.It is possible for static fields with variable initializers to be observed in their default value state. Toutefois, cela est fortement déconseillé en tant que question de style.However, this is strongly discouraged as a matter of style. L’exempleThe 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);
    }
}

présente ce comportement.exhibits this behavior. Malgré les définitions circulaires de a et b, le programme est valide.Despite the circular definitions of a and b, the program is valid. Elle génère la sortieIt results in the output

a = 1, b = 2

étant donné que les a champs b statiques et sont 0 initialisés à (valeur intpar défaut pour) avant l’exécution de leurs initialiseurs.because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. Lorsque l’initialiseur pour a s’exécute, la valeur b de est égale à zéro a , et par conséquent 1, est initialisé à.When the initializer for a runs, the value of b is zero, and so a is initialized to 1. Lorsque l’initialiseur pour b s’exécute, la valeur a de est 1déjà, et b par conséquent, est 2initialisé à.When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

Initialisation de champ statiqueStatic field initialization

Les initialiseurs de variable de champ statique d’une classe correspondent à une séquence d’assignations qui sont exécutées dans l’ordre textuel dans lequel elles apparaissent dans la déclaration de classe.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. Si un constructeur statique (constructeurs statiques) existe dans la classe, l’exécution des initialiseurs de champ statiques se produit immédiatement avant l’exécution de ce constructeur statique.If a static constructor (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Sinon, les initialiseurs de champs statiques sont exécutés à un moment dépendant de l’implémentation avant la première utilisation d’un champ statique de cette classe.Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. L’exempleThe 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");
}

peut produire la sortie :might produce either the output:

Init A
Init B
1 1

ou la sortie :or the output:

Init B
Init A
1 1

étant donné que l' Xexécution de l’initialiseur et Yde l’initialiseur de est susceptible de se produire dans l’un ou l’autre ordre, il ne doit se produire que pour les références à ces champs.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. Toutefois, dans l’exemple :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");
}

la sortie doit être :the output must be:

Init B
Init A
1 1

étant donné que les règles de moment où les constructeurs statiques s’exécutent (comme défini dans les constructeurs statiques) fournissent Bce Bconstructeur statique (et par conséquent, les Ainitialiseurs de champ statiques) doivent s’exécuter avant la statique de initialiseurs de constructeur et de champ.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.

Initialisation du champ d’instanceInstance field initialization

Les initialiseurs de variable de champ d’instance d’une classe correspondent à une séquence d’assignations qui sont exécutées immédiatement après l’entrée de l’un des constructeurs d’instance (initialiseurs de constructeur) de cette classe.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. Les initialiseurs de variable sont exécutés dans l’ordre textuel dans lequel ils apparaissent dans la déclaration de classe.The variable initializers are executed in the textual order in which they appear in the class declaration. Le processus de création et d’initialisation de l’instance de classe est décrit plus en détail dans les constructeurs d’instance.The class instance creation and initialization process is described further in Instance constructors.

Un initialiseur de variable pour un champ d’instance ne peut pas faire référence à l’instance en cours de création.A variable initializer for an instance field cannot reference the instance being created. Par conséquent, il s’agit d’une erreur au moment de la compilation pour référencer this dans un initialiseur de variable, car il s’agit d’une erreur de compilation pour qu’un initialiseur de variable référence un membre d’instance via un 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. Dans l’exempleIn the example

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

l’initialiseur de variable y pour génère une erreur de compilation, car il fait référence à un membre de l’instance en cours de création.the variable initializer for y results in a compile-time error because it references a member of the instance being created.

MéthodesMethods

Une méthode est un membre qui implémente un calcul ou une action qui peut être effectuée par un objet ou une classe.A method is a member that implements a computation or action that can be performed by an object or class. Les méthodes sont déclarées à l’aide de 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 ';'
    | ';'
    ;

Un method_declaration peut inclure un ensemble d' attributs (attributs) et une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès), le new (le nouveau modificateur), static (static et instance Méthodes), virtual (méthodes virtuelles), 0 (méthodes override), 2 (méthodes sealed), 4 (méthodes abstraites) et 6 (méthodes externes).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.

Une déclaration a une combinaison valide de modificateurs si toutes les conditions suivantes sont vraies :A declaration has a valid combination of modifiers if all of the following are true:

  • La déclaration comprend une combinaison valide de modificateurs d’accès (modificateurs d’accès).The declaration includes a valid combination of access modifiers (Access modifiers).
  • La déclaration n’inclut pas le même modificateur plusieurs fois.The declaration does not include the same modifier multiple times.
  • La déclaration comprend au plus l’un des modificateurs suivants : static, virtualet override.The declaration includes at most one of the following modifiers: static, virtual, and override.
  • La déclaration comprend au plus l’un des modificateurs suivants : new et override.The declaration includes at most one of the following modifiers: new and override.
  • Si la déclaration inclut le abstract modificateur, la déclaration n’inclut pas l’un des modificateurs suivants : static, virtual sealed ou extern.If the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • Si la déclaration inclut le private modificateur, la déclaration n’inclut pas l’un des modificateurs suivants : virtual, overrideou abstract.If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • Si la déclaration comprend le sealed modificateur, la déclaration comprend également le override modificateur.If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • Si la déclaration inclut le partial modificateur, elle n’inclut pas l’un des modificateurs suivants : new, public, protected, internal, private, virtual,, override sealed , abstractou .externIf the declaration includes the partial modifier, then it does not include any of the following modifiers: new, public, protected, internal, private, virtual, sealed, override, abstract, or extern.

Une méthode qui a le async modificateur est une fonction Async et suit les règles décrites dans fonctions Async.A method that has the async modifier is an async function and follows the rules described in Async functions.

L' type_retour d’une déclaration de méthode spécifie le type de la valeur calculée et retournée par la méthode.The return_type of a method declaration specifies the type of the value computed and returned by the method. Type_retour est void si la méthode ne retourne pas de valeur.The return_type is void if the method does not return a value. Si la déclaration comprend le partial modificateur, le type de retour doit être void.If the declaration includes the partial modifier, then the return type must be void.

Member_Name spécifie le nom de la méthode.The member_name specifies the name of the method. À moins que la méthode ne soit une implémentation de membre d’interface explicite (implémentations de membres d’interface explicites), Member_Name est simplement un identificateur.Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. Pour une implémentation de membre d’interface explicite, Member_Name se compose d’un INTERFACE_TYPE suivi d’un « . » et d’un identificateur.For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

Le type_parameter_list facultatif spécifie les paramètres de type de la méthode (paramètres de type).The optional type_parameter_list specifies the type parameters of the method (Type parameters). Si un type_parameter_list est spécifié, la méthode est une méthode générique.If a type_parameter_list is specified the method is a generic method. Si la méthode a un modificateur extern, un type_parameter_list ne peut pas être spécifié.If the method has an extern modifier, a type_parameter_list cannot be specified.

Le formal_parameter_list facultatif spécifie les paramètres de la méthode (paramètres de méthode).The optional formal_parameter_list specifies the parameters of the method (Method parameters).

Les type_parameter_constraints_clausefacultatifs spécifient des contraintes sur des paramètres de type individuels (contraintes de paramètre de type) et peuvent être spécifiés uniquement si un type_parameter_list est également fourni, et si la méthode n’a pas de modificateur 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.

L' type_retour et chacun des types référencés dans le formal_parameter_list d’une méthode doivent être au moins aussi accessibles que la méthode elle-même (contraintes d’accessibilité).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 est un point-virgule, un corps d’instruction ou un corps d’expression.The method_body is either a semicolon, a statement body or an expression body. Un corps d’instruction se compose d’un bloc, qui spécifie les instructions à exécuter lorsque la méthode est appelée.A statement body consists of a block, which specifies the statements to execute when the method is invoked. Un corps d’expression se => compose de suivis d’une expression et d’un point-virgule, et désigne une expression unique à exécuter lorsque la méthode est appelée.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the method is invoked.

Pour les méthodes abstract et extern, method_body se compose simplement d’un point-virgule.For abstract and extern methods, the method_body consists simply of a semicolon. Pour les méthodes partial, method_body peut être constitué soit d’un point-virgule, d’un corps de bloc ou d’un corps d’expression.For partial methods the method_body may consist of either a semicolon, a block body or an expression body. Pour toutes les autres méthodes, method_body est soit un corps de bloc, soit un corps d’expression.For all other methods, the method_body is either a block body or an expression body.

Si le method_body se compose d’un point-virgule, la déclaration peut ne pas inclure le modificateur async.If the method_body consists of a semicolon, then the declaration may not include the async modifier.

Le nom, la liste des paramètres de type et la liste de paramètres formels d’une méthode définissent la signature (signatures et surcharge) de la méthode.The name, the type parameter list and the formal parameter list of a method define the signature (Signatures and overloading) of the method. Plus précisément, la signature d’une méthode se compose de son nom, du nombre de paramètres de type et du nombre, des modificateurs et des types de ses paramètres formels.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. Pour ces raisons, tout paramètre de type de la méthode qui se produit dans le type d’un paramètre formel est identifié par son nom, mais par sa position ordinale dans la liste d’arguments de type de la méthode. Le type de retour ne fait pas partie de la signature d’une méthode, ni les noms des paramètres de type ni les paramètres formels.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.

Le nom d’une méthode doit être différent des noms de toutes les autres méthodes non déclarées dans la même classe.The name of a method must differ from the names of all other non-methods declared in the same class. En outre, la signature d’une méthode doit être différente des signatures de toutes les autres méthodes déclarées dans la même classe, et deux méthodes déclarées dans la même classe ne peuvent pas avoir de ref signatures outqui diffèrent uniquement par et.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.

Le type_parameterde la méthode se trouve dans l’étendue dans tout le method_declaration, et peut être utilisé pour former des types dans cette portée dans type_retour, method_bodyet type_parameter_constraints_clauses, mais pas dans attributs.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.

Tous les paramètres formels et les paramètres de type doivent avoir des noms différents.All formal parameters and type parameters must have different names.

Paramètres de méthodeMethod parameters

Les paramètres d’une méthode, le cas échéant, sont déclarés par le formal_parameter_listde la méthode.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
    ;

La liste de paramètres formels se compose d’un ou de plusieurs paramètres séparés par des virgules dont seule la dernière peut être un parameter_array.The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Un fixed_parameter se compose d’un ensemble facultatif d' attributs (attributs), d’un modificateur facultatif ref, out ou this, d’un type, d’un identificateur et d’un default_argumentfacultatif.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. Chaque fixed_parameter déclare un paramètre du type donné avec le nom donné.Each fixed_parameter declares a parameter of the given type with the given name. Le this modificateur désigne la méthode en tant que méthode d’extension et est uniquement autorisé sur le premier paramètre d’une méthode statique.The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method. Les méthodes d’extension sont décrites plus en détail dans méthodes d’extension.Extension methods are further described in Extension methods.

Un fixed_parameter avec un default_argument est connu sous le nom de paramètre facultatif, alors qu’un fixed_parameter sans default_argument est un paramètre obligatoire.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. Un paramètre obligatoire ne peut pas apparaître après un paramètre facultatif dans un formal_parameter_list.A required parameter may not appear after an optional parameter in a formal_parameter_list.

Un paramètre ref ou out ne peut pas avoir de default_argument.A ref or out parameter cannot have a default_argument. L' expression d’un default_argument doit être l’une des suivantes :The expression in a default_argument must be one of the following:

  • a constant_expressiona constant_expression
  • expression de la forme new S()S est un type valeuran expression of the form new S() where S is a value type
  • expression de la forme default(S)S est un type valeuran expression of the form default(S) where S is a value type

L' expression doit être implicitement convertible à l’aide d’une conversion d’identité ou de valeurs NULL dans le type du paramètre.The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

Si des paramètres facultatifs se produisent dans une déclaration de méthode partielle d’implémentation (méthodes partielles), une implémentation de membre d’interface explicite (implémentations de membres d’interface explicites) ou dans une déclaration d’indexeur de paramètre unique ( Indexeurs) le compilateur doit fournir un avertissement, car ces membres ne peuvent jamais être appelés d’une manière qui autorise l’omission des arguments.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.

Un parameter_array se compose d’un ensemble facultatif d' attributs (attributs), d’un modificateur params, d’un array_typeet d’un identificateur.A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. Un tableau de paramètres déclare un paramètre unique du type de tableau donné avec le nom donné.A parameter array declares a single parameter of the given array type with the given name. Le array_type d’un tableau de paramètres doit être un type tableau unidimensionnel (types tableau).The array_type of a parameter array must be a single-dimensional array type (Array types). Dans un appel de méthode, un tableau de paramètres autorise la spécification d’un seul argument du type de tableau donné, ou elle permet de spécifier zéro ou plusieurs arguments du type d’élément de tableau.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. Les tableaux de paramètres sont décrits plus en détail dans les tableaux de paramètres.Parameter arrays are described further in Parameter arrays.

Une parameter_array peut se produire après un paramètre facultatif, mais elle ne peut pas avoir de valeur par défaut ; l’omission des arguments pour un parameter_array entraînerait la création d’un tableau vide.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.

L’exemple suivant illustre différents genres de paramètres :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
) { }

Dans le formal_parameter_list pour M, i est un paramètre ref obligatoire, d est un paramètre de valeur obligatoire, b, s, o et t sont des paramètres de valeur facultatifs et a est un tableau de paramètres.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.

Une déclaration de méthode crée un espace de déclaration distinct pour les paramètres, les paramètres de type et les variables locales.A method declaration creates a separate declaration space for parameters, type parameters and local variables. Les noms sont introduits dans cet espace de déclaration par la liste de paramètres de type et la liste de paramètres formels de la méthode et par les déclarations de variable locale dans le bloc de la méthode.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. Il y a une erreur pour que deux membres d’un espace de déclaration de méthode aient le même nom.It is an error for two members of a method declaration space to have the same name. L’espace de déclaration de méthode et l’espace de déclaration de variable locale d’un espace de déclaration imbriquée doivent contenir des éléments portant le même nom.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.

Un appel de méthode (appel de méthode) crée une copie, spécifique à cet appel, des paramètres formels et des variables locales de la méthode, et la liste d’arguments de l’appel assigne des valeurs ou des références de variable à l’objet formel nouvellement créé. paramètres.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. Dans le bloc d’une méthode, les paramètres formels peuvent être référencés par leurs identificateurs dans les expressions Simple_name (noms simples).Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

Il existe quatre genres de paramètres formels :There are four kinds of formal parameters:

  • Les paramètres de valeur, qui sont déclarés sans modificateurs.Value parameters, which are declared without any modifiers.
  • Les paramètres de référence, qui sont déclarés avec le ref modificateur.Reference parameters, which are declared with the ref modifier.
  • Les paramètres de sortie, qui sont déclarés avec le out modificateur.Output parameters, which are declared with the out modifier.
  • Tableaux de paramètres, qui sont déclarés avec params le modificateur.Parameter arrays, which are declared with the params modifier.

Comme décrit dans signatures et surcharge, les ref modificateurs et out font partie de la signature d’une méthode, mais le params modificateur n’est pas.As described in Signatures and overloading, the ref and out modifiers are part of a method's signature, but the params modifier is not.

Paramètres de valeurValue parameters

Un paramètre déclaré sans modificateurs est un paramètre de valeur.A parameter declared with no modifiers is a value parameter. Un paramètre de valeur correspond à une variable locale qui obtient sa valeur initiale de l’argument correspondant fourni dans l’appel de méthode.A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

Quand un paramètre formel est un paramètre de valeur, l’argument correspondant dans un appel de méthode doit être une expression implicitement convertible (conversions implicites) en type de paramètre formel.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.

Une méthode est autorisée à assigner de nouvelles valeurs à un paramètre de valeur.A method is permitted to assign new values to a value parameter. Ces affectations affectent uniquement l’emplacement de stockage local représenté par le paramètre de valeur, elles n’ont aucun effet sur l’argument réel fourni dans l’appel de méthode.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.

Paramètres de référenceReference parameters

Un paramètre déclaré avec un ref modificateur est un paramètre de référence.A parameter declared with a ref modifier is a reference parameter. Contrairement à un paramètre de valeur, un paramètre de référence ne crée pas un nouvel emplacement de stockage.Unlike a value parameter, a reference parameter does not create a new storage location. Au lieu de cela, un paramètre de référence représente le même emplacement de stockage que la variable donnée comme argument dans l’appel de méthode.Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

Quand un paramètre formel est un paramètre de référence, l’argument correspondant dans un appel de méthode doit se composer du mot clé ref suivi d’un variable_reference (règles précises pour déterminer l’assignation définie) du même type que paramètre formel.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. Une variable doit être assignée définitivement pour pouvoir être passée en tant que paramètre de référence.A variable must be definitely assigned before it can be passed as a reference parameter.

Dans une méthode, un paramètre de référence est toujours considéré comme étant définitivement assigné.Within a method, a reference parameter is always considered definitely assigned.

Une méthode déclarée en tant qu’itérateur (itérateurs) ne peut pas avoir de paramètres de référence.A method declared as an iterator (Iterators) cannot have reference parameters.

L’exempleThe 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);
    }
}

génère la sortieproduces the output

i = 2, j = 1

Pour l’appel de Swap dans Main, x représente i et y représente. jFor the invocation of Swap in Main, x represents i and y represents j. Ainsi, l’appel a pour effet de permuter les valeurs de i et j.Thus, the invocation has the effect of swapping the values of i and j.

Dans une méthode qui accepte des paramètres de référence, il est possible que plusieurs noms représentent le même emplacement de stockage.In a method that takes reference parameters it is possible for multiple names to represent the same storage location. Dans l’exempleIn 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);
    }
}

l' F appel de bdans G s passea une référence à pour et.the invocation of F in G passes a reference to s for both a and b. Ainsi, pour cet appel, les snoms, aet b font tous référence au même emplacement de stockage, et les trois assignations modifient toutes le schamp d’instance.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.

Paramètres de sortieOutput parameters

Un paramètre déclaré avec un out modificateur est un paramètre de sortie.A parameter declared with an out modifier is an output parameter. À l’instar d’un paramètre de référence, un paramètre de sortie ne crée pas un nouvel emplacement de stockage.Similar to a reference parameter, an output parameter does not create a new storage location. Au lieu de cela, un paramètre de sortie représente le même emplacement de stockage que la variable donnée comme argument dans l’appel de la méthode.Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

Quand un paramètre formel est un paramètre de sortie, l’argument correspondant dans un appel de méthode doit se composer du mot clé out suivi d’un variable_reference (règles précises pour déterminer l’assignation définie) du même type que le paramètre formel.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. Une variable n’a pas besoin d’être définitivement assignée avant de pouvoir être transmise en tant que paramètre de sortie, mais après un appel où une variable a été passée comme paramètre de sortie, la variable est considérée comme assignée de manière définitive.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.

Dans une méthode, tout comme une variable locale, un paramètre de sortie est considéré initialement comme non assigné et doit être définitivement assigné avant que sa valeur ne soit utilisée.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.

Chaque paramètre de sortie d’une méthode doit être assigné définitivement avant le retour de la méthode.Every output parameter of a method must be definitely assigned before the method returns.

Une méthode déclarée en tant que méthode partielle (méthodes partielles) ou itérateur (itérateurs) ne peut pas avoir de paramètres de sortie.A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

Les paramètres de sortie sont généralement utilisés dans les méthodes qui produisent plusieurs valeurs de retour.Output parameters are typically used in methods that produce multiple return values. Exemple :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);
    }
}

L’exemple produit la sortie :The example produces the output:

c:\Windows\System\
hello.txt

Notez que les dir variables name et peuvent être non assignées avant d’être SplitPathpassées à, et qu’elles sont considérées comme définitivement assignées après l’appel.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.

Tableaux de paramètresParameter arrays

Un paramètre déclaré avec un params modificateur est un tableau de paramètres.A parameter declared with a params modifier is a parameter array. Si une liste de paramètres formels comprend un tableau de paramètres, elle doit être le dernier paramètre de la liste et doit être de type tableau unidimensionnel.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. Par exemple, les types string[] et string[][] peuvent être utilisés comme type d’un tableau de paramètres, mais le type string[,] ne peut pas.For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. Il n’est pas possible de combiner params le modificateur avec les ref modificateurs outet.It is not possible to combine the params modifier with the modifiers ref and out.

Un tableau de paramètres permet de spécifier des arguments de l’une des deux manières suivantes dans un appel de méthode :A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • L’argument donné pour un tableau de paramètres peut être une expression unique implicitement convertible (conversions implicites) en type tableau de paramètres.The argument given for a parameter array can be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. Dans ce cas, le tableau de paramètres agit précisément comme un paramètre de valeur.In this case, the parameter array acts precisely like a value parameter.
  • L’appel peut également spécifier zéro ou plusieurs arguments pour le tableau de paramètres, où chaque argument est une expression implicitement convertible (conversions implicites) en type d’élément du tableau de paramètres.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. Dans ce cas, l’appel crée une instance du type de tableau de paramètres avec une longueur correspondant au nombre d’arguments, initialise les éléments de l’instance de tableau avec les valeurs d’argument données et utilise l’instance de tableau nouvellement créée comme 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.

À l’exception de l’autorisation d’un nombre variable d’arguments dans un appel, un tableau de paramètres équivaut précisément à un paramètre de valeur (paramètres de valeur) du même type.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.

L’exempleThe 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();
    }
}

génère la sortieproduces the output

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

La première invocation de F passe simplement le tableau a en tant que paramètre de valeur.The first invocation of F simply passes the array a as a value parameter. Le deuxième appel de F crée automatiquement un élément à quatre éléments int[] avec les valeurs d’élément données et passe cette instance de tableau en tant que paramètre de valeur.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. De même, le troisième appel de F crée un élément int[] de zéro et passe cette instance en tant que paramètre de valeur.Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. Le deuxième et le troisième appel sont exactement équivalents à l’écriture :The second and third invocations are precisely equivalent to writing:

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

Lors de l’exécution de la résolution de surcharge, une méthode avec un tableau de paramètres peut s’appliquer soit dans sa forme normale, soit dans sa forme développée (fonction membre applicable).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). La forme développée d’une méthode est disponible uniquement si la forme normale de la méthode n’est pas applicable et uniquement si une méthode applicable avec la même signature que la forme développée n’est pas déjà déclarée dans le même type.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.

L’exempleThe 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);
    }
}

génère la sortieproduces the output

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

Dans l’exemple, deux des formes étendues possibles de la méthode avec un tableau de paramètres sont déjà incluses dans la classe en tant que méthodes normales.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. Ces formulaires développés ne sont donc pas pris en compte lors de l’exécution de la résolution de surcharge, et les premier et troisième appels de méthode sélectionnent donc les méthodes normales.These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. Quand une classe déclare une méthode avec un tableau de paramètres, il n’est pas rare d’inclure également certains des formulaires développés comme méthodes normales.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. En procédant ainsi, il est possible d’éviter l’allocation d’une instance de tableau qui se produit lorsqu’une forme développée d’une méthode avec un tableau de paramètres est appelée.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.

Lorsque le type d’un tableau de paramètres object[]est, une ambiguïté potentielle se produit entre la forme normale de la méthode et la forme étendue pour un paramètre object unique.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. L’ambiguïté est due au fait qu’un object[] est lui-même convertible implicitement en type. objectThe reason for the ambiguity is that an object[] is itself implicitly convertible to type object. Toutefois, l’ambiguïté ne présente aucun problème, car elle peut être résolue en insérant un cast si nécessaire.The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

L’exempleThe 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);
    }
}

génère la sortieproduces the output

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

Dans le premier et le dernier appel de F, la forme normale de F est applicable, car il existe une conversion implicite du type d’argument vers le type de paramètre (les object[]deux sont de type).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[]). Ainsi, la résolution de surcharge sélectionne la forme Fnormale de et l’argument est passé en tant que paramètre de valeur régulière.Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. Dans le deuxième et le troisième appel, la forme normale de F n’est pas applicable, car il n’existe aucune conversion implicite du type d’argument vers le type de paramètre object (le type ne object[]peut pas être converti implicitement en type).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[]). Toutefois, la forme développée de F est applicable, donc elle est sélectionnée par la résolution de surcharge.However, the expanded form of F is applicable, so it is selected by overload resolution. Par conséquent, un élément object[] unique est créé par l’appel, et l’élément unique du tableau est initialisé avec la valeur d’argument donnée (qui est elle-même une référence à un 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[]).

Méthodes statiques et d’instanceStatic and instance methods

Lorsqu’une déclaration de méthode comprend static un modificateur, cette méthode est dite statique.When a method declaration includes a static modifier, that method is said to be a static method. Quand aucun static modificateur n’est présent, la méthode est considérée comme une méthode d’instance.When no static modifier is present, the method is said to be an instance method.

Une méthode statique ne fonctionne pas sur une instance spécifique, et il s’agit d’une erreur de compilation pour faire this référence à dans une méthode statique.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.

Une méthode d’instance opère sur une instance donnée d’une classe, et cette instance est accessible en this tant que (cet accès).An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

Lorsqu’une méthode est référencée dans un member_access (accès aux membres) de la forme E.M, si M est une méthode statique, E doit désigner un type contenant M, et si M est une méthode d’instance, E doit désigner une instance d’un type contenant 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.

Les différences entre les membres statiques et les membres d’instance sont abordées plus en détail dans les membres statiques et d’instance.The differences between static and instance members are discussed further in Static and instance members.

Méthodes virtuellesVirtual methods

Lorsqu’une déclaration de méthode d’instance virtual comprend un modificateur, cette méthode est dite méthode virtuelle.When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. Quand aucun virtual modificateur n’est présent, la méthode est dite non virtuelle.When no virtual modifier is present, the method is said to be a non-virtual method.

L’implémentation d’une méthode non virtuelle est indifférente : L’implémentation est la même que la méthode soit appelée sur une instance de la classe dans laquelle elle est déclarée ou d’une instance d’une classe dérivée.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. En revanche, l’implémentation d’une méthode virtuelle peut être remplacée par des classes dérivées.In contrast, the implementation of a virtual method can be superseded by derived classes. Le processus de remplacement de l’implémentation d’une méthode virtuelle héritée est connu sous le nom de substitution de cette méthode (méthodes override).The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

Dans un appel de méthode virtuelle, le type au moment de l’exécution de l’instance pour laquelle cet appel se produit détermine l’implémentation de méthode réelle à appeler.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. Dans un appel de méthode non virtuel, le type au moment de la compilation de l’instance est le facteur déterminant.In a non-virtual method invocation, the compile-time type of the instance is the determining factor. En termes précis, quand une méthode nommée N est appelée avec une liste A d’arguments sur une instance avec un type C au moment de la compilation et un type R au moment de R l' C exécution (où est ou une classe dérivée à Cpartir de), l’appel est traité comme suit :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:

  • Tout d’abord, la résolution de Csurcharge Nest appliquée Aà, et, pour sélectionner M une méthode spécifique à partir de l’ensemble de méthodes Cdéclaré dans et héritée par.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. Cela est décrit dans appels de méthode.This is described in Method invocations.
  • Ensuite, si M est une méthode non virtuelle, M est appelée.Then, if M is a non-virtual method, M is invoked.
  • Sinon, M est une méthode virtuelle et l’implémentation la plus dérivée M de par rapport R à est appelée.Otherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

Pour chaque méthode virtuelle déclarée dans ou héritée par une classe, il existe une implémentation la plus dérivée de la méthode par rapport à cette classe.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. L’implémentation la plus dérivée d’une M méthode virtuelle en ce qui R concerne une classe est déterminée comme suit :The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • Si R contient la Déclaration virtual d’introduction Mde, il s’agit de l’implémentation la Mplus dérivée de.If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • Sinon, si R contient un override de M, il s’agit de l’implémentation la plus Mdérivée de.Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • Sinon, l' M implémentation la plus dérivée de par rapport à R est identique à l’implémentation la plus M dérivée de par rapport à la classe Rde base directe de.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.

L’exemple suivant illustre les différences entre les méthodes virtuelles et non virtuelles :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();
    }
}

Dans l’exemple, A introduit une méthode F non virtuelle et une méthode Gvirtuelle.In the example, A introduces a non-virtual method F and a virtual method G. La classe B introduit une nouvelle méthode Fnon virtuelle, masquant ainsi la héritée Fet substitue également la méthode Ghéritée.The class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. L’exemple produit la sortie :The example produces the output:

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

Notez que l’instruction a.G() B.Gappelle, et non A.G.Notice that the statement a.G() invokes B.G, not A.G. Cela est dû au fait que le type au moment de l’exécution de Bl’instance (qui est), et non le type au moment de Ala compilation de l’instance (c’est-à-dire), détermine l’implémentation réelle de la méthode à appeler.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.

Étant donné que les méthodes sont autorisées à masquer les méthodes héritées, il est possible qu’une classe contienne plusieurs méthodes virtuelles avec la même signature.Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. Cela ne présente pas de problème d’ambiguïté, puisque toutes les méthodes sauf la plus dérivée sont masquées.This does not present an ambiguity problem, since all but the most derived method are hidden. Dans l’exempleIn 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();
    }
}

les C classes D et contiennent deux méthodes virtuelles avec la même signature : Celui introduit par A et celui introduit par 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. La méthode introduite C par masque la méthode héritée Ade.The method introduced by C hides the method inherited from A. Ainsi, la D déclaration override de substitue la méthode introduite par C, et il n’est pas possible pour D de substituer la méthode introduite Apar.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. L’exemple produit la sortie :The example produces the output:

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

Notez qu’il est possible d’appeler la méthode virtuelle masquée en accédant à une D instance de via un type moins dérivé dans lequel la méthode n’est pas masquée.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.

Substituer les méthodesOverride methods

Lorsqu’une déclaration de méthode d’instance override comprend un modificateur, la méthode est considérée comme une méthode de substitution.When an instance method declaration includes an override modifier, the method is said to be an override method. Une méthode override remplace une méthode virtuelle héritée avec la même signature.An override method overrides an inherited virtual method with the same signature. Là où une déclaration de méthode virtuelle présente une nouvelle méthode, une déclaration de méthode de substitution spécialise une méthode virtuelle héritée existante en fournissant une nouvelle implémentation de cette méthode.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.

La méthode substituée par une override déclaration est connue sous le nom de méthode de base substituée.The method overridden by an override declaration is known as the overridden base method. Pour M une méthode de substitution déclarée dans une Cclasse, la méthode de base substituée est déterminée en examinant chaque type Cde classe de base de, en commençant par C le type de classe de base directe de et en continuant avec chaque type de classe de base directe, jusqu’à ce que dans un type de classe de base donné, au moins une méthode accessible M soit localisée avec la même signature qu’après la substitution des arguments de type.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. Dans le cadre de la recherche de la méthode de base substituée, une méthode est considérée comme accessible publicsi protected protected internalc’est le cas, si c’est le cas, ou internal si elle est et est déclarée Cdans le même programme que.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.

Une erreur de compilation se produit sauf si toutes les conditions suivantes sont vraies pour une déclaration override :A compile-time error occurs unless all of the following are true for an override declaration:

  • Une méthode de base substituée peut être localisée comme décrit ci-dessus.An overridden base method can be located as described above.
  • Il existe exactement une méthode de base substituée.There is exactly one such overridden base method. Cette restriction n’a d’effet que si le type de classe de base est un type construit où la substitution d’arguments de type rend la signature de deux méthodes identique.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.
  • La méthode de base substituée est une méthode virtuelle, abstraite ou de substitution.The overridden base method is a virtual, abstract, or override method. En d’autres termes, la méthode de base substituée ne peut pas être statique ou non virtuelle.In other words, the overridden base method cannot be static or non-virtual.
  • La méthode de base substituée n’est pas une méthode sealed.The overridden base method is not a sealed method.
  • La méthode de substitution et la méthode de base substituée ont le même type de retour.The override method and the overridden base method have the same return type.
  • La déclaration override et la méthode de base substituée ont la même accessibilité déclarée.The override declaration and the overridden base method have the same declared accessibility. En d’autres termes, une déclaration override ne peut pas modifier l’accessibilité de la méthode virtuelle.In other words, an override declaration cannot change the accessibility of the virtual method. Toutefois, si la méthode de base substituée est protégée interne et qu’elle est déclarée dans un assembly différent de l’assembly contenant la méthode de substitution, l’accessibilité déclarée de la méthode de substitution doit être protégée.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.
  • La déclaration override ne spécifie pas de clauses de type-Parameter-Constraints.The override declaration does not specify type-parameter-constraints-clauses. Au lieu de cela, les contraintes sont héritées de la méthode de base substituée.Instead the constraints are inherited from the overridden base method. Notez que les contraintes qui sont des paramètres de type dans la méthode substituée peuvent être remplacées par des arguments de type dans la contrainte héritée.Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. Cela peut entraîner des contraintes qui ne sont pas autorisées quand elles sont explicitement spécifiées, telles que des types valeur ou des types sealed.This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

L’exemple suivant montre comment les règles de substitution fonctionnent pour les classes génériques :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>
}

Une déclaration override peut accéder à la méthode de base substituée à l’aide d’un base_access (accès de base).An override declaration can access the overridden base method using a base_access (Base access). Dans l’exempleIn 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);
    }
}

l' base.PrintFields() appel dans B appelle la PrintFields méthode déclarée dans A.the base.PrintFields() invocation in B invokes the PrintFields method declared in A. Un base_access désactive le mécanisme d’appel virtuel et traite simplement la méthode de base comme une méthode non virtuelle.A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. Si l' B appel a été écrit ((A)this).PrintFields(), il appellerait de manière récursive la PrintFields méthode déclarée dans B, pas celle qui est déclarée dans PrintFields A, car est virtuel et le type d’exécution de ((A)this) estBde.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.

Une méthode ne peut override se substituer à une autre méthode qu’en incluant un modificateur.Only by including an override modifier can a method override another method. Dans tous les autres cas, une méthode avec la même signature qu’une méthode héritée masque simplement la méthode héritée.In all other cases, a method with the same signature as an inherited method simply hides the inherited method. Dans l’exempleIn the example

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

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

la F méthode dans B n’inclut override pas de modificateur et, par conséquent, ne substitue pas Ala F méthode dans.the F method in B does not include an override modifier and therefore does not override the F method in A. Au lieu de F cela, B la méthode dans masque la Améthode dans et un avertissement est signalé, car la déclaration n’inclut new pas de modificateur.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.

Dans l’exempleIn 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
}

la F méthode dans B masque la méthode virtuelle F héritée de A.the F method in B hides the virtual F method inherited from A. Étant donné que F le B nouveau dans a un accès privé, son étendue inclut uniquement le B corps de classe de et Cn’étend pas à.Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. Par conséquent, la Déclaration F de C dans est autorisée à substituer F l’héritage Ade.Therefore, the declaration of F in C is permitted to override the F inherited from A.

Méthodes sealedSealed methods

Lorsqu’une déclaration de méthode d’instance sealed comprend un modificateur, cette méthode est dite sealed.When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. Si une déclaration de méthode d’instance sealed inclut le modificateur, elle doit également override inclure le modificateur.If an instance method declaration includes the sealed modifier, it must also include the override modifier. L' sealed utilisation du modificateur empêche une classe dérivée de substituer davantage la méthode.Use of the sealed modifier prevents a derived class from further overriding the method.

Dans l’exempleIn 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");
    } 
}

la classe B fournit deux méthodes override : une F méthode qui a le modificateur et une G méthode qui ne le sealed fait pas.the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. Bl’utilisation du sealed modifier empêche C toute substitution Fsupplémentaire.B's use of the sealed modifier prevents C from further overriding F.

Méthodes abstraitesAbstract methods

Lorsqu’une déclaration de méthode d’instance abstract comprend un modificateur, cette méthode est dite abstraite.When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Bien qu’une méthode abstraite soit implicitement également une méthode virtuelle, elle ne peut pas virtualavoir de modificateur.Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

Une déclaration de méthode abstraite introduit une nouvelle méthode virtuelle, mais ne fournit pas d’implémentation de cette méthode.An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Au lieu de cela, les classes dérivées non abstraites sont requises pour fournir leur propre implémentation en substituant cette méthode.Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Étant donné qu’une méthode abstraite ne fournit pas d’implémentation réelle, le method_body d’une méthode abstraite se compose simplement d’un point-virgule.Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

Les déclarations de méthode abstraite sont autorisées uniquement dans les classes abstraites (classes abstraites).Abstract method declarations are only permitted in abstract classes (Abstract classes).

Dans l’exempleIn 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);
    }
}

la Shape classe définit la notion abstraite d’un objet de forme géométrique qui peut se peindre lui-même.the Shape class defines the abstract notion of a geometrical shape object that can paint itself. La Paint méthode est abstraite, car il n’y a pas d’implémentation significative par défaut.The Paint method is abstract because there is no meaningful default implementation. Les Ellipse classes Box et sont des Shape implémentations concrètes.The Ellipse and Box classes are concrete Shape implementations. Étant donné que ces classes ne sont pas abstraites, elles doivent substituer Paint la méthode et fournir une implémentation réelle.Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

Il s’agit d’une erreur au moment de la compilation pour qu’un base_access (base Access) référence une méthode abstraite.It is a compile-time error for a base_access (Base access) to reference an abstract method. Dans l’exempleIn the example

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

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

une erreur de compilation est signalée pour l' base.F() appel, car elle fait référence à une méthode abstraite.a compile-time error is reported for the base.F() invocation because it references an abstract method.

Une déclaration de méthode abstraite est autorisée à substituer une méthode virtuelle.An abstract method declaration is permitted to override a virtual method. Cela permet à une classe abstraite de forcer la réimplémentation de la méthode dans les classes dérivées et rend l’implémentation d’origine de la méthode indisponible.This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. Dans l’exempleIn 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");
    }
}

la A classe déclare une méthode virtuelle, la B classe remplace cette méthode par une méthode abstraite, et la C classe substitue la méthode abstraite pour fournir sa propre implémentation.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.

Méthodes externesExternal methods

Lorsqu’une déclaration de méthode comprend extern un modificateur, cette méthode est dite externe.When a method declaration includes an extern modifier, that method is said to be an external method. Les méthodes externes sont implémentées en externe, généralement à l’aide C#d’un langage autre que.External methods are implemented externally, typically using a language other than C#. Étant donné qu’une déclaration de méthode externe ne fournit aucune implémentation réelle, le method_body d’une méthode externe se compose simplement d’un point-virgule.Because an external method declaration provides no actual implementation, the method_body of an external method simply consists of a semicolon. Une méthode externe ne peut pas être générique.An external method may not be generic.

Le extern modificateur est généralement utilisé conjointement avec un DllImport attribut (interopérabilité avec les composants COM et Win32), ce qui permet d’implémenter des méthodes externes par des dll (bibliothèques de liens dynamiques).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). L’environnement d’exécution peut prendre en charge d’autres mécanismes par lesquels des implémentations de méthodes externes peuvent être fournies.The execution environment may support other mechanisms whereby implementations of external methods can be provided.

Quand une méthode externe inclut un DllImport attribut, la déclaration de méthode doit également inclure static un modificateur.When an external method includes a DllImport attribute, the method declaration must also include a static modifier. Cet exemple illustre l’utilisation du extern modificateur et de l' DllImport attribut :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);
}

Méthodes partielles (récapitulatif)Partial methods (recap)

Lorsqu’une déclaration de méthode comprend partial un modificateur, cette méthode est dite partielle.When a method declaration includes a partial modifier, that method is said to be a partial method. Les méthodes partielles ne peuvent être déclarées que comme membres de types partiels (types partiels) et sont soumises à un certain nombre de restrictions.Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. Les méthodes partielles sont décrites plus en détail dans les méthodes partielles.Partial methods are further described in Partial methods.

Méthodes d’extensionExtension methods

Quand le premier paramètre d’une méthode comprend le this modificateur, cette méthode est considérée comme une méthode d’extension.When the first parameter of a method includes the this modifier, that method is said to be an extension method. Les méthodes d’extension ne peuvent être déclarées que dans des classes statiques non imbriquées et non génériques.Extension methods can only be declared in non-generic, non-nested static classes. Le premier paramètre d’une méthode d’extension ne peut pas avoir de modificateurs autres que this, et le type de paramètre ne peut pas être un type pointeur.The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

Voici un exemple de classe statique qui déclare deux méthodes d’extension :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;
    }
}

Une méthode d’extension est une méthode statique normale.An extension method is a regular static method. En outre, lorsque sa classe statique englobante est dans la portée, une méthode d’extension peut être appelée à l’aide de la syntaxe d’appel de méthode d’instance (appels de méthode d’extension), à l’aide de l’expression de récepteur comme premier argument.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.

Le programme suivant utilise les méthodes d’extension déclarées ci-dessus :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());
        }
    }
}

La Slice méthode est disponible sur le string[], et la ToInt32 méthode est disponible sur string, car elles ont été déclarées comme méthodes d’extension.The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. La signification du programme est la même que celle qui suit, à l’aide d’appels de méthode statique ordinaires :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));
        }
    }
}

Corps de la méthodeMethod body

Le method_body d’une déclaration de méthode se compose d’un corps de bloc, d’un corps d’expression ou d’un point-virgule.The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

Le type de résultat d’une méthode void est si le type de voidretour est, ou si la méthode est asynchrone et que le System.Threading.Tasks.Tasktype de retour est.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. Sinon, le type de résultat d’une méthode non asynchrone est son type de retour et le type de résultat d’une méthode Async avec le System.Threading.Tasks.Task<T> type Tde retour est.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.

Quand une méthode a un void type de résultat et un corps de return bloc, les instructions (l’instruction return) dans le bloc ne sont pas autorisées à spécifier une expression.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. Si l’exécution du bloc d’une méthode void se termine normalement (autrement dit, le contrôle circule à la fin du corps de la méthode), cette méthode retourne simplement à son appelant actuel.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.

Quand une méthode a un résultat void et un corps d’expression, l’expression E doit être un statement_expressionet le corps est exactement équivalent à un corps de bloc de la forme { 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; }.

Quand une méthode a un type de résultat non void et un corps de bloc, return chaque instruction du bloc doit spécifier une expression qui est implicitement convertible en type de résultat.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. Le point de terminaison d’un corps de bloc d’une méthode retournant une valeur ne doit pas être accessible.The endpoint of a block body of a value-returning method must not be reachable. En d’autres termes, dans une méthode retournant une valeur avec un corps de bloc, le contrôle n’est pas autorisé à circuler à la fin du corps de la méthode.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.

Quand une méthode a un type de résultat non void et un corps d’expression, l’expression doit être implicitement convertible en type de résultat, et le corps est exactement équivalent à un corps de bloc de { return E; }la forme.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; }.

Dans l’exempleIn 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;
}

la méthode qui retourne F une valeur génère une erreur au moment de la compilation, car le contrôle peut circuler à la fin du corps de la méthode.the value-returning F method results in a compile-time error because control can flow off the end of the method body. Les G méthodes H et sont correctes, car tous les chemins d’exécution possibles se terminent par une instruction return qui spécifie une valeur de retour.The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. La I méthode est correcte, car son corps est équivalent à un bloc d’instructions avec une seule instruction return.The I method is correct, because its body is equivalent to a statement block with just a single return statement in it.

Surcharge de méthodeMethod overloading

Les règles de résolution de surcharge de méthode sont décrites dans inférence de type.The method overload resolution rules are described in Type inference.

PropertiesProperties

Une propriété est un membre qui fournit l’accès à une caractéristique d’un objet ou d’une classe.A property is a member that provides access to a characteristic of an object or a class. Les exemples de propriétés incluent la longueur d’une chaîne, la taille d’une police, la légende d’une fenêtre, le nom d’un client, etc.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. Les propriétés sont une extension naturelle des champs, les deux étant des membres nommés avec des types associés, et la syntaxe d’accès aux champs et aux propriétés est la même.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. Toutefois, contrairement aux champs, les propriétés ne désignent pas des emplacements de stockage.However, unlike fields, properties do not denote storage locations. Au lieu de cela, les propriétés ont des accesseurs qui spécifient les instructions à exécuter lorsque les valeurs sont lues ou écrites.Instead, properties have accessors that specify the statements to be executed when their values are read or written. Les propriétés fournissent donc un mécanisme permettant d’associer des actions à la lecture et à l’écriture des attributs d’un objet. en outre, ils permettent de calculer ces attributs.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.

Les propriétés sont déclarées à l’aide de 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 ';'
    ;

Un property_declaration peut inclure un ensemble d' attributs (attributs) et une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès), le new (le nouveau modificateur), static (static et instance Méthodes), virtual (méthodes virtuelles), 0 (méthodes override), 2 (méthodes sealed), 4 (méthodes abstraites) et 6 (méthodes externes).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.

Les déclarations de propriété sont soumises aux mêmes règles que les déclarations de méthode (méthodes) en ce qui concerne les combinaisons valides de modificateurs.Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Le type d’une déclaration de propriété spécifie le type de la propriété introduite par la déclaration, et Member_Name spécifie le nom de la propriété.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. À moins que la propriété ne soit une implémentation de membre d’interface explicite, Member_Name est simplement un identificateur.Unless the property is an explicit interface member implementation, the member_name is simply an identifier. Pour une implémentation de membre d’interface explicite (implémentations de membres d’interface explicites), Member_Name se compose d’un interface_type suivi d’un « . » et d’un identificateur.For an explicit interface member implementation (Explicit interface member implementations), the member_name consists of an interface_type followed by a "." and an identifier.

Le type d’une propriété doit être au moins aussi accessible que la propriété elle-même (contraintes d’accessibilité).The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Un property_body peut être constitué d’un corps d’accesseur ou d’un corps d’expression.A property_body may either consist of an accessor body or an expression body. Dans un corps d’accesseur, accessor_declarations, qui doit être placé entre les jetons « { » et « } », déclare les accesseurs (accesseurs) de la propriété.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Les accesseurs spécifient les instructions exécutables associées à la lecture et à l’écriture de la propriété.The accessors specify the executable statements associated with reading and writing the property.

Un corps d’expression composé => de suivis d’une expression E et d’un point-virgule est exactement { get { return E; } }équivalent au corps de l’instruction et ne peut donc être utilisé que pour spécifier des propriétés Getter uniquement lorsque le résultat de l’accesseur Get est fourni par une expression unique.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.

Un property_initializer peut uniquement être fourni pour une propriété implémentée automatiquement (Propriétés implémentées automatiquement) et provoque l’initialisation du champ sous-jacent de telles propriétés avec la valeur donnée par l' expression .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.

Même si la syntaxe d’accès à une propriété est identique à celle d’un champ, une propriété n’est pas classée comme une variable.Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Par conséquent, il n’est pas possible de passer une propriété ref en out tant qu’argument ou.Thus, it is not possible to pass a property as a ref or out argument.

Lorsqu’une déclaration de propriété comprend extern un modificateur, la propriété est considérée comme une propriété externe.When a property declaration includes an extern modifier, the property is said to be an external property. Étant donné qu’une déclaration de propriété externe ne fournit aucune implémentation réelle, chacun de ses accessor_declarations se compose d’un point-virgule.Because an external property declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Propriétés statiques et d’instanceStatic and instance properties

Lorsqu’une déclaration de propriété comprend static un modificateur, la propriété est considérée comme une propriété statique.When a property declaration includes a static modifier, the property is said to be a static property. Quand aucun static modificateur n’est présent, la propriété est considérée comme une propriété d’instance.When no static modifier is present, the property is said to be an instance property.

Une propriété statique n’est pas associée à une instance spécifique, et il s’agit d’une erreur de compilation pour this faire référence aux accesseurs d’une propriété statique.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.

Une propriété d’instance est associée à une instance donnée d’une classe, et cette instance est accessible en this tant que (cet accès) dans les accesseurs de cette propriété.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.

Lorsqu’une propriété est référencée dans un member_access (accès aux membres) de la forme E.M, si M est une propriété statique, E doit désigner un type contenant M, et si M est une propriété d’instance, E doit désigner une instance d’un type contenant 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.

Les différences entre les membres statiques et les membres d’instance sont abordées plus en détail dans les membres statiques et d’instance.The differences between static and instance members are discussed further in Static and instance members.

AccesseursAccessors

Le accessor_declarations d’une propriété spécifie les instructions exécutables associées à la lecture et à l’écriture de cette propriété.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
    | ';'
    ;

Les déclarations d’accesseur se composent d’un get_accessor_declaration, d’un set_accessor_declarationou des deux.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Chaque déclaration d’accesseur se compose du jeton get ou set suivi d’un accessor_modifier facultatif et d’un accessor_body.Each accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

L’utilisation de accessor_modifiers est régie par les restrictions suivantes :The use of accessor_modifiers is governed by the following restrictions:

  • Un accessor_modifier ne peut pas être utilisé dans une interface ou dans une implémentation de membre d’interface explicite.An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • Pour une propriété ou un indexeur qui n’a pas de modificateur override, un accessor_modifier est autorisé uniquement si la propriété ou l’indexeur a à la fois un accesseur get et set, et n’est autorisé que sur l’un de ces accesseurs.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.
  • Pour une propriété ou un indexeur qui comprend un modificateur override, un accesseur doit correspondre au accessor_modifier, le cas échéant, de l’accesseur en cours de substitution.For a property or indexer that includes an override modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden.
  • Le accessor_modifier doit déclarer une accessibilité qui est strictement plus restrictive que l’accessibilité déclarée de la propriété ou de l’indexeur lui-même.The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. Pour être précis :To be precise:
    • Si la propriété ou l’indexeur a une accessibilité déclarée de public, accessor_modifier peut être protected internal, internal, protected ou private.If the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • Si la propriété ou l’indexeur a une accessibilité déclarée de protected internal, la valeur de accessor_modifier peut être internal, protected ou private.If the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • Si la propriété ou l’indexeur a une accessibilité déclarée de internal ou protected, accessor_modifier doit être private.If the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • Si la propriété ou l’indexeur a une accessibilité déclarée de private, aucun accessor_modifier ne peut être utilisé.If the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

Pour les propriétés abstract et extern, accessor_body pour chaque accesseur spécifié est simplement un point-virgule.For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. Une propriété non abstraite et non-extern peut avoir chaque accessor_body un point-virgule, auquel cas il s’agit d’une propriété implémentée automatiquement (Propriétés implémentées automatiquement).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). Une propriété implémentée automatiquement doit avoir au moins un accesseur Get.An automatically implemented property must have at least a get accessor. Pour les accesseurs de toute autre propriété non-abstract non-extern, accessor_body est un bloc qui spécifie les instructions à exécuter lorsque l’accesseur correspondant est appelé.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.

Un get accesseur correspond à une méthode sans paramètre avec une valeur de retour du type de propriété.A get accessor corresponds to a parameterless method with a return value of the property type. À l’exception de la cible d’une assignation, lorsqu’une propriété est référencée dans une expression get , l’accesseur de la propriété est appelé pour calculer la valeur de la propriété (valeurs des expressions).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). Le corps d’un get accesseur doit se conformer aux règles pour les méthodes retournant des valeurs décrites dans le corps de la méthode.The body of a get accessor must conform to the rules for value-returning methods described in Method body. En particulier, toutes return les instructions dans le corps d' get un accesseur doivent spécifier une expression qui est implicitement convertible en type de propriété.In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. En outre, le point de get terminaison d’un accesseur ne doit pas être accessible.Furthermore, the endpoint of a get accessor must not be reachable.

Un set accesseur correspond à une méthode avec un paramètre de valeur unique du type de propriété void et un type de retour.A set accessor corresponds to a method with a single value parameter of the property type and a void return type. Le paramètre implicite d' set un accesseur est valuetoujours nommé.The implicit parameter of a set accessor is always named value. Lorsqu’une propriété est référencée en tant que cible d’une assignation(opérateurs d’affectation), ou en tant ++ qu' -- opérande de ou (opérateurs suffixés d’incrémentation et de décrémentation, opérateurs de préfixe d’incrémentation et de décrémentation), l’accesseur est appelé avec un argument (dont la valeur est celle du côté droit de l’assignation ou l’opérande de l' ++ opérateur or -- ) qui fournit la nouvelle valeur (assignation simple). setWhen a property is referenced as the target of an assignment (Assignment operators), or as the operand of ++ or -- (Postfix increment and decrement operators, Prefix increment and decrement operators), the set accessor is invoked with an argument (whose value is that of the right-hand side of the assignment or the operand of the ++ or -- operator) that provides the new value (Simple assignment). Le corps d’un set accesseur doit se conformer void aux règles pour les méthodes décrites dans le corps de la méthode.The body of a set accessor must conform to the rules for void methods described in Method body. En particulier, return les instructions dans set le corps de l’accesseur ne sont pas autorisées à spécifier une expression.In particular, return statements in the set accessor body are not permitted to specify an expression. Comme un set accesseur a implicitement un paramètre valuenommé, il s’agit d’une erreur de compilation pour une variable locale ou une déclaration set de constante dans un accesseur portant ce nom.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.

En fonction de la présence ou de l' get absence set des accesseurs et, une propriété est classée comme suit :Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • Une propriété qui comprend à la get fois un accesseur et un set accesseur est considérée comme une propriété en lecture-écriture .A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • Une propriété qui a uniquement un get accesseur est considérée comme étant une propriété en lecture seule .A property that has only a get accessor is said to be a read-only property. Il s’agit d’une erreur de compilation pour qu’une propriété en lecture seule soit la cible d’une assignation.It is a compile-time error for a read-only property to be the target of an assignment.
  • Une propriété qui a uniquement un set accesseur est considérée comme étant une propriété en écriture seule .A property that has only a set accessor is said to be a write-only property. À l’exception de la cible d’une assignation, il s’agit d’une erreur au moment de la compilation pour référencer une propriété en écriture seule dans une expression.Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

Dans l’exempleIn 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
    }
}

le Button contrôle déclare une propriété publique Caption .the Button control declares a public Caption property. L' get accesseur de Caption la propriété retourne la chaîne stockée dans caption le champ privé.The get accessor of the Caption property returns the string stored in the private caption field. L' set accesseur vérifie si la nouvelle valeur est différente de la valeur actuelle, et si c’est le cas, elle stocke la nouvelle valeur et repeint le contrôle.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. Les propriétés suivent souvent le modèle ci-dessus : L' get accesseur retourne simplement une valeur stockée dans un champ privé, set et l’accesseur modifie ce champ privé, puis exécute toutes les actions supplémentaires requises pour mettre à jour complètement l’état de l’objet.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.

À partir Button de la classe ci-dessus, voici un exemple d’utilisation Caption de la propriété :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

Ici, l' set accesseur est appelé en affectant une valeur à la propriété, et l' get accesseur est appelé en référençant la propriété dans une expression.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.

Les get accesseurs et set d’une propriété ne sont pas des membres distincts et il n’est pas possible de déclarer les accesseurs d’une propriété séparément.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. Par conséquent, il n’est pas possible que les deux accesseurs d’une propriété en lecture-écriture aient une accessibilité différente.As such, it is not possible for the two accessors of a read-write property to have different accessibility. L’exempleThe 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; }
    }
}

ne déclare pas une propriété en lecture-écriture unique.does not declare a single read-write property. Au lieu de cela, elle déclare deux propriétés portant le même nom, une seule en lecture seule et une seule en écriture seule.Rather, it declares two properties with the same name, one read-only and one write-only. Dans la mesure où deux membres déclarés dans la même classe ne peuvent pas avoir le même nom, l’exemple provoque une erreur au moment de la compilation.Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

Lorsqu’une classe dérivée déclare une propriété à l’aide du même nom qu’une propriété héritée, la propriété dérivée masque la propriété héritée en ce qui concerne la lecture et l’écriture.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. Dans l’exempleIn the example

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

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

la P propriété dans B masque la propriété P dans en A ce qui concerne la lecture et l’écriture.the P property in B hides the P property in A with respect to both reading and writing. Par conséquent, dans les instructionsThus, 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

l’assignation à b.P provoque le signalement d’une erreur au moment de la compilation, puisque la P propriété en B lecture seule dans masque la propriété P en écriture Aseule dans.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. Notez, toutefois, qu’un cast peut être utilisé pour accéder à la P propriété Hidden.Note, however, that a cast can be used to access the hidden P property.

Contrairement aux champs publics, les propriétés fournissent une séparation entre l’état interne d’un objet et son interface publique.Unlike public fields, properties provide a separation between an object's internal state and its public interface. Prenons l’exemple suivant: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; }
    }
}

Ici, la Label classe utilise deux int champs, x et y, pour stocker son emplacement.Here, the Label class uses two int fields, x and y, to store its location. L’emplacement est exposé publiquement en tant X que Y et en tant que propriété Location et en tant Pointque propriété de type.The location is publicly exposed both as an X and a Y property and as a Location property of type Point. Si, dans une future version de Label, il devient plus pratique de stocker l’emplacement Point en interne, la modification peut être effectuée sans affecter l’interface publique de la classe :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; }
    }
}

Avait x public readonly Label et y à la place des champs, il aurait été impossible d’apporter une telle modification à la classe.Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

L’exposition de l’état via des propriétés n’est pas nécessairement moins efficace que l’exposition directe de champs.Exposing state through properties is not necessarily any less efficient than exposing fields directly. En particulier, lorsqu’une propriété est non virtuelle et ne contient qu’une petite quantité de code, l’environnement d’exécution peut remplacer les appels aux accesseurs par le code réel des accesseurs.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. Ce processus est appelé incorporation, et il rend l’accès aux propriétés aussi efficace que l’accès au champ, tout en préservant la flexibilité accrue des propriétés.This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Étant donné que l' get appel d’un accesseur est conceptuellement équivalent à la lecture de la valeur d’un champ, il get est considéré comme un style de programmation incorrect pour les accesseurs d’avoir des effets secondaires observables.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. Dans l’exempleIn the example

class Counter
{
    private int next;

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

la valeur de la Next propriété dépend du nombre de fois où l’accès à la propriété a été effectué précédemment.the value of the Next property depends on the number of times the property has previously been accessed. Ainsi, l’accès à la propriété produit un effet secondaire observable, et la propriété doit être implémentée comme une méthode à la place.Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

La Convention « sans effets secondaires » pour get les accesseurs ne signifie pas que get les accesseurs doivent toujours être écrits pour renvoyer simplement des valeurs stockées dans des champs.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. En effet get , les accesseurs calculent souvent la valeur d’une propriété en accédant à plusieurs champs ou en appelant des méthodes.Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. Toutefois, un accesseur get correctement conçu n’effectue aucune action qui entraîne des modifications observables de l’état de l’objet.However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

Les propriétés peuvent être utilisées pour différer l’initialisation d’une ressource jusqu’au moment où elle est référencée pour la première fois.Properties can be used to delay initialization of a resource until the moment it is first referenced. Exemple :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;
        }
    }
}

La Console classe contient trois propriétés, In, Outet Error, qui représentent respectivement les périphériques d’entrée, de sortie et d’erreur standard.The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. En exposant ces membres en tant que propriétés, Console la classe peut différer leur initialisation jusqu’à ce qu’elles soient réellement utilisées.By exposing these members as properties, the Console class can delay their initialization until they are actually used. Par exemple, lors de la première Out référence à la propriété, comme dansFor example, upon first referencing the Out property, as in

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

le sous TextWriter -jacent pour le périphérique de sortie est créé.the underlying TextWriter for the output device is created. Toutefois, si l’application ne fait pas référence In aux Error propriétés et, aucun objet n’est créé pour ces appareils.But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

Propriétés implémentées automatiquementAutomatically implemented properties

Une propriété implémentée automatiquement (ou propriété automatique pour Short) est une propriété non-extern non abstraite avec des corps d’accesseur de point-virgule uniquement.An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Les propriétés automatiques doivent avoir un accesseur get et peuvent éventuellement avoir un accesseur Set.Auto-properties must have a get accessor and can optionally have a set accessor.

Quand une propriété est spécifiée en tant que propriété implémentée automatiquement, un champ de stockage masqué est automatiquement disponible pour la propriété, et les accesseurs sont implémentés pour lire et écrire dans ce champ de stockage.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. Si la propriété automatique n’a pas d’accesseur Set, le champ de stockage readonly est pris en compte (champs en lecture seule).If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Tout comme un readonly champ, une propriété automatique Getter uniquement peut également être assignée à dans le corps d’un constructeur de la classe englobante.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. Une assignation de ce type est directement assignée au champ de stockage en lecture seule de la propriété.Such an assignment assigns directly to the readonly backing field of the property.

Une propriété automatique peut éventuellement avoir un property_initializer, qui est appliqué directement au champ de stockage en tant que variable_initializer (initialiseurs de variable).An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

L’exemple suivant :The following example:

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

équivaut à la déclaration suivante :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; } }
}

L’exemple suivant :The following example:

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

équivaut à la déclaration suivante :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; }
}

Notez que les assignations au champ ReadOnly sont autorisées, car elles se produisent dans le constructeur.Notice that the assignments to the readonly field are legal, because they occur within the constructor.

AccessibilitéAccessibility

Si un accesseur a un accessor_modifier, le domaine d’accessibilité (domaines d’accessibilité) de l’accesseur est déterminé à l’aide de l’accessibilité déclarée de 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. Si un accesseur n’a pas de accessor_modifier, le domaine d’accessibilité de l’accesseur est déterminé à partir de l’accessibilité déclarée de la propriété ou de l’indexeur.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.

La présence d’un accessor_modifier n’affecte jamais la recherche de membre (opérateurs) ou la résolution de surcharge (résolution de surcharge).The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). Les modificateurs de la propriété ou de l’indexeur déterminent toujours la propriété ou l’indexeur auquel il est lié, quel que soit le contexte de l’accès.The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

Une fois qu’une propriété ou un indexeur particulier a été sélectionné, les domaines d’accessibilité des accesseurs spécifiques impliqués sont utilisés pour déterminer si cette utilisation est valide :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:

Dans l’exemple suivant, la propriété A.Text est masquée par la B.Textpropriété, même dans les contextes où set seul l’accesseur est appelé.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. En revanche, la propriété B.Count n’est pas accessible à Mla classe, donc la A.Count propriété accessible est utilisée à la place.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
    }
}

Un accesseur utilisé pour implémenter une interface peut ne pas avoir de accessor_modifier.An accessor that is used to implement an interface may not have an accessor_modifier. Si un seul accesseur est utilisé pour implémenter une interface, l’autre accesseur peut être déclaré avec un 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
    }
}

Accesseurs de propriété Virtual, sealed, override et abstractVirtual, sealed, override, and abstract property accessors

Une virtual déclaration de propriété spécifie que les accesseurs de la propriété sont virtuels.A virtual property declaration specifies that the accessors of the property are virtual. Le virtual modificateur s’applique aux deux accesseurs d’une propriété en lecture-écriture ; il n’est pas possible pour un seul accesseur d’une propriété en lecture-écriture d’être virtuel.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.

Une abstract déclaration de propriété spécifie que les accesseurs de la propriété sont virtuels, mais ne fournissent pas d’implémentation réelle des accesseurs.An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. Au lieu de cela, les classes dérivées non abstraites sont requises pour fournir leur propre implémentation des accesseurs en substituant la propriété.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Comme un accesseur pour une déclaration de propriété abstraite ne fournit pas d’implémentation réelle, son accessor_body se compose simplement d’un point-virgule.Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

Une déclaration de propriété qui comprend à abstract la override fois les modificateurs et spécifie que la propriété est abstraite et substitue une propriété de base.A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Les accesseurs d’une telle propriété sont également abstraits.The accessors of such a property are also abstract.

Les déclarations de propriété abstraite sont autorisées uniquement dans les classes abstraites (classes abstraites). Les accesseurs d’une propriété virtuelle héritée peuvent être substitués dans une classe dérivée en incluant une déclaration de propriété override qui spécifie une directive.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. C’est ce qu’on appelle une déclaration de propriété de substitution.This is known as an overriding property declaration. Une déclaration de propriété de substitution ne déclare pas de nouvelle propriété.An overriding property declaration does not declare a new property. Au lieu de cela, il spécialise simplement les implémentations des accesseurs d’une propriété virtuelle existante.Instead, it simply specializes the implementations of the accessors of an existing virtual property.

Une déclaration de propriété de substitution doit spécifier exactement les mêmes modificateurs d’accessibilité, le même type et le même nom que la propriété héritée.An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. Si la propriété héritée n’a qu’un seul accesseur (autrement dit, si la propriété héritée est en lecture seule ou en écriture seule), la propriété de substitution doit inclure uniquement cet accesseur.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. Si la propriété héritée inclut des accesseurs (c’est-à-dire que si la propriété héritée est en lecture-écriture), la propriété de substitution peut inclure un seul accesseur ou les deux accesseurs.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.

Une déclaration de propriété de substitution peut inclure sealed le modificateur.An overriding property declaration may include the sealed modifier. L’utilisation de ce modificateur empêche une classe dérivée de substituer davantage la propriété.Use of this modifier prevents a derived class from further overriding the property. Les accesseurs d’une propriété sealed sont également sealed.The accessors of a sealed property are also sealed.

À l’exception des différences dans la syntaxe de déclaration et d’appel, les accesseurs Virtual, sealed, override et abstract se comportent exactement comme des méthodes virtuelles, sealed, override et abstract.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Plus précisément, les règles décrites dans les méthodes virtuelles, les méthodes override, les méthodes sealedet les méthodes abstraites s’appliquent comme si les accesseurs étaient des méthodes d’un formulaire correspondant :Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • Un get accesseur correspond à une méthode sans paramètre avec une valeur de retour du type de propriété et les mêmes modificateurs que la propriété conteneur.A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • Un set accesseur correspond à une méthode avec un paramètre de valeur unique du type de propriété void , un type de retour et les mêmes modificateurs que la propriété conteneur.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.

Dans l’exempleIn 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; }
}

Xest une propriété en lecture seule virtuelle, Y est une propriété en lecture-écriture virtuelle et Z est une propriété en lecture-écriture abstraite.X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. Étant Z donné que est abstrait, la A classe conteneur doit également être déclarée abstract.Because Z is abstract, the containing class A must also be declared abstract.

Une classe qui dérive de A est affichée ci-dessous :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; }
    }
}

Ici, les déclarations de X, Yet Z remplacent les déclarations de propriété.Here, the declarations of X, Y, and Z are overriding property declarations. Chaque déclaration de propriété correspond exactement aux modificateurs d’accessibilité, au type et au nom de la propriété héritée correspondante.Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. L' get accesseur X de et set l’accesseur de base Y utilisent le mot clé pour accéder aux accesseurs hérités.The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. La déclaration de Z substitue les deux accesseurs abstraits. par conséquent, il n’existe aucun membre de Bfonction abstraite en attente dans, et B il est autorisé à être une classe non abstraite.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.

Quand une propriété est déclarée en overridetant que, tout accesseur substitué doit être accessible au code de substitution.When a property is declared as an override, any overridden accessors must be accessible to the overriding code. En outre, l’accessibilité déclarée de la propriété ou de l’indexeur lui-même, et des accesseurs, doit correspondre à celle du membre et des accesseurs substitués.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. Exemple :For example:

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

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

EventsEvents

Un événement est un membre qui permet à un objet ou à une classe de fournir des notifications.An event is a member that enables an object or class to provide notifications. Les clients peuvent joindre du code exécutable pour les événements en fournissant des gestionnaires d’événements.Clients can attach executable code for events by supplying event handlers.

Les événements sont déclarés à l’aide de 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
    ;

Un event_declaration peut inclure un ensemble d' attributs (attributs) et une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès), le new (le nouveau modificateur), static (static et instance Méthodes), virtual (méthodes virtuelles), 0 (méthodes override), 2 (méthodes sealed), 4 (méthodes abstraites) et 6 (méthodes externes).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.

Les déclarations d’événements sont soumises aux mêmes règles que les déclarations de méthode (méthodes) en ce qui concerne les combinaisons valides de modificateurs.Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Le type d’une déclaration d’événement doit être un delegate_type (types référence) et delegate_type doit être au moins aussi accessible que l’événement lui-même (contraintes d’accessibilité).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).

Une déclaration d’événement peut inclure event_accessor_declarations.An event declaration may include event_accessor_declarations. Toutefois, si ce n’est pas le cas pour les événements non-extern et non abstraits, le compilateur les fournit automatiquement (événements de type champ); pour les événements extern, les accesseurs sont fournis en externe.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.

Une déclaration d’événement qui omet event_accessor_declarations définit un ou plusieurs événements, un pour chacun des variable_declarators.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarators. Les attributs et les modificateurs s’appliquent à tous les membres déclarés par un event_declaration.The attributes and modifiers apply to all of the members declared by such an event_declaration.

Il s’agit d’une erreur au moment de la compilation pour qu’un event_declaration inclue à la fois le modificateur abstract et les event_accessor_declarationsdélimités par des accolades.It is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

Lorsqu’une déclaration Event comprend un extern modificateur, l’événement est considéré comme un événement externe.When an event declaration includes an extern modifier, the event is said to be an external event. Étant donné qu’une déclaration d’événement externe ne fournit aucune implémentation réelle, il y a une erreur pour qu’elle inclue à la fois le modificateur extern et 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.

Il s’agit d’une erreur de compilation pour un variable_declarator d’une déclaration d’événement avec un modificateur abstract ou external pour inclure un 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.

Un événement peut être utilisé comme opérande de gauche des opérateurs += et -= (assignation d'événement).An event can be used as the left-hand operand of the += and -= operators (Event assignment). Ces opérateurs sont utilisés, respectivement, pour attacher des gestionnaires d’événements à ou pour supprimer des gestionnaires d’événements d’un événement, et les modificateurs d’accès de l’événement contrôlent les contextes dans lesquels ces opérations sont autorisées.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.

Étant += donné -= que et sont les seules opérations autorisées sur un événement à l’extérieur du type qui déclare l’événement, le code externe peut ajouter et supprimer des gestionnaires pour un événement, mais ne peut pas obtenir ou modifier la liste sous-jacente de l’événement. gestionnaires d'.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.

Dans une opération de la forme x += y ou x -= y, quand x est un événement et que la référence a lieu à l’extérieur du type qui contient xla déclaration de, le résultat de l' void opération a le type (par opposition à having type de x, avec la valeur de x après l’assignation.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). Cette règle interdit au code externe d’examiner indirectement le délégué sous-jacent d’un événement.This rule prohibits external code from indirectly examining the underlying delegate of an event.

L’exemple suivant montre comment les gestionnaires d’événements sont attachés aux instances Button de la classe :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
    }
}

Ici, le LoginDialog constructeur d’instance crée Button deux instances et attache des gestionnaires d' Click événements aux événements.Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

Événements de type champField-like events

Dans le texte de programme de la classe ou du struct qui contient la déclaration d’un événement, certains événements peuvent être utilisés comme des champs.Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. Pour être utilisée de cette manière, un événement ne doit pas être abstract ou extern, et ne doit pas inclure explicitement 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. Un tel événement peut être utilisé dans n’importe quel contexte qui autorise un champ.Such an event can be used in any context that permits a field. Le champ contient un délégué (délégués) qui fait référence à la liste des gestionnaires d’événements qui ont été ajoutés à l’événement.The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. Si aucun gestionnaire d’événements n’a été ajouté, le champ nullcontient.If no event handlers have been added, the field contains null.

Dans l’exempleIn 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;
    }
}

Clickest utilisé en tant que champ dans Button la classe.Click is used as a field within the Button class. Comme le montre l’exemple, le champ peut être examiné, modifié et utilisé dans les expressions d’appel de délégué.As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. La OnClick méthode de la Button classe « déclenche » l' Click événement.The OnClick method in the Button class "raises" the Click event. La notion de déclenchement d’un événement est équivalente à l’appel de délégué représenté par l’événement. Par conséquent, il n’existe aucune construction de langage particulière pour déclencher des événements.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. Notez que l’appel de délégué est précédé d’une vérification qui garantit que le délégué est non null.Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

En dehors de la Déclaration Button de la classe Click , le membre peut uniquement être utilisé sur le côté gauche des += opérateurs et -= , comme dansOutside 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(...);

qui ajoute un délégué à la liste d’appel de l' Click événement, etwhich appends a delegate to the invocation list of the Click event, and

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

qui supprime un délégué de la liste d’appel de l' Click événement.which removes a delegate from the invocation list of the Click event.

Lors de la compilation d’un événement de type champ, le compilateur crée automatiquement le stockage pour contenir le délégué et crée des accesseurs pour l’événement qui ajoute ou supprime des gestionnaires d’événements dans le champ délégué.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. Les opérations d’ajout et de suppression sont thread-safe et peuvent (mais n’ont pas besoin d’être) effectuées tout en détenant le verrou (l’instruction lock) sur l’objet conteneur d’un événement d’instance, ou le type Object (expressions de création d’objets anonymes) pour un événement statique.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.

Ainsi, une déclaration d’événement d’instance de la forme :Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

sera compilé en une valeur équivalente à :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 */
        }
    }
}

Dans la classe X, les références Ev à sur le côté gauche des += opérateurs et -= entraînent l’appel des accesseurs Add et Remove.Within the class X, references to Ev on the left-hand side of the += and -= operators cause the add and remove accessors to be invoked. Toutes les autres références Ev à sont compilées pour référencer __Ev le champ masqué à la place (accès aux membres).All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). Le nom «__Ev» est arbitraire ; le champ masqué peut avoir n’importe quel nom ou aucun nom.The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

Accesseurs d’événementEvent accessors

Les déclarations d’événements omettent généralement event_accessor_declarations, comme dans l’exemple Button ci-dessus.Event declarations typically omit event_accessor_declarations, as in the Button example above. Une situation pour cela implique le cas où le coût de stockage d’un champ par événement n’est pas acceptable.One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. Dans ce cas, une classe peut inclure des event_accessor_declarations et utiliser un mécanisme privé pour stocker la liste des gestionnaires d’événements.In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

Le event_accessor_declarations d’un événement spécifie les instructions exécutables associées à l’ajout et à la suppression de gestionnaires d’événements.The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

Les déclarations d’accesseur se composent d’un add_accessor_declaration et d’un remove_accessor_declaration.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Chaque déclaration d’accesseur se compose add du remove jeton ou suivi d’un bloc.Each accessor declaration consists of the token add or remove followed by a block. Le bloc associé à un add_accessor_declaration spécifie les instructions à exécuter lors de l’ajout d’un gestionnaire d’événements, et le bloc associé à un remove_accessor_declaration spécifie les instructions à exécuter. Lorsqu’un gestionnaire d’événements est supprimé.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.

Chaque add_accessor_declaration et remove_accessor_declaration correspond à une méthode avec un paramètre de valeur unique du type d’événement et un type de retour void.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. Le paramètre implicite d’un accesseur d' valueévénement est nommé.The implicit parameter of an event accessor is named value. Lorsqu’un événement est utilisé dans une assignation d’événement, l’accesseur d’événement approprié est utilisé.When an event is used in an event assignment, the appropriate event accessor is used. Plus précisément, si l’opérateur d' += assignation est, l’accesseur Add est utilisé, et si l' -= opérateur d’assignation est, l’accesseur remove est utilisé.Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is -= then the remove accessor is used. Dans les deux cas, l’opérande droit de l’opérateur d’assignation est utilisé comme argument de l’accesseur d’événement.In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. Le bloc d’un add_accessor_declaration ou d’un remove_accessor_declaration doit respecter les règles pour les méthodes void décrites dans le corps de la méthode.The block of an add_accessor_declaration or a remove_accessor_declaration must conform to the rules for void methods described in Method body. En particulier, return les instructions d’un tel bloc ne sont pas autorisées à spécifier une expression.In particular, return statements in such a block are not permitted to specify an expression.

Comme un accesseur d’événement a implicitement un valueparamètre nommé, il s’agit d’une erreur de compilation pour une variable locale ou une constante déclarée dans un accesseur d’événement pour avoir ce nom.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.

Dans l’exempleIn 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);
    }
}

la Control classe implémente un mécanisme de stockage interne pour les événements.the Control class implements an internal storage mechanism for events. La AddEventHandler méthode associe une valeur de délégué à une clé GetEventHandler , la méthode retourne le délégué actuellement associé à une clé, RemoveEventHandler et la méthode supprime un délégué en tant que gestionnaire d’événements pour l’événement spécifié.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. Vraisemblablement, le mécanisme de stockage sous-jacent est conçu de telle sorte qu’il n’y null a aucun coût d’association d’une valeur de délégué à une clé, et par conséquent, les événements non gérés ne consomment aucun stockage.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.

Événements statiques et d’instanceStatic and instance events

Lorsqu’une déclaration Event comprend un static modificateur, l’événement est considéré comme un événement statique.When an event declaration includes a static modifier, the event is said to be a static event. Si aucun static modificateur n’est présent, l’événement est considéré comme un événement d’instance.When no static modifier is present, the event is said to be an instance event.

Un événement statique n’est pas associé à une instance spécifique, et il s’agit d’une erreur de compilation pour this faire référence aux accesseurs d’un événement statique.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.

Un événement d’instance est associé à une instance donnée d’une classe, et cette instance est accessible en this tant que (cet accès) dans les accesseurs de cet événement.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.

Lorsqu’un événement est référencé dans un member_access (accès aux membres) de la forme E.M, si M est un événement statique, E doit désigner un type contenant M, et si M est un événement d’instance, E doit désigner une instance d’un type contenant 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.

Les différences entre les membres statiques et les membres d’instance sont abordées plus en détail dans les membres statiques et d’instance.The differences between static and instance members are discussed further in Static and instance members.

Accesseurs d’événement virtuels, sealed, override et abstractVirtual, sealed, override, and abstract event accessors

Une virtual déclaration d’événement spécifie que les accesseurs de cet événement sont virtuels.A virtual event declaration specifies that the accessors of that event are virtual. Le virtual modificateur s’applique aux deux accesseurs d’un événement.The virtual modifier applies to both accessors of an event.

Une abstract déclaration d’événement spécifie que les accesseurs de l’événement sont virtuels, mais ne fournissent pas d’implémentation réelle des accesseurs.An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. Au lieu de cela, les classes dérivées non abstraites sont requises pour fournir leur propre implémentation des accesseurs en substituant l’événement.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Étant donné qu’une déclaration d’événement abstraite ne fournit pas d’implémentation réelle, elle ne peut pas fournir des event_accessor_declarationsdélimités par des accolades.Because an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

Une déclaration d’événement qui comprend à abstract la override fois les modificateurs et spécifie que l’événement est abstrait et substitue un événement de base.An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. Les accesseurs d’un tel événement sont également abstraits.The accessors of such an event are also abstract.

Les déclarations d’événements abstract sont autorisées uniquement dans les classes abstraites (classes abstraites).Abstract event declarations are only permitted in abstract classes (Abstract classes).

Les accesseurs d’un événement virtuel hérité peuvent être substitués dans une classe dérivée en incluant une déclaration d’événement qui override spécifie un modificateur.The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override modifier. C’est ce qu’on appelle une déclaration d’événement de substitution.This is known as an overriding event declaration. Une déclaration d’événement de substitution ne déclare pas un nouvel événement.An overriding event declaration does not declare a new event. Au lieu de cela, il spécialise simplement les implémentations des accesseurs d’un événement virtuel existant.Instead, it simply specializes the implementations of the accessors of an existing virtual event.

Une déclaration d’événement de substitution doit spécifier exactement les mêmes modificateurs d’accessibilité, le même type et le même nom que l’événement substitué.An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

Une déclaration d’événement de substitution peut inclure sealed le modificateur.An overriding event declaration may include the sealed modifier. L’utilisation de ce modificateur empêche une classe dérivée de substituer davantage l’événement.Use of this modifier prevents a derived class from further overriding the event. Les accesseurs d’un événement sealed sont également sealed.The accessors of a sealed event are also sealed.

Il s’agit d’une erreur au moment de la compilation pour qu’une déclaration d' new événement de substitution inclue un modificateur.It is a compile-time error for an overriding event declaration to include a new modifier.

À l’exception des différences dans la syntaxe de déclaration et d’appel, les accesseurs Virtual, sealed, override et abstract se comportent exactement comme des méthodes virtuelles, sealed, override et abstract.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Plus précisément, les règles décrites dans les méthodes virtuelles, les méthodes override, les méthodes sealedet les méthodes abstraites s’appliquent comme si les accesseurs étaient des méthodes d’un formulaire correspondant.Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form. Chaque accesseur correspond à une méthode avec un paramètre de valeur unique du type d’événement void , un type de retour et les mêmes modificateurs que l’événement conteneur.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.

IndexeursIndexers

Un indexeur est un membre qui permet à un objet d’être indexé de la même manière qu’un tableau.An indexer is a member that enables an object to be indexed in the same way as an array. Les indexeurs sont déclarés à l’aide de 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 ';'
    ;

Un indexer_declaration peut inclure un ensemble d' attributs (attributs) et une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès), le new (le nouveau modificateur), virtual (méthodes virtuelles ), override (méthodes override), 0 (méthodes sealed), 2 (méthodes abstraites) et les modificateurs 4 (méthodes externes).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.

Les déclarations de l’indexeur sont soumises aux mêmes règles que les déclarations de méthode (méthodes) en ce qui concerne les combinaisons valides de modificateurs, à la seule exception que le modificateur static n’est pas autorisé sur une déclaration d’indexeur.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.

Les modificateurs virtual, overrideet abstract s’excluent mutuellement, sauf dans un cas.The modifiers virtual, override, and abstract are mutually exclusive except in one case. Les abstract modificateurs et override peuvent être utilisés ensemble afin qu’un indexeur abstrait puisse remplacer un indexeur virtuel.The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

Le type d’une déclaration d’indexeur spécifie le type d’élément de l’indexeur introduit par la déclaration.The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. À moins que l’indexeur ne soit une implémentation de membre d’interface explicite, le type thisest suivi du mot clé.Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. Pour une implémentation de membre d’interface explicite, le type est suivi d’un INTERFACE_TYPE, d’un « . » et du mot clé this.For an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. Contrairement aux autres membres, les indexeurs n’ont pas de noms définis par l’utilisateur.Unlike other members, indexers do not have user-defined names.

Formal_parameter_list spécifie les paramètres de l’indexeur.The formal_parameter_list specifies the parameters of the indexer. La liste de paramètres formels d’un indexeur correspond à celle d’une méthode (paramètres de méthode), à ceci près qu’au moins un paramètre doit être ref spécifié out et que les modificateurs de paramètres et ne sont pas autorisés.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.

Le type d’un indexeur et chacun des types référencés dans le formal_parameter_list doivent être au moins aussi accessibles que l’indexeur lui-même (contraintes d’accessibilité).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).

Un indexer_body peut être constitué d’un corps d’accesseur ou d’un corps d’expression.An indexer_body may either consist of an accessor body or an expression body. Dans un corps d’accesseur, accessor_declarations, qui doit être placé entre les jetons « { » et « } », déclare les accesseurs (accesseurs) de la propriété.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Les accesseurs spécifient les instructions exécutables associées à la lecture et à l’écriture de la propriété.The accessors specify the executable statements associated with reading and writing the property.

Un corps d’expression composé de=>«» suivi d’une E expression et d’un point-virgule est exactement équivalent { get { return E; } }au corps de l’instruction et ne peut donc être utilisé que pour spécifier des indexeurs Getter uniquement où le résultat de l’accesseur Get est fourni par une expression unique.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.

Même si la syntaxe d’accès à un élément d’indexeur est identique à celle d’un élément de tableau, un élément d’indexeur n’est pas classifié comme une variable.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. Par conséquent, il n’est pas possible de passer un élément d’indexeur out en tant qu' ref argument ou.Thus, it is not possible to pass an indexer element as a ref or out argument.

La liste de paramètres formels d’un indexeur définit la signature (signatures et surcharge) de l’indexeur.The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. Plus précisément, la signature d’un indexeur se compose du nombre et des types de ses paramètres formels.Specifically, the signature of an indexer consists of the number and types of its formal parameters. Le type d’élément et les noms des paramètres formels ne font pas partie de la signature d’un indexeur.The element type and names of the formal parameters are not part of an indexer's signature.

La signature d’un indexeur doit être différente des signatures de tous les autres indexeurs déclarés dans la même classe.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Les indexeurs et les propriétés sont très similaires, mais diffèrent des façons suivantes :Indexers and properties are very similar in concept, but differ in the following ways:

  • Une propriété est identifiée par son nom, tandis qu’un indexeur est identifié par sa signature.A property is identified by its name, whereas an indexer is identified by its signature.
  • Une propriété est accessible par le biais d’un simple_name (noms simples) ou d’un member_access (accès aux membres), tandis qu’un élément d’indexeur est accessible via un element_access (accès à l'indexeur).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).
  • Une propriété peut être un static membre, alors qu’un indexeur est toujours un membre d’instance.A property can be a static member, whereas an indexer is always an instance member.
  • Un get accesseur d’une propriété correspond à une méthode sans paramètres, alors qu' get un accesseur d’un indexeur correspond à une méthode avec la même liste de paramètres formels que l’indexeur.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.
  • Un set accesseur d’une propriété correspond à une méthode avec un paramètre unique valuenommé, alors set qu’un accesseur d’un indexeur correspond à une méthode avec la même liste de paramètres formels que l’indexeur, plus un paramètre supplémentaire nommé 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.
  • Il s’agit d’une erreur de compilation permettant à un accesseur d’indexeur de déclarer une variable locale portant le même nom qu’un paramètre d’indexeur.It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • Dans une déclaration de propriété de substitution, la propriété héritée est accessible à base.Pl’aide P de la syntaxe, où est le nom de la propriété.In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. Dans une déclaration d’indexeur de substitution, l’indexeur hérité est accessible à l' base[E]aide de E la syntaxe, où est une liste d’expressions séparées par des virgules.In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • Il n’existe aucun concept d’indexeur implémenté automatiquement.There is no concept of an "automatically implemented indexer". Il s’agit d’une erreur d’avoir un indexeur non abstrait non externe avec des accesseurs de point-virgule.It is an error to have a non-abstract, non-external indexer with semicolon accessors.

Hormis ces différences, toutes les règles définies dans les accesseurs et les Propriétés implémentées automatiquement s’appliquent aux accesseurs d’indexeur ainsi qu’aux accesseurs de propriété.Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

Lorsqu’une déclaration d’indexeur comprend extern un modificateur, l’indexeur est considéré comme un indexeur externe.When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer. Étant donné qu’une déclaration d’indexeur externe ne fournit aucune implémentation réelle, chacun de ses accessor_declarations se compose d’un point-virgule.Because an external indexer declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

L’exemple ci-dessous déclare BitArray une classe qui implémente un indexeur pour accéder aux bits individuels dans le tableau de bits.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);
            }
        }
    }
}

Une instance de la BitArray classe consomme sensiblement moins de mémoire qu’une bool[] correspondante (puisque chaque valeur du premier n’occupe qu’un seul bit au lieu du dernier octet), mais elle autorise les mêmes opérations qu’un 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[].

La classe CountPrimes suivante utilise un BitArray et l’algorithme « crible » classique pour calculer le nombre de premiers compris entre 1 et un maximum donné :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);
    }
}

Notez que la syntaxe pour accéder aux éléments de BitArray est exactement la même que pour un. bool[]Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

L’exemple suivant montre une classe de grille 26 * 10 qui a un indexeur avec deux paramètres.The following example shows a 26 * 10 grid class that has an indexer with two parameters. Le premier paramètre doit être une lettre majuscule ou minuscule dans la plage A-Z, tandis que le second doit être un entier compris dans la plage 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;
        }
    }
}

Surcharge de l’indexeurIndexer overloading

Les règles de résolution de surcharge de l’indexeur sont décrites dans inférence de type.The indexer overload resolution rules are described in Type inference.

OpérateursOperators

Un opérateur est un membre qui définit la signification d’un opérateur d’expression qui peut être appliqué aux instances de la classe.An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. Les opérateurs sont déclarés à l’aide de 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 ';'
    | ';'
    ;

Il existe trois catégories d’opérateurs surchargeables : Opérateurs unaires (opérateurs unaires), opérateurs binaires (opérateurs binaires) et opérateurs de conversion (opérateurs de conversion).There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

Operator_body est un point-virgule, un corps d’instruction ou un corps d’expression.The operator_body is either a semicolon, a statement body or an expression body. Un corps d’instruction se compose d’un bloc, qui spécifie les instructions à exécuter lorsque l’opérateur est appelé.A statement body consists of a block, which specifies the statements to execute when the operator is invoked. Le bloc doit se conformer aux règles pour les méthodes qui retournent des valeurs décrites dans le corps de la méthode.The block must conform to the rules for value-returning methods described in Method body. Un corps d’expression se => compose de suivis d’une expression et d’un point-virgule, et désigne une expression unique à exécuter lorsque l’opérateur est appelé.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked.

Pour les opérateurs extern, operator_body se compose simplement d’un point-virgule.For extern operators, the operator_body consists simply of a semicolon. Pour tous les autres opérateurs, operator_body est soit un corps de bloc, soit un corps d’expression.For all other operators, the operator_body is either a block body or an expression body.

Les règles suivantes s’appliquent à toutes les déclarations d’opérateur :The following rules apply to all operator declarations:

  • Une déclaration d’opérateur doit inclure à public la fois static un et un modificateur.An operator declaration must include both a public and a static modifier.
  • Le (s) paramètre (s) d’un opérateur doivent être des paramètres de valeur (paramètres de valeur).The parameter(s) of an operator must be value parameters (Value parameters). Il s’agit d’une erreur de compilation pour qu’une déclaration d' ref opérateur out spécifie des paramètres ou.It is a compile-time error for an operator declaration to specify ref or out parameters.
  • La signature d’un opérateur (opérateurs unaires, opérateurs binaires, opérateurs de conversion) doit être différente des signatures de tous les autres opérateurs déclarés dans la même classe.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.
  • Tous les types référencés dans une déclaration d’opérateur doivent être au moins aussi accessibles que l’opérateur lui-même (contraintes d’accessibilité).All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • Il s’agit d’une erreur pour que le même modificateur apparaisse plusieurs fois dans une déclaration d’opérateur.It is an error for the same modifier to appear multiple times in an operator declaration.

Chaque catégorie d’opérateur impose des restrictions supplémentaires, comme décrit dans les sections suivantes.Each operator category imposes additional restrictions, as described in the following sections.

Comme les autres membres, les opérateurs déclarés dans une classe de base sont hérités par les classes dérivées.Like other members, operators declared in a base class are inherited by derived classes. Étant donné que les déclarations d’opérateur requièrent toujours la classe ou le struct dans lequel l’opérateur est déclaré pour participer à la signature de l’opérateur, il n’est pas possible pour un opérateur déclaré dans une classe dérivée de masquer un opérateur déclaré dans une classe de base.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. Ainsi, le new modificateur n’est jamais requis et, par conséquent, n’est jamais autorisé dans une déclaration d’opérateur.Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

Vous trouverez des informations supplémentaires sur les opérateurs unaires et binaires dans opérateurs.Additional information on unary and binary operators can be found in Operators.

Vous trouverez des informations supplémentaires sur les opérateurs de conversion dans les conversions définies par l’utilisateur.Additional information on conversion operators can be found in User-defined conversions.

Les opérateurs unaires.Unary operators

Les règles suivantes s’appliquent aux déclarations d’opérateur T unaires, où désigne le type d’instance de la classe ou du struct qui contient la déclaration d’opérateur :The following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Un opérateur + -unaire ~ T ,, ou doit accepter un seul paramètre de type ou T? et peut retourner n’importe quel type. !A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • Un opérateur ++ or -- unaire doit accepter un seul paramètre de T type T? ou et doit retourner ce même type ou un type dérivé de celui-ci.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.
  • Un opérateur true or false unaire doit accepter un seul paramètre de T type T? ou et doit retourner boolle type.A unary true or false operator must take a single parameter of type T or T? and must return type bool.

La signature d’un opérateur unaire se compose du jeton d'+opérateur -( !, ~, ++, --, true,, falseou) et du type du paramètre formel unique.The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. Le type de retour ne fait pas partie de la signature d’un opérateur unaire, ni du nom du paramètre formel.The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

Les true opérateurs false unaires et requièrent une déclaration de couple.The true and false unary operators require pair-wise declaration. Une erreur de compilation se produit si une classe déclare l’un de ces opérateurs sans également déclarer l’autre.A compile-time error occurs if a class declares one of these operators without also declaring the other. Les true opérateurs false et sont décrits plus en détail dans les opérateurs logiques conditionnels définis par l’utilisateur et les expressions booléennes.The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

L’exemple suivant illustre une implémentation et une utilisation ultérieure operator ++ de pour une classe Vector entière :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
    }
}

Notez comment la méthode d’opérateur retourne la valeur produite par l’ajout de 1 à l’opérande, tout comme les opérateurs d’incrémentation et de décrémentation suffixés (opérateurs d’incrémentation et de décrémentation suffixés) et les opérateurs d’incrémentation et de décrémentation de préfixe (préfixe opérateurs d’incrémentation et de décrémentation).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). Contrairement à C++, cette méthode n’a pas besoin de modifier directement la valeur de son opérande.Unlike in C++, this method need not modify the value of its operand directly. En fait, la modification de la valeur de l’opérande violerait la sémantique standard de l’opérateur d’incrémentation suffixée.In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

Opérateurs binairesBinary operators

Les règles suivantes s’appliquent aux déclarations d’opérateur T binaire, où dénote le type d’instance de la classe ou du struct qui contient la déclaration d’opérateur :The following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Un opérateur binaire sans décalage doit accepter deux paramètres, au moins un qui doit avoir le type T ou T?, et peut retourner tout type.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.
  • Un binaire << ou >> un opérateur doit accepter deux paramètres, le premier qui doit avoir le T type T? ou et le second, le type int ou int?, et peut retourner n’importe quel type.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.

La signature d’un opérateur binaire se compose du jeton d’opérateur+( -, *, /, %, &, |, ^, <<, >>,, ==, !=, ,,>ou )etlestypesdesdeux<=paramètres formels. < >=The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. Le type de retour et les noms des paramètres formels ne font pas partie de la signature d’un opérateur binaire.The return type and the names of the formal parameters are not part of a binary operator's signature.

Certains opérateurs binaires requièrent une déclaration de couple.Certain binary operators require pair-wise declaration. Pour chaque déclaration de l’un des opérateurs d’une paire, il doit y avoir une déclaration correspondante de l’autre opérateur de la paire.For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Deux déclarations d’opérateur correspondent lorsqu’elles ont le même type de retour et le même type pour chaque paramètre.Two operator declarations match when they have the same return type and the same type for each parameter. Les opérateurs suivants requièrent une déclaration de paires :The following operators require pair-wise declaration:

  • operator == et operator !=operator == and operator !=
  • operator > et operator <operator > and operator <
  • operator >= et operator <=operator >= and operator <=

Opérateurs de conversionConversion operators

Une déclaration d’opérateur de conversion introduit une conversion définie par l’utilisateur (conversions définies par l’utilisateur) qui augmente les conversions implicites et explicites prédéfinies.A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

Une déclaration d’opérateur de conversion qui implicit inclut le mot clé introduit une conversion implicite définie par l’utilisateur.A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Les conversions implicites peuvent se produire dans diverses situations, y compris les appels de membres de fonction, les expressions de Cast et les assignations.Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. Cela est décrit plus en détail dans conversions implicites.This is described further in Implicit conversions.

Une déclaration d’opérateur de conversion qui explicit inclut le mot clé introduit une conversion explicite définie par l’utilisateur.A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Les conversions explicites peuvent se produire dans les expressions de Cast et sont décrites plus en détail dans les conversions explicites.Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

Un opérateur de conversion convertit un type source, indiqué par le type de paramètre de l’opérateur de conversion, en un type cible, indiqué par le type de retour de l’opérateur de conversion.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.

Pour un S type de source et un type Tcible donnés, T si S ou sont des types S0 Nullable T0 , Let et font référence à leurs S0 types T0 sous-jacents ; sinon, et sont égal à S et T , respectivement.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. Une classe ou un struct est autorisé à déclarer une conversion d’un type S source en type T cible uniquement si toutes les conditions suivantes sont vraies :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:

  • S0et T0 sont des types différents.S0 and T0 are different types.
  • S0 OuT0 est le type de classe ou de struct dans lequel la déclaration d’opérateur a lieu.Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • Ni S0, ni T0 n’est un INTERFACE_TYPE.Neither S0 nor T0 is an interface_type.
  • En excluant les conversions définies par l’utilisateur, il n' S existe T pas de conversion Sde vers ou de T à.Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

Dans le cadre de ces règles, tous les paramètres de type S associés T à ou sont considérés comme des types uniques qui n’ont aucune relation d’héritage avec d’autres types, et toutes les contraintes sur ces paramètres de type sont ignorées.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.

Dans l’exempleIn 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
}

les deux premières déclarations d’opérateur sont autorisées, car, à des fins d' indexeurs T . 3 int et string et, respectivement, sont considérés comme des types uniques sans relation.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. Toutefois, le troisième opérateur est une erreur, C<T> car est la classe de D<T>base de.However, the third operator is an error because C<T> is the base class of D<T>.

À partir de la deuxième règle, il suit qu’un opérateur de conversion doit effectuer une conversion vers ou à partir du type de classe ou de struct dans lequel l’opérateur est déclaré.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. Par exemple, il est possible pour C un type de classe ou de struct de définir une conversion de C vers int int boolet Cde à, mais int pas de à.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.

Il n’est pas possible de redéfinir directement une conversion prédéfinie.It is not possible to directly redefine a pre-defined conversion. Ainsi, les opérateurs de conversion ne sont pas autorisés à effectuer object une conversion depuis ou vers, car les conversions implicites et explicites existent déjà entre object et tous les autres types.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. De même, ni la source ni les types cibles d’une conversion ne peuvent être un type de base de l’autre, car une conversion existera déjà.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.

Toutefois, il est possible de déclarer des opérateurs sur des types génériques qui, pour des arguments de type particuliers, spécifient des conversions qui existent déjà en tant que conversions prédéfinies.However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. Dans l’exempleIn the example

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

Quand le object type est spécifié en tant qu’argument Tde type pour, le deuxième opérateur déclare une conversion qui existe déjà (implicite et, par conséquent, une conversion explicite existe de tout type objecten type).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).

Dans les cas où une conversion prédéfinie existe entre deux types, toutes les conversions définies par l’utilisateur entre ces types sont ignorées.In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. Plus précisément :Specifically:

  • Si une conversion implicite prédéfinie (conversions implicites) existe S du type Ten type, toutes les conversions définies par l’utilisateur (implicites T ou explicites) de S à sont ignorées.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.
  • Si une conversion explicite prédéfinie (conversions explicites) existe du S type en Ttype, toutes les conversions explicites définies S par T l’utilisateur de à sont ignorées.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. ParFurthermore:

Si T est un type interface, les conversions implicites définies par S l' T utilisateur de à sont ignorées.If T is an interface type, user-defined implicit conversions from S to T are ignored.

Sinon, les conversions implicites définies par S l' T utilisateur de à sont toujours prises en compte.Otherwise, user-defined implicit conversions from S to T are still considered.

Pour tous les types object, mais, les opérateurs déclarés par le Convertible<T> type ci-dessus ne sont pas en conflit avec les conversions prédéfinies.For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. Exemple :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
}

Toutefois, pour les objectconversions de type, les conversions prédéfinies masquent les conversions définies par l’utilisateur dans tous les cas, à l’exception de :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
}

Les conversions définies par l’utilisateur ne sont pas autorisées à convertir à partir de ou en INTERFACE_TYPEs.User-defined conversions are not allowed to convert from or to interface_types. En particulier, cette restriction garantit qu’aucune transformation définie par l’utilisateur ne se produit lors de la conversion vers un INTERFACE_TYPE, et qu’une conversion vers un INTERFACE_TYPE réussit uniquement si l’objet en cours de conversion implémente réellement le INTERFACE_TYPEspécifié.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.

La signature d’un opérateur de conversion se compose du type de source et du type de cible.The signature of a conversion operator consists of the source type and the target type. (Notez qu’il s’agit de la seule forme de membre pour laquelle le type de retour participe à la signature.) La implicit classification explicit ou d’un opérateur de conversion ne fait pas partie de la signature de l’opérateur.(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. Ainsi, une classe ou un struct ne peut pas implicit déclarer à explicit la fois un opérateur de conversion et un opérateur de conversion avec les mêmes types source et cible.Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

En général, les conversions implicites définies par l’utilisateur doivent être conçues pour ne jamais lever d’exceptions et ne pas perdre d’informations.In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. Si une conversion définie par l’utilisateur peut donner lieu à des exceptions (par exemple, parce que l’argument source est hors limites) ou une perte d’informations (par exemple, en ignorant les bits de poids fort), cette conversion doit être définie comme une conversion explicite.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.

Dans l’exempleIn 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);
    }
}

la conversion de Digit en byte est implicite, car elle ne lève jamais d’exception ou ne perd pas d' byte informations, mais la Digit conversion de en Digit est explicite, car peut uniquement représenter un sous-ensemble du possible valeurs d’un 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.

Constructeurs d’instanceInstance constructors

Un constructeur d’instance est un membre qui implémente les actions requises pour initialiser une instance d’une classe.An instance constructor is a member that implements the actions required to initialize an instance of a class. Les constructeurs d’instance sont déclarés à l’aide de 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
    | ';'
    ;

Un constructor_declaration peut inclure un ensemble d' attributs (attributs), une combinaison valide des quatre modificateurs d’accès (modificateurs d’accès) et un modificateur de extern (méthodes externes).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. Une déclaration de constructeur n’est pas autorisée à inclure le même modificateur plusieurs fois.A constructor declaration is not permitted to include the same modifier multiple times.

L' identificateur d’un constructor_declarator doit nommer la classe dans laquelle le constructeur d’instance est déclaré.The identifier of a constructor_declarator must name the class in which the instance constructor is declared. Si un autre nom est spécifié, une erreur de compilation se produit.If any other name is specified, a compile-time error occurs.

Le formal_parameter_list facultatif d’un constructeur d’instance est soumis aux mêmes règles que les formal_parameter_list d’une méthode (méthodes).The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (Methods). La liste de paramètres formels définit la signature (signatures et surcharge) d’un constructeur d’instance et régit le processus par lequel la résolution de surcharge (inférence de type) sélectionne un constructeur d’instance particulier dans un appel.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.

Chacun des types référencés dans le formal_parameter_list d’un constructeur d’instance doit être au moins aussi accessible que le constructeur lui-même (contraintes d’accessibilité).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).

Le constructor_initializer facultatif spécifie un autre constructeur d’instance à appeler avant d’exécuter les instructions fournies dans le constructor_body de ce constructeur d’instance.The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. Cela est décrit plus en détail dans les initialiseurs de constructeur.This is described further in Constructor initializers.

Lorsqu’une déclaration de constructeur comprend extern un modificateur, le constructeur est considéré comme un constructeur externe.When a constructor declaration includes an extern modifier, the constructor is said to be an external constructor. Étant donné qu’une déclaration de constructeur externe ne fournit aucune implémentation réelle, son constructor_body se compose d’un point-virgule.Because an external constructor declaration provides no actual implementation, its constructor_body consists of a semicolon. Pour tous les autres constructeurs, le constructor_body se compose d’un bloc qui spécifie les instructions pour initialiser une nouvelle instance de la classe.For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. Cela correspond exactement au bloc d’une méthode d’instance avec un void type de retour (corps de méthode).This corresponds exactly to the block of an instance method with a void return type (Method body).

Les constructeurs d’instance ne sont pas hérités.Instance constructors are not inherited. Par conséquent, une classe n’a pas de constructeurs d’instance autres que ceux réellement déclarés dans la classe.Thus, a class has no instance constructors other than those actually declared in the class. Si une classe ne contient aucune déclaration de constructeur d’instance, un constructeur d’instance par défaut est fourni automatiquement (constructeurs par défaut).If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

Les constructeurs d’instance sont appelés par object_creation_expressions (expressions de création d’objet) et par le biais de constructor_initializers.Instance constructors are invoked by object_creation_expressions (Object creation expressions) and through constructor_initializers.

Initialiseurs de constructeurConstructor initializers

Tous les constructeurs d’instance (sauf ceux de la classe object) incluent implicitement un appel d’un autre constructeur d’instance immédiatement avant le constructor_body.All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. Le constructeur pour appeler implicitement est déterminé par le constructor_initializer:The constructor to implicitly invoke is determined by the constructor_initializer:

  • Un initialiseur de constructeur d’instance du base(argument_list) formulaire base() ou provoque l’appel d’un constructeur d’instance de la classe de base directe.An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. Ce constructeur est sélectionné à l’aide de argument_list s’il est présent et les règles de résolution de surcharge de la résolution de surcharge.That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. L’ensemble de constructeurs d’instance candidat se compose de tous les constructeurs d’instance accessibles contenus dans la classe de base directe, ou du constructeur par défaut (constructeurs par défaut), si aucun constructeur d’instance n’est déclaré dans la classe de base directe.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. Si cet ensemble est vide ou si un seul constructeur d’instance unique ne peut pas être identifié, une erreur de compilation se produit.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • Un initialiseur de constructeur d’instance du this(argument-list) formulaire this() ou provoque l’appel d’un constructeur d’instance de la classe elle-même.An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. Le constructeur est sélectionné à l’aide de argument_list s’il est présent et les règles de résolution de surcharge de la résolution de surcharge.The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. L’ensemble de constructeurs d’instance candidat se compose de tous les constructeurs d’instance accessibles déclarés dans la classe elle-même.The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. Si cet ensemble est vide ou si un seul constructeur d’instance unique ne peut pas être identifié, une erreur de compilation se produit.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. Si une déclaration de constructeur d’instance comprend un initialiseur de constructeur qui appelle le constructeur lui-même, une erreur de compilation se produit.If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

Si un constructeur d’instance n’a pas d’initialiseur de constructeur, un initialiseur base() de constructeur du formulaire est fourni implicitement.If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. Ainsi, une déclaration de constructeur d’instance de la formeThus, an instance constructor declaration of the form

C(...) {...}

est exactement équivalent àis exactly equivalent to

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

La portée des paramètres fournis par le formal_parameter_list d’une déclaration de constructeur d’instance comprend l’initialiseur de constructeur de cette déclaration.The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. Ainsi, un initialiseur de constructeur est autorisé à accéder aux paramètres du constructeur.Thus, a constructor initializer is permitted to access the parameters of the constructor. Exemple :For example:

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

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

Un initialiseur de constructeur d’instance ne peut pas accéder à l’instance en cours de création.An instance constructor initializer cannot access the instance being created. Par conséquent, il s’agit d’une erreur au moment de la compilation pour référencer this dans une expression d’argument de l’initialiseur de constructeur, comme c’est le cas pour une expression d’argument qui fait référence à un membre d’instance par l’intermédiaire d’un 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.

Initialiseurs de variable d’instanceInstance variable initializers

Lorsqu’un constructeur d’instance n’a pas d’initialiseur de constructeur, ou qu’il a un initialiseur de constructeur de la forme base(...), ce constructeur effectue implicitement les initialisations spécifiées par les variable_initializerdes champs d’instance déclarés dans sa classe.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. Cela correspond à une séquence d’assignations qui sont exécutées immédiatement après l’entrée dans le constructeur et avant l’appel implicite du constructeur de classe de base directe.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. Les initialiseurs de variable sont exécutés dans l’ordre textuel dans lequel ils apparaissent dans la déclaration de classe.The variable initializers are executed in the textual order in which they appear in the class declaration.

Exécution du constructeurConstructor execution

Les initialiseurs de variable sont transformés en instructions d’assignation, et ces instructions d’assignation sont exécutées avant l’appel du constructeur d’instance de la classe de base.Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. Ce classement garantit que tous les champs d’instance sont initialisés par leurs initialiseurs de variable avant l’exécution des instructions qui ont accès à cette instance.This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

À partir de l’exempleGiven 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);
    }
}

Lorsque new B() est utilisé pour créer une instance de B, la sortie suivante est produite :when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

La valeur de x est 1, car l’initialiseur de variable est exécuté avant l’appel du constructeur d’instance de la classe de base.The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. Toutefois, la valeur de y est 0 (la valeur par défaut d' intun), car l' y assignation à n’est pas exécutée tant que le constructeur de classe de base n’a pas été retourné.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.

Il est utile de considérer les initialiseurs de variable d’instance et les initialiseurs de constructeur comme des instructions qui sont insérées automatiquement avant constructor_body.It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. L’exempleThe 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;
    }
}

contient plusieurs initialiseurs de variable ; Il contient également des initialiseurs de constructeur des deuxbase formes this(et).contains several variable initializers; it also contains constructor initializers of both forms (base and this). L’exemple correspond au code indiqué ci-dessous, où chaque commentaire indique une instruction insérée automatiquement (la syntaxe utilisée pour les appels de constructeur insérés automatiquement n’est pas valide, mais sert simplement à illustrer le mécanisme).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;
    }
}

Constructeurs par défautDefault constructors

Si une classe ne contient aucune déclaration de constructeur d’instance, un constructeur d’instance par défaut est fourni automatiquement.If a class contains no instance constructor declarations, a default instance constructor is automatically provided. Ce constructeur par défaut appelle simplement le constructeur sans paramètre de la classe de base directe.That default constructor simply invokes the parameterless constructor of the direct base class. Si la classe est abstraite, l’accessibilité déclarée pour le constructeur par défaut est protégée.If the class is abstract then the declared accessibility for the default constructor is protected. Dans le cas contraire, l’accessibilité déclarée pour le constructeur par défaut est public.Otherwise, the declared accessibility for the default constructor is public. Par conséquent, le constructeur par défaut est toujours de la formeThus, the default constructor is always of the form

protected C(): base() {}

ouor

public C(): base() {}

C est le nom de la classe.where C is the name of the class. Si la résolution de surcharge ne peut pas déterminer un meilleur candidat unique pour l’initialiseur de constructeur de classe de base, une erreur de compilation se produit.If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

Dans l’exempleIn the example

class Message
{
    object sender;
    string text;
}

un constructeur par défaut est fourni, car la classe ne contient aucune déclaration de constructeur d’instance.a default constructor is provided because the class contains no instance constructor declarations. Ainsi, l’exemple est exactement équivalent àThus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

Constructeurs privésPrivate constructors

Quand une classe T déclare uniquement des constructeurs d’instance privés, il n’est pas possible que les classes situées en dehors T du texte de T programme de soient dérivées de Tou de pour créer directement des instances de.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. Ainsi, si une classe contient uniquement des membres statiques et n’est pas destinée à être instanciée, l’ajout d’un constructeur d’instance privée vide empêchera l’instanciation.Thus, if a class contains only static members and isn't intended to be instantiated, adding an empty private instance constructor will prevent instantiation. Exemple :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) {...}
}

La Trig classe regroupe des constantes et des méthodes connexes, mais n’est pas destinée à être instanciée.The Trig class groups related methods and constants, but is not intended to be instantiated. Par conséquent, il déclare un seul constructeur d’instance privée vide.Therefore it declares a single empty private instance constructor. Au moins un constructeur d’instance doit être déclaré pour supprimer la génération automatique d’un constructeur par défaut.At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

Paramètres facultatifs du constructeur d’instanceOptional instance constructor parameters

La this(...) forme de l’initialiseur de constructeur est couramment utilisée conjointement avec la surcharge pour implémenter des paramètres de constructeur d’instance facultatifs.The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. Dans l’exempleIn 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
    }
}

les deux premiers constructeurs d’instance fournissent simplement les valeurs par défaut pour les arguments manquants.the first two instance constructors merely provide the default values for the missing arguments. Les deux utilisent this(...) un initialiseur de constructeur pour appeler le troisième constructeur d’instance, qui effectue en fait le travail d’initialisation de la nouvelle instance.Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. L’effet est celui des paramètres de constructeur facultatifs :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");

Constructeurs statiquesStatic constructors

Un constructeur statique est un membre qui implémente les actions requises pour initialiser un type de classe fermé.A static constructor is a member that implements the actions required to initialize a closed class type. Les constructeurs statiques sont déclarés à l’aide de 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
    | ';'
    ;

Un static_constructor_declaration peut inclure un ensemble d' attributs (attributs) et un modificateur extern (méthodes externes).A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

L' identificateur d’un static_constructor_declaration doit nommer la classe dans laquelle le constructeur statique est déclaré.The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. Si un autre nom est spécifié, une erreur de compilation se produit.If any other name is specified, a compile-time error occurs.

Lorsqu’une déclaration de constructeur statique comprend extern un modificateur, le constructeur statique est dit comme un constructeur statique externe.When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Étant donné qu’une déclaration de constructeur statique externe ne fournit aucune implémentation réelle, son static_constructor_body se compose d’un point-virgule.Because an external static constructor declaration provides no actual implementation, its static_constructor_body consists of a semicolon. Pour toutes les autres déclarations de constructeur statiques, le static_constructor_body se compose d’un bloc qui spécifie les instructions à exécuter pour initialiser la classe.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. Cela correspond exactement au method_body d’une méthode statique avec un type de retour void (corps de méthode).This corresponds exactly to the method_body of a static method with a void return type (Method body).

Les constructeurs statiques ne sont pas hérités et ne peuvent pas être appelés directement.Static constructors are not inherited, and cannot be called directly.

Le constructeur statique d’un type de classe fermé s’exécute au plus une fois dans un domaine d’application donné.The static constructor for a closed class type executes at most once in a given application domain. L’exécution d’un constructeur statique est déclenchée par le premier des événements suivants pour se produire au sein d’un domaine d’application :The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • Une instance du type de classe est créée.An instance of the class type is created.
  • Tous les membres statiques du type de classe sont référencés.Any of the static members of the class type are referenced.

Si une classe contient la Main méthode (démarragede l’application) dans laquelle l’exécution commence, le constructeur statique de cette classe s' Main exécute avant l’appel de la méthode.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.

Pour initialiser un nouveau type de classe fermé, tout d’abord un nouvel ensemble de champs statiques (champs d’instance et statiques) pour ce type fermé particulier est créé.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. Chacun des champs statiques est initialisé à sa valeur par défaut (valeurs par défaut).Each of the static fields is initialized to its default value (Default values). Ensuite, les initialiseurs de champ statique (initialisation de champ statique) sont exécutés pour ces champs statiques.Next, the static field initializers (Static field initialization) are executed for those static fields. Enfin, le constructeur statique est exécuté.Finally, the static constructor is executed.

L’exempleThe 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");
    }
}

doit produire la sortie :must produce the output:

Init A
A.F
Init B
B.F

étant donné que l' Aexécution du constructeur statique de est déclenchée par l' A.Fappel à, et que Bl’exécution du constructeur statique de est déclenchée par B.Fl’appel à.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.

Il est possible de construire des dépendances circulaires qui autorisent le respect des champs statiques avec des initialiseurs de variable dans leur état de valeur par défaut.It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

L’exempleThe 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);
    }
}

génère la sortieproduces the output

X = 1, Y = 2

Pour exécuter la Main méthode, le système exécute d’abord l’initialiseur B.Ypour, avant le Bconstructeur statique de la classe.To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Yl’initialiseur de Aentraîne l’exécution du constructeur statique de, car la A.X valeur de est référencée.Y's initializer causes A's static constructor to be run because the value of A.X is referenced. Le constructeur statique de A à son tour poursuit le calcul de la valeur Xde, ce qui permet d’extraire la valeur par défaut Yde, qui est égale à zéro.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.Xest donc initialisé à 1.A.X is thus initialized to 1. Le processus d’exécution Ades initialiseurs de champ statiques et du constructeur statique se termine alors, en retournant au calcul Yde la valeur initiale de, dont le résultat devient 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.

Étant donné que le constructeur statique est exécuté une seule fois pour chaque type de classe construite fermée, il s’agit d’un emplacement pratique pour appliquer des contrôles au moment de l’exécution sur le paramètre de type qui ne peuvent pas être vérifiés au moment de la compilation via des contraintes (contraintes de paramètre de type) .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). Par exemple, le type suivant utilise un constructeur statique pour appliquer que l’argument de type est une énumération :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");
        }
    }
}

DestructeursDestructors

Un destructeur est un membre qui implémente les actions requises pour détruire une instance d’une classe.A destructor is a member that implements the actions required to destruct an instance of a class. Un destructeur est déclaré à l’aide d’un destructor_declaration:A destructor is declared using a destructor_declaration:

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

destructor_body
    : block
    | ';'
    ;

Un destructor_declaration peut inclure un ensemble d' attributs (attributs).A destructor_declaration may include a set of attributes (Attributes).

L' identificateur d’un destructor_declaration doit nommer la classe dans laquelle le destructeur est déclaré.The identifier of a destructor_declaration must name the class in which the destructor is declared. Si un autre nom est spécifié, une erreur de compilation se produit.If any other name is specified, a compile-time error occurs.

Lorsqu’une déclaration de destructeur comprend un extern modificateur, le destructeur est considéré comme un destructeur externe.When a destructor declaration includes an extern modifier, the destructor is said to be an external destructor. Étant donné qu’une déclaration de destructeur externe ne fournit aucune implémentation réelle, son destructor_body se compose d’un point-virgule.Because an external destructor declaration provides no actual implementation, its destructor_body consists of a semicolon. Pour tous les autres destructeurs, le destructor_body se compose d’un bloc qui spécifie les instructions à exécuter afin de détruire une instance de la classe.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. Un destructor_body correspond exactement au method_body d’une méthode d’instance avec un type de retour void (corps de méthode).A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

Les destructeurs ne sont pas hérités.Destructors are not inherited. Par conséquent, une classe n’a pas de destructeurs autres que celui qui peut être déclaré dans cette classe.Thus, a class has no destructors other than the one which may be declared in that class.

Étant donné qu’un destructeur ne doit pas avoir de paramètres, il ne peut pas être surchargé, donc une classe peut avoir, au plus, un destructeur.Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

Les destructeurs sont appelés automatiquement et ne peuvent pas être appelés explicitement.Destructors are invoked automatically, and cannot be invoked explicitly. Une instance devient éligible pour la destruction lorsqu’il n’est plus possible pour un code d’utiliser cette instance.An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. L’exécution du destructeur pour l’instance peut se produire à tout moment une fois que l’instance est éligible pour la destruction.Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. Lorsqu’une instance est détruite, les destructeurs dans la chaîne d’héritage de cette instance sont appelés, dans l’ordre, de la plus dérivée à la moins dérivée.When an instance is destructed, the destructors in that instance's inheritance chain are called, in order, from most derived to least derived. Un destructeur peut être exécuté sur n’importe quel thread.A destructor may be executed on any thread. Pour plus d’informations sur les règles qui régissent le moment et le mode d’exécution d’un destructeur, consultez gestion automatiquede la mémoire.For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

La sortie de l’exempleThe 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();
   }
}

estis

B's destructor
A's destructor

étant donné que les destructeurs dans une chaîne d’héritage sont appelés dans l’ordre, de la plus dérivée à la moins dérivée.since destructors in an inheritance chain are called in order, from most derived to least derived.

Les destructeurs sont implémentés en substituant la Finalize méthode System.Objectvirtuelle sur.Destructors are implemented by overriding the virtual method Finalize on System.Object. C#les programmes ne sont pas autorisés à substituer cette méthode ou à les appeler (ou à les remplacer) directement.C# programs are not permitted to override this method or call it (or overrides of it) directly. Par exemple, le programmeFor instance, the program

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

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

contient deux erreurs.contains two errors.

Le compilateur se comporte comme si cette méthode, et ses substitutions, n’existent pas du tout.The compiler behaves as if this method, and overrides of it, do not exist at all. Ainsi, ce programme :Thus, this program:

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

est valide, et la méthode affichée masque System.Objectla méthode de. Finalizeis valid, and the method shown hides System.Object's Finalize method.

Pour une description du comportement lorsqu’une exception est levée à partir d’un destructeur, consultez la rubrique Comment les exceptions sont gérées.For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

IteratorsIterators

Une fonction membre (fonction members) implémentée à l’aide d’un bloc itérateur (blocs) est appelée un itérateur.A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

Un bloc itérateur peut être utilisé comme corps d’un membre de fonction tant que le type de retour de la fonction membre correspondante est l’une des interfaces d’énumérateur (interfaces d’énumérateur) ou l’une des interfaces énumérables (interfaces énumérables) .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). Elle peut se produire sous forme de method_body, operator_body ou accessor_body, alors que les événements, les constructeurs d’instance, les constructeurs statiques et les destructeurs ne peuvent pas être implémentés en tant qu’itérateurs.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.

Lorsqu’un membre de fonction est implémenté à l’aide d’un bloc itérateur, il s’agit d’une erreur de compilation pour la liste de paramètres formels ref de out la fonction membre qui spécifie les paramètres ou.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.

Interfaces d’énumérateurEnumerator interfaces

Les interfaces d’énumérateur sont l’interface System.Collections.IEnumerator non générique et toutes les instanciations de l’interface System.Collections.Generic.IEnumerator<T>générique.The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. Par souci de concision, dans ce chapitre, ces interfaces sont référencées en IEnumerator tant IEnumerator<T>que et, respectivement.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

Interfaces énumérablesEnumerable interfaces

Les interfaces énumérables sont l’interface System.Collections.IEnumerable non générique et toutes les instanciations de l' System.Collections.Generic.IEnumerable<T>interface générique.The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. Par souci de concision, dans ce chapitre, ces interfaces sont référencées en IEnumerable tant IEnumerable<T>que et, respectivement.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Type yieldYield type

Un itérateur produit une séquence de valeurs, toutes du même type.An iterator produces a sequence of values, all of the same type. Ce type est appelé le type yield de l’itérateur.This type is called the yield type of the iterator.

  • Type yield d’un itérateur qui retourne IEnumerator ou IEnumerable est object.The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • Type yield d’un itérateur qui retourne IEnumerator<T> ou IEnumerable<T> est T.The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

Objets EnumeratorEnumerator objects

Lorsqu’un membre de fonction qui retourne un type d’interface énumérateur est implémenté à l’aide d’un bloc itérateur, l’appel du membre de fonction n’exécute pas immédiatement le code dans le bloc itérateur.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. Au lieu de cela, un objet énumérateur est créé et retourné.Instead, an enumerator object is created and returned. Cet objet encapsule le code spécifié dans le bloc Iterator, et l’exécution du code dans le bloc Iterator se produit lorsque la méthode de MoveNext l’objet énumérateur est appelée.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. Un objet énumérateur a les caractéristiques suivantes :An enumerator object has the following characteristics:

  • Il implémente IEnumerator et IEnumerator<T>, où T est le type de rendement de l’itérateur.It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • Elle implémente System.IDisposable.It implements System.IDisposable.
  • Elle est initialisée à l’aide d’une copie des valeurs d’argument (le cas échéant) et de la valeur d’instance passée à la fonction membre.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • Il a quatre États potentiels, avant, en cours d’exécution, suspenduet après, et est initialement dans l’état avant .It has four potential states, before, running, suspended, and after, and is initially in the before state.

Un objet énumérateur est généralement une instance d’une classe d’énumérateur générée par le compilateur qui encapsule le code dans le bloc itérateur et implémente les interfaces d’énumérateur, mais d’autres méthodes d’implémentation sont possibles.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. Si une classe d’énumération est générée par le compilateur, cette classe sera imbriquée, directement ou indirectement, dans la classe contenant la fonction membre, elle aura une accessibilité privée et un nom est réservé à l’utilisation du compilateur (identificateurs).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).

Un objet énumérateur peut implémenter plus d’interfaces que celles spécifiées ci-dessus.An enumerator object may implement more interfaces than those specified above.

Les sections suivantes décrivent le comportement exact des MoveNextmembres Current, et Dispose des implémentations IEnumerable d' IEnumerable<T> interface et fournies par un objet énumérateur.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.

Notez que les objets Enumerator ne prennent pas IEnumerator.Reset en charge la méthode.Note that enumerator objects do not support the IEnumerator.Reset method. L’appel de cette méthode entraîne System.NotSupportedException la levée d’une exception.Invoking this method causes a System.NotSupportedException to be thrown.

Méthode MoveNextThe MoveNext method

La MoveNext méthode d’un objet énumérateur encapsule le code d’un bloc itérateur.The MoveNext method of an enumerator object encapsulates the code of an iterator block. L’appel de MoveNext la méthode exécute le code dans le bloc Iterator et définit Current la propriété de l’objet énumérateur comme il convient.Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. L’action exacte effectuée par MoveNext dépend de l’état de l’objet énumérateur lorsque MoveNext est appelé :The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • Si l’état de l’objet énumérateur est antérieur, appelez MoveNext:If the state of the enumerator object is before, invoking MoveNext:
    • Modifie l’État en en cours d’exécution.Changes the state to running.
    • Initialise les paramètres (y compris this) du bloc itérateur aux valeurs d’argument et à la valeur d’instance enregistrées lorsque l’objet énumérateur a été initialisé.Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • Exécute le bloc itérateur à partir du début jusqu’à ce que l’exécution soit interrompue (comme décrit ci-dessous).Executes the iterator block from the beginning until execution is interrupted (as described below).
  • Si l’état de l’objet énumérateur est en cours d’exécution, le résultat MoveNext de l’appel de n’est pas spécifié.If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • Si l’état de l’objet énumérateur est suspendu, appelez MoveNext:If the state of the enumerator object is suspended, invoking MoveNext:
    • Modifie l’État en en cours d’exécution.Changes the state to running.
    • Restaure les valeurs de tous les paramètres et variables locales (y compris celui-ci) aux valeurs enregistrées lors de la dernière suspension de l’exécution du bloc itérateur.Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Notez que le contenu de tous les objets référencés par ces variables peut avoir changé depuis l’appel précédent à MoveNext.Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • Reprend l’exécution du bloc itérateur immédiatement après l' yield return instruction qui a provoqué la suspension de l’exécution et continue jusqu’à ce que l’exécution soit interrompue (comme décrit ci-dessous).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).
  • Si l’état de l’objet énumérateur est après, l’appel MoveNext de falseretourne.If the state of the enumerator object is after, invoking MoveNext returns false.

Lorsque MoveNext exécute le bloc Iterator, l’exécution peut être interrompue de quatre façons : Par une yield return instruction, par une yield break instruction, en rencontrant la fin du bloc itérateur, et par une exception levée et propagée en dehors du bloc itérateur.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.

  • Quand une yield return instruction est rencontrée (l’instruction yield) :When a yield return statement is encountered (The yield statement):
    • L’expression donnée dans l’instruction est évaluée, implicitement convertie en type yield et assignée à la Current propriété de l’objet énumérateur.The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • L’exécution du corps de l’itérateur est suspendue.Execution of the iterator body is suspended. Les valeurs de tous les paramètres et variables locales ( thisy compris) sont enregistrées, comme c’est l' yield return emplacement de cette instruction.The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. Si l' yield return instruction se trouve dans un ou try plusieurs blocs, les finally blocs associés ne sont pas exécutés pour l’instant.If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • L’état de l’objet énumérateur est modifié en suspendu.The state of the enumerator object is changed to suspended.
    • La MoveNext méthode retourne true à son appelant, ce qui indique que l’itération a réussi à avancer jusqu’à la valeur suivante.The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • Quand une yield break instruction est rencontrée (l’instruction yield) :When a yield break statement is encountered (The yield statement):
    • Si l' yield break instruction se trouve dans un ou try plusieurs blocs, les finally blocs associés sont exécutés.If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • L’état de l’objet énumérateur est remplacé par après.The state of the enumerator object is changed to after.
    • La MoveNext méthode retourne false à son appelant, ce qui indique que l’itération est terminée.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Quand la fin du corps de l’itérateur est rencontrée :When the end of the iterator body is encountered:
    • L’état de l’objet énumérateur est remplacé par après.The state of the enumerator object is changed to after.
    • La MoveNext méthode retourne false à son appelant, ce qui indique que l’itération est terminée.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Lorsqu’une exception est levée et propagée en dehors du bloc Iterator :When an exception is thrown and propagated out of the iterator block:
    • Les finally blocs appropriés dans le corps de l’itérateur ont été exécutés par la propagation de l’exception.Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • L’état de l’objet énumérateur est remplacé par après.The state of the enumerator object is changed to after.
    • La propagation de l’exception continue à l’appelant de MoveNext la méthode.The exception propagation continues to the caller of the MoveNext method.

La propriété actuelleThe Current property

La propriété d' Current un objet Enumerator est affectée yield return par les instructions du bloc Iterator.An enumerator object's Current property is affected by yield return statements in the iterator block.

Lorsqu’un objet énumérateur est dans l’état suspendu , la valeur de Current est la valeur définie par l’appel précédent à MoveNext.When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. Lorsqu’un objet énumérateur se trouve dans les États Before, Runningou after , Current le résultat de l’accès n’est pas spécifié.When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified.

Pour un itérateur avec un type yield autre que object, le résultat de l' Current accès par le biais de l' IEnumerable implémentation de l’objet énumérateur Current correspond IEnumerator<T> à l’accès par le biais de l’objet énumérateur implémenter et effectuer un cast objectdu résultat vers.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.

Méthode disposeThe Dispose method

La Dispose méthode est utilisée pour nettoyer l’itération en plaçant l’objet énumérateur à l’état after .The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • Si l’état de l’objet énumérateur est antérieur, l’appel Dispose de passe l’État à après.If the state of the enumerator object is before, invoking Dispose changes the state to after.
  • Si l’état de l’objet énumérateur est en cours d’exécution, le résultat Dispose de l’appel de n’est pas spécifié.If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • Si l’état de l’objet énumérateur est suspendu, appelez Dispose:If the state of the enumerator object is suspended, invoking Dispose:
    • Modifie l’État en en cours d’exécution.Changes the state to running.
    • Exécute tous les blocs finally comme si la dernière yield return instruction exécutée yield break était une instruction.Executes any finally blocks as if the last executed yield return statement were a yield break statement. Si cela entraîne la levée d’une exception et sa propagation en dehors du corps de l’itérateur, l’état de l’objet énumérateur est défini sur après et l’exception est propagée à l’appelant Dispose de la méthode.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.
    • Passe l’État à après.Changes the state to after.
  • Si l’état de l’objet énumérateur est après, l’appel Dispose de n’a aucun effet.If the state of the enumerator object is after, invoking Dispose has no affect.

Objets énumérablesEnumerable objects

Lorsqu’un membre de fonction qui retourne un type d’interface énumérable est implémenté à l’aide d’un bloc itérateur, l’appel de la fonction member n’exécute pas immédiatement le code dans le bloc itérateur.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. Au lieu de cela, un objet énumérable est créé et retourné.Instead, an enumerable object is created and returned. La méthode de GetEnumerator l’objet énumérable retourne un objet énumérateur qui encapsule le code spécifié dans le bloc Iterator, et l’exécution du code dans le bloc Iterator se produit lorsque la MoveNext méthode de l’objet énumérateur est appelée.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. Un objet énumérable présente les caractéristiques suivantes :An enumerable object has the following characteristics:

  • Il implémente IEnumerable et IEnumerable<T>, où T est le type de rendement de l’itérateur.It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • Elle est initialisée à l’aide d’une copie des valeurs d’argument (le cas échéant) et de la valeur d’instance passée à la fonction membre.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

Un objet énumérable est généralement une instance d’une classe énumérable générée par le compilateur qui encapsule le code dans le bloc itérateur et implémente les interfaces énumérables, mais d’autres méthodes d’implémentation sont possibles.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. Si une classe énumérable est générée par le compilateur, cette classe sera imbriquée, directement ou indirectement, dans la classe contenant la fonction membre, elle aura une accessibilité privée et un nom est réservé à l’utilisation du compilateur (identificateurs).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).

Un objet énumérable peut implémenter plus d’interfaces que celles spécifiées ci-dessus.An enumerable object may implement more interfaces than those specified above. En particulier, un objet énumérable peut également IEnumerator implémenter et IEnumerator<T>, ce qui lui permet de servir à la fois un énumérable et un énumérateur.In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. Dans ce type d’implémentation, la première fois qu’une méthode d' GetEnumerator un objet énumérable est appelée, l’objet énumérable lui-même est retourné.In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. Les appels suivants de l’objet GetEnumeratorénumérable, le cas échéant, retournent une copie de l’objet énumérable.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Ainsi, chaque énumérateur retourné a son propre État et les modifications apportées dans un énumérateur n’en affectent pas une autre.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

Méthode GetEnumeratorThe GetEnumerator method

Un objet énumérable fournit une implémentation des GetEnumerator méthodes IEnumerable des interfaces et IEnumerable<T> .An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. Les deux GetEnumerator méthodes partagent une implémentation commune qui acquiert et retourne un objet énumérateur disponible.The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. L’objet énumérateur est initialisé avec les valeurs d’argument et la valeur d’instance enregistrées lorsque l’objet énumérable a été initialisé, mais dans le cas contraire, l’objet énumérateur fonctionne comme décrit dans objets 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.

Exemple d’implémentationImplementation example

Cette section décrit une implémentation possible d’itérateurs en termes de constructions C# standard.This section describes a possible implementation of iterators in terms of standard C# constructs. L’implémentation décrite ici est basée sur les principes utilisés par le compilateur Microsoft C# , mais il ne s’agit pas d’une implémentation obligatoire ou du seul possible.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.

La classe Stack<T> suivante implémente sa GetEnumerator méthode à l’aide d’un itérateur.The following Stack<T> class implements its GetEnumerator method using an iterator. L’itérateur énumère les éléments de la pile dans l’ordre de haut en bas.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];
    }
}

La GetEnumerator méthode peut être traduite en une instanciation d’une classe d’énumérateur générée par le compilateur qui encapsule le code dans le bloc itérateur, comme indiqué dans l’exemple suivant.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();
        }
    }
}

Dans la traduction précédente, le code du bloc itérateur est converti en ordinateur d’État et placé dans la MoveNext méthode de la classe 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. En outre, la variable i locale est transformée en champ dans l’objet énumérateur afin de pouvoir continuer à exister entre les appels MoveNextde.Furthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext.

L’exemple suivant imprime une table de multiplication simple des entiers 1 à 10.The following example prints a simple multiplication table of the integers 1 through 10. La FromTo méthode de l’exemple retourne un objet énumérable et est implémentée à l’aide d’un itérateur.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();
        }
    }
}

La FromTo méthode peut être traduite en une instanciation d’une classe énumérable générée par le compilateur qui encapsule le code dans le bloc itérateur, comme indiqué dans l’exemple suivant.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();
        }
    }
}

La classe Enumerable implémente à la fois les interfaces Enumerable et les interfaces d’énumérateur, ce qui lui permet de servir à la fois un énumérable et un énumérateur.The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. La première fois que GetEnumerator la méthode est appelée, l’objet énumérable lui-même est retourné.The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Les appels suivants de l’objet GetEnumeratorénumérable, le cas échéant, retournent une copie de l’objet énumérable.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Ainsi, chaque énumérateur retourné a son propre État et les modifications apportées dans un énumérateur n’en affectent pas une autre.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. La Interlocked.CompareExchange méthode est utilisée pour garantir l’opération thread-safe.The Interlocked.CompareExchange method is used to ensure thread-safe operation.

Les from paramètres to et sont convertis en champs dans la classe Enumerable.The from and to parameters are turned into fields in the enumerable class. Étant from donné que est modifié dans le bloc Iterator, __from un champ supplémentaire est introduit pour contenir la valeur initiale from donnée à dans chaque énumérateur.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.

La MoveNext méthode lève une InvalidOperationException si elle est appelée lorsque __state est 0.The MoveNext method throws an InvalidOperationException if it is called when __state is 0. Cela permet de protéger l’utilisation de l’objet énumérable en tant qu’objet GetEnumeratorénumérateur sans appeler au préalable.This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

L’exemple suivant montre une classe d’arborescence simple.The following example shows a simple tree class. La Tree<T> classe implémente sa GetEnumerator méthode à l’aide d’un itérateur.The Tree<T> class implements its GetEnumerator method using an iterator. L’itérateur énumère les éléments de l’arborescence dans l’ordre des infixes.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();
    }
}

La GetEnumerator méthode peut être traduite en une instanciation d’une classe d’énumérateur générée par le compilateur qui encapsule le code dans le bloc itérateur, comme indiqué dans l’exemple suivant.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();
        }
    }
}

Les compilateurs temporaires générés par le foreach compilateur utilisés dans les instructions sont __left levés dans les champs et __right de l’objet énumérateur.The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. Le __state champ de l’objet énumérateur est mis à jour avec soin afin Dispose() que la méthode correcte soit appelée correctement si une exception est levée.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. Notez qu’il n’est pas possible d’écrire le code traduit avec foreach des instructions simples.Note that it is not possible to write the translated code with simple foreach statements.

Fonctions AsyncAsync functions

Une méthode (méthodes) ou une fonction anonyme (expressions de fonction anonymes) avec le async modificateur est appelée fonction Async.A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an async function. En général, le terme Async est utilisé pour décrire tout type de fonction qui a le async modificateur.In general, the term async is used to describe any kind of function that has the async modifier.

Il s’agit d’une erreur de compilation pour la liste de paramètres formels d’une fonction Async ref pour out spécifier des paramètres ou.It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

Le type_retour d’une méthode Async doit être void ou un type de tâche.The return_type of an async method must be either void or a task type. Les types de tâches System.Threading.Tasks.Task sont et les types System.Threading.Tasks.Task<T>construits à partir de.The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. Par souci de concision, dans ce chapitre, ces types sont référencés en Task tant Task<T>que et, respectivement.For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. Une méthode Async qui retourne un type de tâche est dite retournant des tâches.An async method returning a task type is said to be task-returning.

La définition exacte des types de tâches est définie par l’implémentation, mais du point de vue du langage, un type de tâche se trouve dans l’un des États incomplet, SUCCEEDED ou Faulted.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. Une tâche ayant généré une erreur enregistre une exception pertinente.A faulted task records a pertinent exception. Une opération Task<T> réussie enregistre un résultat de Ttype.A succeeded Task<T> records a result of type T. Les types de tâches sont await, et peuvent par conséquent être les opérandes des expressions await (expression await).Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

Un appel de fonction Async a la possibilité de suspendre l’évaluation à l’aide d’expressions await (expression await) dans son corps.An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. L’évaluation peut être reprise ultérieurement au moment de l’interruption de l’expression await au moyen d’un délégué de reprise.Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. Le délégué de reprise est de System.Actiontype, et lorsqu’il est appelé, l’évaluation de l’appel de la fonction Async reprend à partir de l’expression await là où elle s’est arrêtée.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. L' appelant actuel d’un appel de fonction Async est l’appelant d’origine si l’appel de fonction n’a jamais été interrompu ou l’appelant le plus récent du délégué de reprise dans le cas contraire.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.

Évaluation d’une fonction asynchrone retournant des tâchesEvaluation of a task-returning async function

L’appel d’une fonction Async retournant des tâches provoque la génération d’une instance du type de tâche retourné.Invocation of a task-returning async function causes an instance of the returned task type to be generated. C’est ce que l’on appelle la tâche de retour de la fonction Async.This is called the return task of the async function. L’état initial de la tâche est incomplet.The task is initially in an incomplete state.

Le corps de la fonction Async est ensuite évalué jusqu’à ce qu’il soit suspendu (en atteignant une expression await) ou se termine, auquel point le contrôle est retourné à l’appelant, ainsi que la tâche de retour.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.

Lorsque le corps de la fonction Async se termine, la tâche de retour est déplacée de l’état incomplet :When the body of the async function terminates, the return task is moved out of the incomplete state:

  • Si le corps de la fonction se termine suite à l’atteinte d’une instruction return ou à la fin du corps, toute valeur de résultat est enregistrée dans la tâche de retour, qui est placée dans un état de réussite.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.
  • Si le corps de la fonction se termine à la suite d’une exception non interceptée (instruction throw), l’exception est enregistrée dans la tâche de retour qui est placée dans un état d’erreur.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.

Évaluation d’une fonction Async qui retourne voidEvaluation of a void-returning async function

Si le type de retour de la fonction Async voidest, l’évaluation diffère de celle ci-dessus de la manière suivante : Étant donné qu’aucune tâche n’est retournée, la fonction communique à la place l’achèvement et les exceptions au contexte de synchronisationdu thread actuel.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. La définition exacte du contexte de synchronisation dépend de l’implémentation, mais est une représentation de « où » le thread actuel est en cours d’exécution.The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. Le contexte de synchronisation est notifié lorsque l’évaluation d’une fonction Async qui retourne une valeur void commence, se termine correctement ou provoque la levée d’une exception non interceptée.The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

Cela permet au contexte d’effectuer le suivi du nombre de fonctions Async à retour void qui s’exécutent sous celui-ci et de décider comment propager les exceptions en provenance de ces dernières.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.