Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
23.1 Général
Une grande partie du langage C# permet au programmeur de spécifier des informations déclaratives sur les entités définies dans le programme. Par exemple, l’accessibilité d’une méthode dans une classe est spécifiée en la décorant avec les method_modifiers public, protected, internalet private.
C# permet aux programmeurs d’inventer de nouveaux types d’informations déclaratives , appeléesattributs. Les programmeurs peuvent ensuite attacher des attributs à différentes entités de programme et récupérer des informations d’attribut dans un environnement d’exécution.
Remarque : Par exemple, une infrastructure peut définir un
HelpAttributeattribut qui peut être placé sur certains éléments de programme (tels que des classes et des méthodes) pour fournir un mappage de ces éléments de programme à leur documentation. Note de fin
Les attributs sont définis par le biais de la déclaration de classes d’attributs (§23.2), qui peuvent avoir des paramètres positionnels et nommés (§23.2.3). Les attributs sont attachés à des entités dans un programme C# à l’aide de spécifications d’attributs (§23.3) et peuvent être récupérés au moment de l’exécution en tant qu’instances d’attribut (§23.4).
23.2 Classes d’attributs
23.2.1 Général
Une classe qui dérive de la classe System.Attributeabstraite , directement ou indirectement, est une classe d’attributs. La déclaration d’une classe d’attribut définit un nouveau type d’attribut qui peut être placé sur des entités de programme. Par convention, les classes d’attributs sont nommées avec un suffixe de Attribute. Les utilisations d’un attribut peuvent inclure ou omettre ce suffixe.
Une déclaration de classe générique ne doit pas être utilisée System.Attribute comme classe de base directe ou indirecte.
Exemple :
public class B : Attribute {} public class C<T> : B {} // Error – generic cannot be an attributeexemple de fin
23.2.2 Utilisation des attributs
L’attribut AttributeUsage (§23.5.2) est utilisé pour décrire comment une classe d’attributs peut être utilisée.
AttributeUsage a un paramètre positionnel (§23.2.3) qui permet à une classe d’attributs de spécifier les types d’entités de programme sur lesquelles il peut être utilisé.
Exemple : L’exemple suivant définit une classe d’attribut nommée
SimpleAttributequi peut être placée sur class_declarations et interface_declarations uniquement, et affiche plusieurs utilisations de l’attributSimple.[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class SimpleAttribute : Attribute { ... } [Simple] class Class1 {...} [Simple] interface Interface1 {...}Bien que cet attribut soit défini avec le nom
SimpleAttribute, lorsque cet attribut est utilisé, leAttributesuffixe peut être omis, ce qui entraîne le nomSimplecourt. Par conséquent, l’exemple ci-dessus équivaut sémantiquement à ce qui suit :[SimpleAttribute] class Class1 {...} [SimpleAttribute] interface Interface1 {...}exemple de fin
AttributeUsage a un paramètre nommé (§23.2.3), appelé AllowMultiple, qui indique si l’attribut peut être spécifié plusieurs fois pour une entité donnée. Si AllowMultiple pour une classe d’attribut est true, cette classe d’attribut est une classe d’attributs multi-utilisation et peut être spécifiée plusieurs fois sur une entité. Si AllowMultiple pour une classe d’attribut est false ou qu’elle n’est pas spécifiée, cette classe d’attribut est une classe d’attribut à usage unique et peut être spécifiée au maximum une fois sur une entité.
Exemple : L’exemple suivant définit une classe d’attributs multi-utilisation nommée
AuthorAttributeet affiche une déclaration de classe avec deux utilisations de l’attributAuthor:[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class AuthorAttribute : Attribute { public string Name { get; } public AuthorAttribute(string name) => Name = name; } [Author("Brian Kernighan"), Author("Dennis Ritchie")] class Class1 { ... }exemple de fin
AttributeUsage a un autre paramètre nommé (§23.2.3), appelé Inherited, qui indique si l’attribut, lorsqu’il est spécifié sur une classe de base, est également hérité par les classes qui dérivent de cette classe de base. Si Inherited pour une classe d’attribut est true, cet attribut est hérité. Si Inherited pour une classe d’attribut est false, cet attribut n’est pas hérité. S’il n’est pas spécifié, sa valeur par défaut est true.
Une classe X d’attributs n’ayant pas d’attribut AttributeUsage attaché à celui-ci, comme dans
class X : Attribute { ... }
équivaut à ce qui suit :
[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false,
Inherited = true)
]
class X : Attribute { ... }
23.2.3 Paramètres positionnels et nommés
Les classes d’attributs peuvent avoir des paramètrespositionnels et des paramètresnommés. Chaque constructeur d’instance publique pour une classe d’attribut définit une séquence valide de paramètres positionnels pour cette classe d’attributs. Chaque champ et propriété en lecture-écriture publique non statique pour une classe d’attribut définit un paramètre nommé pour la classe d’attributs. Pour qu’une propriété définisse un paramètre nommé, cette propriété doit avoir à la fois un accesseur get public et un accesseur de jeu public.
Exemple : L’exemple suivant définit une classe d’attributs nommée
HelpAttributequi a un paramètre positionnel,urlet un paramètre nommé,Topic. Bien qu’elle ne soit pas statique et publique, la propriétéUrlne définit pas de paramètre nommé, car elle n’est pas en lecture-écriture. Deux utilisations de cet attribut sont également affichées :[AttributeUsage(AttributeTargets.Class)] public class HelpAttribute : Attribute { public HelpAttribute(string url) // url is a positional parameter { ... } // Topic is a named parameter public string Topic { get; set; } public string Url { get; } } [Help("http://www.mycompany.com/xxx/Class1.htm")] class Class1 { } [Help("http://www.mycompany.com/xxx/Misc.htm", Topic ="Class2")] class Class2 { }exemple de fin
23.2.4 Types de paramètres d’attribut
Les types de paramètres positionnels et nommés d’une classe d’attribut sont limités aux types de paramètres d’attribut, qui sont les suivants :
- L’un des types suivants :
bool, ,byte,char.doublefloatintlongsbyteshortstringuintulongushort - Le type
object. - Le type
System.Type. - Les types enum.
- Tableaux unidimensionnels des types ci-dessus.
- Un argument de constructeur ou un champ public qui n’a pas l’un de ces types ne doit pas être utilisé comme paramètre positionnel ou nommé dans une spécification d’attribut.
23.3 Spécification d’attribut
L’application d’un attribut précédemment défini à une entité de programme est appelée spécification d’attribut. Un attribut est une partie d’informations déclaratives supplémentaires spécifiées pour une entité de programme. Les attributs peuvent être spécifiés à l’étendue globale (pour spécifier des attributs sur l’assembly ou module conteneur) et pour les type_declarations (§14.7), les class_member_declaration(§15.3), les interface_member_declaration(§19). .4), struct_member_declarations (§16.3), enum_member_declarations (§20.2), accessor_declaration(§15.7.3), event_accessor_declarations (§15.8)), éléments des parameter_list(§15.6.2) et des éléments de type_parameter_lists (§15.2.3).
Les attributs sont spécifiés dans les sections d’attributs. Une section d’attribut se compose d’une paire de crochets, qui entourent une liste séparée par des virgules d’un ou plusieurs attributs. L’ordre dans lequel les attributs sont spécifiés dans une telle liste, et l’ordre dans lequel les sections attachées à la même entité de programme sont organisées, n’est pas significative. Par exemple, les spécifications d’attribut , , [A][B][B][A]et [A, B] sont équivalentes[B, A].
global_attributes
: global_attribute_section+
;
global_attribute_section
: '[' global_attribute_target_specifier attribute_list ']'
;
global_attribute_target_specifier
: global_attribute_target ':'
;
global_attribute_target
: identifier
;
attributes
: attribute_section+
;
attribute_section
: '[' attribute_target_specifier? attribute_list ']'
;
attribute_target_specifier
: attribute_target ':'
;
attribute_target
: identifier
| keyword
;
attribute_list
: attribute (',' attribute)* ','?
;
attribute
: attribute_name attribute_arguments?
;
attribute_name
: type_name
;
attribute_arguments
: '(' ')'
| '(' positional_argument_list (',' named_argument_list)? ')'
| '(' named_argument_list ')'
;
positional_argument_list
: positional_argument (',' positional_argument)*
;
positional_argument
: argument_name? attribute_argument_expression
;
named_argument_list
: named_argument (',' named_argument)*
;
named_argument
: identifier '=' attribute_argument_expression
;
attribute_argument_expression
: non_assignment_expression
;
Pour la global_attribute_target de production, et dans le texte ci-dessous, l’identificateur doit avoir une orthographe égale ou assembly, où l’égalité est définie dans module. Pour l’attribute_target de production, et dans le texte ci-dessous, l’identificateur doit avoir une orthographe qui n’est pas égale ou assemblymodule, en utilisant la même définition d’égalité que ci-dessus.
Un attribut se compose d’un attribute_name et d’une liste facultative d’arguments positionnels et nommés. Les arguments positionnels (s’il y en a) précèdent les arguments nommés. Un argument positionnel se compose d’un attribute_argument_expression ; un argument nommé se compose d’un nom, suivi d’un signe égal, suivi d’un attribute_argument_expression, qui, ensemble, sont limités par les mêmes règles que l’affectation simple. L’ordre des arguments nommés n’est pas significatif.
Remarque : Pour des raisons pratiques, une virgule de fin est autorisée dans un global_attribute_section et un attribute_section, tout comme dans un array_initializer (§17.7). Note de fin
La attribute_name identifie une classe d’attributs.
Lorsqu’un attribut est placé au niveau global, une global_attribute_target_specifier est requise. Lorsque la global_attribute_target est égale à :
-
assembly— la cible est l’assembly conteneur -
module— la cible est le module conteneur
Aucune autre valeur pour global_attribute_target n’est autorisée.
Les noms de attribute_target standardisés sont , , eventfield, method, param, , , propertyet return.typetypevar Ces noms cibles ne doivent être utilisés que dans les contextes suivants :
-
event— un événement. -
field— un champ. Un événement de type champ (c’est-à-dire un sans accesseurs) (§15.8.2) et une propriété implémentée automatiquement (§15.7.4) peut également avoir un attribut avec cette cible. -
method— constructeur, finaliseur, méthode, opérateur, propriété get and set accessors, indexer get and set accessors, and event add and remove accessors. Un événement de type champ (c’est-à-dire sans accesseurs) peut également avoir un attribut avec cette cible. -
param— un accesseur de jeu de propriétés, un accesseur de jeu d’indexeurs, des accesseurs d’ajout et de suppression d’événements et un paramètre dans un constructeur, une méthode et un opérateur. -
property— une propriété et un indexeur. -
return— un délégué, une méthode, un opérateur, un accesseur get de propriété et un accesseur get de l’indexeur. -
type— un délégué, une classe, un struct, une énumération et une interface. -
typevar— paramètre de type.
Certains contextes permettent la spécification d’un attribut sur plusieurs cibles. Un programme peut spécifier explicitement la cible en incluant un attribute_target_specifier. Sans attribute_target_specifier une valeur par défaut est appliquée, mais une attribute_target_specifier peut être utilisée pour affirmer ou remplacer la valeur par défaut. Les contextes sont résolus comme suit :
- Pour un attribut sur une déclaration de délégué, la cible par défaut est le délégué. Sinon, lorsque la attribute_target est égale à :
-
type— la cible est le délégué -
return— la cible est la valeur de retour
-
- Pour un attribut sur une déclaration de méthode, la cible par défaut est la méthode. Sinon, lorsque la attribute_target est égale à :
-
method— la cible est la méthode -
return— la cible est la valeur de retour
-
- Pour un attribut sur une déclaration d’opérateur, la cible par défaut est l’opérateur. Sinon, lorsque la attribute_target est égale à :
-
method— la cible est l’opérateur -
return— la cible est la valeur de retour
-
- Pour un attribut sur une déclaration d’accesseur get pour une déclaration de propriété ou d’indexeur, la cible par défaut est la méthode associée. Sinon, lorsque la attribute_target est égale à :
-
method— la cible est la méthode associée -
return— la cible est la valeur de retour
-
- Pour un attribut spécifié sur un accesseur set pour une déclaration de propriété ou d’indexeur, la cible par défaut est la méthode associée. Sinon, lorsque la attribute_target est égale à :
-
method— la cible est la méthode associée -
param— la cible est le paramètre implicite seul
-
- Pour un attribut sur une déclaration de propriété implémentée automatiquement, la cible par défaut est la propriété. Sinon, lorsque la attribute_target est égale à :
-
field— la cible est le champ de stockage généré par le compilateur pour la propriété
-
- Pour un attribut spécifié sur une déclaration d’événement qui omet event_accessor_declarations la cible par défaut est la déclaration d’événement. Sinon, lorsque la attribute_target est égale à :
-
event— la cible est la déclaration d’événement -
field— la cible est le champ -
method— les cibles sont les méthodes
-
- Dans le cas d’une déclaration d’événement qui n’omet pas event_accessor_declarations la cible par défaut est la méthode.
-
method— la cible est la méthode associée -
param— la cible est le paramètre solitaire
-
Dans tous les autres contextes, l’inclusion d’une attribute_target_specifier est autorisée mais inutile.
Exemple : une déclaration de classe peut inclure ou omettre le spécificateur
type:[type: Author("Brian Kernighan")] class Class1 {} [Author("Dennis Ritchie")] class Class2 {}exemple de fin.
Une implémentation peut accepter d’autres attribute_targets, dont les objectifs sont définis par l’implémentation. Une implémentation qui ne reconnaît pas une telle attribute_target émet un avertissement et ignore le attribute_section contenant.
Par convention, les classes d’attributs sont nommées avec un suffixe de Attribute. Une attribute_name peut inclure ou omettre ce suffixe. Plus précisément, une attribute_name est résolue comme suit :
- Si l’identificateur le plus à droite de l’attribute_name est un identificateur détaillé (§6.4.3), le attribute_name est résolu en tant que type_name (§7.8). Si le résultat n’est pas un type dérivé de , une erreur au moment de
System.Attributela compilation se produit. - Sinon,
- Le attribute_name est résolu sous la forme d’un type_name (§7.8), sauf si des erreurs sont supprimées. Si cette résolution réussit et génère un type dérivé de
System.Attributecette étape, le type est le résultat de cette étape. - Les caractères
Attributesont ajoutés à l’identificateur le plus à droite dans l’attribute_nameet la chaîne de jetons résultante est résolue en tant que type_name (§7.8), sauf toute erreur est supprimée. Si cette résolution réussit et génère un type dérivé deSystem.Attributecette étape, le type est le résultat de cette étape.
- Le attribute_name est résolu sous la forme d’un type_name (§7.8), sauf si des erreurs sont supprimées. Si cette résolution réussit et génère un type dérivé de
Si exactement l’une des deux étapes ci-dessus entraîne un type dérivé System.Attributede , ce type est le résultat de la attribute_name. Sinon, une erreur au moment de la compilation se produit.
Exemple : si une classe d’attributs est trouvée avec et sans ce suffixe, une ambiguïté est présente et un résultat d’erreur au moment de la compilation. Si l’attribute_name est orthographié de telle sorte que son identificateur le plus à droite soit un identificateur détaillé (§6.4.3), seul un attribut sans suffixe est mis en correspondance, ce qui permet de résoudre une telle ambiguïté. L’exemple
[AttributeUsage(AttributeTargets.All)] public class Example : Attribute {} [AttributeUsage(AttributeTargets.All)] public class ExampleAttribute : Attribute {} [Example] // Error: ambiguity class Class1 {} [ExampleAttribute] // Refers to ExampleAttribute class Class2 {} [@Example] // Refers to Example class Class3 {} [@ExampleAttribute] // Refers to ExampleAttribute class Class4 {}affiche deux classes d’attribut nommées
ExampleetExampleAttribute. L’attribut[Example]est ambigu, car il peut faire référence à l’un ouExamplel’autreExampleAttribute. L’utilisation d’un identificateur détaillé permet de spécifier l’intention exacte dans de tels cas rares. L’attribut[ExampleAttribute]n’est pas ambigu (même s’il y avait une classe d’attribut nomméeExampleAttributeAttribute!). Si la déclaration de la classeExampleest supprimée, les deux attributs font référence à la classe d’attribut nomméeExampleAttribute, comme suit :[AttributeUsage(AttributeTargets.All)] public class ExampleAttribute : Attribute {} [Example] // Refers to ExampleAttribute class Class1 {} [ExampleAttribute] // Refers to ExampleAttribute class Class2 {} [@Example] // Error: no attribute named “Example” class Class3 {}exemple de fin
Il s’agit d’une erreur au moment de la compilation pour utiliser une classe d’attributs à usage unique plusieurs fois sur la même entité.
Exemple : l’exemple
[AttributeUsage(AttributeTargets.Class)] public class HelpStringAttribute : Attribute { public HelpStringAttribute(string value) { Value = value; } public string Value { get; } } [HelpString("Description of Class1")] [HelpString("Another description of Class1")] // multiple uses not allowed public class Class1 {}génère une erreur au moment de la compilation, car elle tente d’utiliser
HelpString, qui est une classe d’attribut à usage unique, plusieurs fois sur la déclaration deClass1.exemple de fin
Une expression E est une attribute_argument_expression si toutes les instructions suivantes sont vraies :
- Le type de paramètre est un type de
Eparamètre d’attribut (§23.2.4). - Au moment de la compilation, la valeur de
Epeut être résolue en l’une des options suivantes :
Exemple :
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field)] public class TestAttribute : Attribute { public int P1 { get; set; } public Type P2 { get; set; } public object P3 { get; set; } } [Test(P1 = 1234, P3 = new int[]{1, 3, 5}, P2 = typeof(float))] class MyClass {} class C<T> { [Test(P2 = typeof(T))] // Error – T not a closed type. int x1; [Test(P2 = typeof(C<T>))] // Error – C<;T>; not a closed type. int x2; [Test(P2 = typeof(C<int>))] // Ok int x3; [Test(P2 = typeof(C<>))] // Ok int x4; }exemple de fin
Les attributs d’un type déclaré dans plusieurs parties sont déterminés en combinant, dans un ordre non spécifié, les attributs de chacune de ses parties. Si le même attribut est placé sur plusieurs parties, il équivaut à spécifier cet attribut plusieurs fois sur le type.
Exemple : les deux parties :
[Attr1, Attr2("hello")] partial class A {} [Attr3, Attr2("goodbye")] partial class A {}sont équivalents à la déclaration unique suivante :
[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")] class A {}exemple de fin
Les attributs sur les paramètres de type se combinent de la même façon.
23.4 Instances d’attribut
23.4.1 Général
Une instance d’attribut est une instance qui représente un attribut au moment de l’exécution. Un attribut est défini avec une classe d’attributs, des arguments positionnels et des arguments nommés. Une instance d’attribut est une instance de la classe d’attribut initialisée avec les arguments positionnels et nommés.
La récupération d’une instance d’attribut implique à la fois le traitement au moment de la compilation et au moment de l’exécution, comme décrit dans les sous-sections suivantes.
23.4.2 Compilation d’un attribut
La compilation d’un , T, P et spécifiée sur une entité N de programme est compilée dans un assembly E en procédant comme suit :
- Suivez les étapes de traitement au moment de la compilation d’un object_creation_expression du formulaire nouveau
T(P). Ces étapes entraînent une erreur au moment de la compilation ou déterminent un constructeurCd’instance surTlequel il peut être appelé au moment de l’exécution. - S’il
Cn’a pas d’accessibilité publique, une erreur au moment de la compilation se produit. - Pour chaque named_argument
ArgdansN:- Supposons
Nameque l’identificateurde la named_argumentArg. -
Namedoit identifier un champ public ou une propriété en lecture-écriture non statique surT. S’ilTn’existe pas de champ ou de propriété de ce type, une erreur au moment de la compilation se produit.
- Supposons
- Si l’une des valeurs comprises dans positional_argument_list
Pou l’une des valeurs de named_argument_listNest de typeSystem.Stringet que la valeur n’est pas bien formée telle que définie par la norme Unicode, elle est définie par l’implémentation si la valeur compilée est égale à la valeur d’exécution récupérée (§23.4.3).Remarque : Par exemple, une chaîne qui contient une unité de code UTF-16 de substitution élevée qui n’est pas immédiatement suivie d’une unité de code de substitution faible n’est pas bien formée. Note de fin
- Stockez les informations suivantes (pour l’instanciation au moment de l’exécution de l’attribut) dans la sortie de l’assembly par le compilateur en raison de la compilation du programme contenant l’attribut : la classe
Td’attribut, le constructeurCd’instance surT, le positional_argument_listP, le named_argument_listNet l’entitéEde programme associée, avec les valeurs résolues complètement au moment de la compilation.
23.4.3 Récupération au moment de l’exécution d’une instance d’attribut
À l’aide des termes définis dans le §23.4.2, l’instance d’attribut représentée par T, CPet Nassociée E à celle-ci peut être récupérée au moment de l’exécution à partir de l’assembly A en procédant comme suit :
- Suivez les étapes de traitement au moment de l’exécution d’un object_creation_expression du formulaire
new T(P), en utilisant le constructeurCd’instance et les valeurs comme déterminé au moment de la compilation. Ces étapes entraînent une exception ou produisent une instanceOdeT. - Pour chaque named_argument
ArgdansN, dans l’ordre :- Supposons
Nameque l’identificateurde la named_argumentArg. SiNameelle n’identifie pas un champ ou une propriété en lecture-écriture publique non statique surO, une exception est levée. - Nous allons
Valueêtre le résultat de l’évaluation de la attribute_argument_expression deArg. - Si
Nameelle identifie un champ surO, définissez ce champ surValue. - Sinon, Name identifie une propriété sur
O. Définissez cette propriété sur Value. - Le résultat est
O, une instance de la classeTd’attributs qui a été initialisée avec le positional_argument_listPet le named_argument_listN.
- Supposons
Remarque : Le format de stockage
T, ,CP(Net l’associant àE) dansAet le mécanisme permettant de spécifierEet de récupérerT,CPNà partirA(et donc de la façon dont une instance d’attribut est obtenue au moment de l’exécution) dépasse l’étendue de cette spécification. Note de fin
23.5 Attributs réservés
23.5.1 Général
Un certain nombre d’attributs affectent la langue d’une certaine manière. Ces attributs incluent :
-
System.AttributeUsageAttribute(§23.5.2), qui est utilisé pour décrire les façons dont une classe d’attribut peut être utilisée. -
System.Diagnostics.ConditionalAttribute(§23.5.3), est une classe d’attributs multi-utilisation utilisée pour définir des méthodes conditionnelles et des classes d’attributs conditionnels. Cet attribut indique une condition en testant un symbole de compilation conditionnelle. -
System.ObsoleteAttribute(§23.5.4), utilisé pour marquer un membre comme obsolète. -
System.Runtime.CompilerServices.AsyncMethodBuilderAttribute(§23.5.5), utilisé pour établir un générateur de tâches pour une méthode asynchrone. -
System.Runtime.CompilerServices.CallerLineNumberAttribute(§23.5.6.2),System.Runtime.CompilerServices.CallerFilePathAttribute(§23.5.6.3) etSystem.Runtime.CompilerServices.CallerMemberNameAttribute(§23.5.6.4), qui sont utilisées pour fournir des informations sur le contexte appelant aux paramètres facultatifs. -
System.Runtime.CompilerServices.EnumeratorCancellationAttribute(§23.5.8), utilisé pour spécifier le paramètre du jeton d’annulation dans un itérateur asynchrone.
Les attributs d’analyse statique nullable (§23.5.7) peuvent améliorer la correction des avertissements générés pour les capacités Null et les états Null (§8.9.5).
Un environnement d’exécution peut fournir des attributs définis par l’implémentation supplémentaires qui affectent l’exécution d’un programme C#.
23.5.2 Attributs
L’attribut AttributeUsage est utilisé pour décrire la façon dont la classe d’attributs peut être utilisée.
Une classe décorée avec l’attribut AttributeUsage dérive directement ou indirectement de System.Attributel’attribut. Sinon, une erreur au moment de la compilation se produit.
Remarque : Pour obtenir un exemple d’utilisation de cet attribut, consultez §23.2.2. Note de fin
23.5.3 L’attribut conditionnel
23.5.3.1 Général
L’attribut Conditional active la définition des es de la méthode conditionnelleet des classes d’attributs conditionnels.
23.5.3.2 Méthodes conditionnelles
Une méthode décorée avec l’attribut Conditional est une méthode conditionnelle. Chaque méthode conditionnelle est donc associée aux symboles de compilation conditionnelle déclarés dans ses Conditional attributs.
Exemple :
class Eg { [Conditional("ALPHA")] [Conditional("BETA")] public static void M() { // ... } }déclare
Eg.Men tant que méthode conditionnelle associée aux deux symbolesALPHAde compilation conditionnelle etBETA.exemple de fin
Un appel à une méthode conditionnelle est inclus si un ou plusieurs de ses symboles de compilation conditionnelle associés sont définis au point d’appel, sinon l’appel est omis.
Une méthode conditionnelle est soumise aux restrictions suivantes :
- La méthode conditionnelle doit être une méthode dans un class_declaration ou struct_declaration. Une erreur au moment de la compilation se produit si l’attribut
Conditionalest spécifié sur une méthode dans une déclaration d’interface. - La méthode conditionnelle ne doit pas être un accesseur d’une propriété, d’un indexeur ou d’un événement.
- La méthode conditionnelle doit avoir un type de retour .
void - La méthode conditionnelle ne doit pas être marquée avec le
overridemodificateur. Toutefois, une méthode conditionnelle peut être marquée avec levirtualmodificateur. Les remplacements de cette méthode sont implicitement conditionnels et ne doivent pas être explicitement marqués avec unConditionalattribut. - La méthode conditionnelle ne doit pas être une implémentation d’une méthode d’interface. Sinon, une erreur au moment de la compilation se produit.
- Les paramètres de la méthode conditionnelle ne doivent pas être des paramètres de sortie.
Remarque : Les attributs avec un
AttributeUsage(§23.2.2), y comprisAttributeTargets.Methodpeuvent normalement être appliqués aux accesseurs de propriétés, d’indexeurs et d’événements. Les restrictions ci-dessus interdisent cette utilisation de l’attributConditional. Note de fin
En outre, une erreur au moment de la compilation se produit si un délégué est créé à partir d’une méthode conditionnelle.
Exemple : l’exemple
#define DEBUG using System; using System.Diagnostics; class Class1 { [Conditional("DEBUG")] public static void M() { Console.WriteLine("Executed Class1.M"); } } class Class2 { public static void Test() { Class1.M(); } }déclare
Class1.Mcomme méthode conditionnelle.Class2laTestméthode appelle cette méthode. Étant donné que le symboleDEBUGde compilation conditionnelle est défini, s’ilClass2.Testest appelé, il appelleM. Si le symboleDEBUGn’avait pas été défini, n’appelaitClass2.TestClass1.Mpas .exemple de fin
Il est important de comprendre que l’inclusion ou l’exclusion d’un appel à une méthode conditionnelle est contrôlée par les symboles de compilation conditionnelle au point de l’appel.
Exemple : dans le code suivant
// File Class1.cs: using System; using System.Diagnostics; class Class1 { [Conditional("DEBUG")] public static void F() { Console.WriteLine("Executed Class1.F"); } } // File Class2.cs: #define DEBUG class Class2 { public static void G() { Class1.F(); // F is called } } // File Class3.cs: #undef DEBUG class Class3 { public static void H() { Class1.F(); // F is not called } }les classes
Class2etClass3chacune contiennent des appels à la méthodeClass1.Fconditionnelle, qui est conditionnelle selon qu’elle est définie ou nonDEBUG. Étant donné que ce symbole est défini dans le contexte deClass2mais pasClass3, l’appel àFdansClass2est inclus, tandis que l’appel àFl’entréeClass3est omis.exemple de fin
L’utilisation de méthodes conditionnelles dans une chaîne d’héritage peut prêter à confusion. Les appels effectués vers une méthode conditionnelle via base, du formulaire base.M, sont soumis aux règles d’appel de méthode conditionnelle normales.
Exemple : dans le code suivant
// File Class1.cs using System; using System.Diagnostics; class Class1 { [Conditional("DEBUG")] public virtual void M() => Console.WriteLine("Class1.M executed"); } // File Class2.cs class Class2 : Class1 { public override void M() { Console.WriteLine("Class2.M executed"); base.M(); // base.M is not called! } } // File Class3.cs #define DEBUG class Class3 { public static void Main() { Class2 c = new Class2(); c.M(); // M is called } }
Class2inclut un appel à l’élémentMdéfini dans sa classe de base. Cet appel est omis, car la méthode de base est conditionnelle en fonction de la présence du symboleDEBUG, qui n’est pas définie. Ainsi, la méthode écrit uniquement dans la console «Class2.M executed». L’utilisation judicieuse de pp_declarationpeut éliminer de tels problèmes.exemple de fin
23.5.3.3 Classes d’attributs conditionnels
Une classe d’attributs (§23.2) décorée avec un ou plusieurs Conditional attributs est une classe d’attributs conditionnelle. Une classe d’attributs conditionnels est donc associée aux symboles de compilation conditionnelle déclarés dans ses Conditional attributs.
Exemple :
[Conditional("ALPHA")] [Conditional("BETA")] public class TestAttribute : Attribute {}déclare
TestAttributeen tant que classe d’attribut conditionnel associée aux symboles de compilation conditionnelleALPHAetBETA.exemple de fin
Les spécifications d’attribut (§23.3) d’un attribut conditionnel sont incluses si un ou plusieurs de ses symboles de compilation conditionnelle associés sont définis au point de spécification, sinon la spécification d’attribut est omise.
Il est important de noter que l’inclusion ou l’exclusion d’une spécification d’attribut d’une classe d’attributs conditionnels est contrôlée par les symboles de compilation conditionnelle au point de la spécification.
Exemple : Dans l’exemple
// File Test.cs: using System; using System.Diagnostics; [Conditional("DEBUG")] public class TestAttribute : Attribute {} // File Class1.cs: #define DEBUG [Test] // TestAttribute is specified class Class1 {} // File Class2.cs: #undef DEBUG [Test] // TestAttribute is not specified class Class2 {}les classes
Class1etClass2sont chacune décorées avec un attributTest, qui est conditionnelle selon qu’elle est définie ou nonDEBUG. Dans la mesure où ce symbole est défini dans le contexte deClass1l’attribut Test activé, mais pasClass2dans le contexte, la spécification de l’attribut estClass1incluse, tandis que la spécification de l’attributTestestClass2omise.exemple de fin
23.5.4 Attribut obsolète
L’attribut Obsolete est utilisé pour marquer les types et les membres des types qui ne doivent plus être utilisés.
Si un programme utilise un type ou un membre décoré avec l’attribut Obsolete, un compilateur émet un avertissement ou une erreur. Plus précisément, un compilateur émet un avertissement si aucun paramètre d’erreur n’est fourni, ou si le paramètre d’erreur est fourni et a la valeur false. Un compilateur émet une erreur si le paramètre d’erreur est spécifié et a la valeur true.
Exemple : dans le code suivant
[Obsolete("This class is obsolete; use class B instead")] class A { public void F() {} } class B { public void F() {} } class Test { static void Main() { A a = new A(); // Warning a.F(); } }la classe
Aest décorée avec l’attributObsolete. Chaque utilisation deArésultatsMaindans un avertissement qui inclut le message spécifié, « Cette classe est obsolète ; utiliser la classeBà la place ».exemple de fin
23.5.5 L’attribut AsyncMethodBuilder
Cet attribut est décrit dans le §15.14.1.
23.5.6 Attributs caller-info
23.5.6.1 Général
À des fins telles que la journalisation et la création de rapports, il est parfois utile pour un membre de fonction d’obtenir certaines informations au moment de la compilation sur le code appelant. Les attributs d’informations de l’appelant permettent de transmettre ces informations de manière transparente.
Lorsqu’un paramètre facultatif est annoté avec l’un des attributs caller-info, l’omission de l’argument correspondant dans un appel n’entraîne pas nécessairement la substitution de la valeur de paramètre par défaut. Au lieu de cela, si les informations spécifiées sur le contexte appelant sont disponibles, ces informations seront transmises en tant que valeur d’argument.
Exemple :
public void Log( [CallerLineNumber] int line = -1, [CallerFilePath] string path = null, [CallerMemberName] string name = null ) { Console.WriteLine((line < 0) ? "No line" : "Line "+ line); Console.WriteLine((path == null) ? "No file path" : path); Console.WriteLine((name == null) ? "No member name" : name); }Un appel à
Log()sans arguments imprime le numéro de ligne et le chemin d’accès du fichier de l’appel, ainsi que le nom du membre dans lequel l’appel s’est produit.exemple de fin
Les attributs d’informations de l’appelant peuvent se produire sur des paramètres facultatifs n’importe où, y compris dans les déclarations de délégué. Toutefois, les attributs d’informations de l’appelant spécifique ont des restrictions sur les types des paramètres qu’ils peuvent attribuer, afin qu’il y ait toujours une conversion implicite d’une valeur substituée au type de paramètre.
Il s’agit d’une erreur d’avoir le même attribut d’info-appelant sur un paramètre de la définition et de l’implémentation d’une partie d’une déclaration de méthode partielle. Seuls les attributs d’informations de l’appelant dans la partie de définition sont appliqués, tandis que les attributs d’informations de l’appelant qui se produisent uniquement dans le composant d’implémentation sont ignorés.
Les informations de l’appelant n’affectent pas la résolution de surcharge. Étant donné que les paramètres facultatifs attribués sont toujours omis à partir du code source de l’appelant, la résolution de surcharge ignore ces paramètres de la même façon qu’il ignore les autres paramètres facultatifs omis (§12.6.4).
Les informations de l’appelant sont remplacées uniquement lorsqu’une fonction est explicitement appelée dans le code source. Les appels implicites tels que les appels de constructeur parent implicites n’ont pas d’emplacement source et ne remplacent pas les informations de l’appelant. En outre, les appels liés dynamiquement ne remplacent pas les informations de l’appelant. Lorsqu’un paramètre attribut d’info-appelant est omis dans de tels cas, la valeur par défaut spécifiée du paramètre est utilisée à la place.
Une exception est les expressions de requête. Ces extensions sont considérées comme syntactiques et, si les appels qu’ils développent pour omettre des paramètres facultatifs avec des attributs d’informations d’appelant, les informations de l’appelant seront remplacées. L’emplacement utilisé est l’emplacement de la clause de requête à partir de laquelle l’appel a été généré.
Si plusieurs attributs caller-info sont spécifiés sur un paramètre donné, ils sont reconnus dans l’ordre suivant : CallerLineNumber, CallerFilePath. CallerMemberName Considérez la déclaration de paramètre suivante :
[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ...
CallerLineNumber est prioritaire, et les deux autres attributs sont ignorés. S’il CallerLineNumber était omis, CallerFilePath précédait et CallerMemberName serait ignoré. L’ordre lexical de ces attributs n’est pas pertinent.
23.5.6.2 Attribut CallerLineNumber
L’attribut System.Runtime.CompilerServices.CallerLineNumberAttribute est autorisé sur les paramètres facultatifs lorsqu’il existe une conversion implicite standard (§10.4.2) de la valeur int.MaxValue constante au type du paramètre. Cela garantit que tout numéro de ligne non négatif jusqu’à cette valeur peut être transmis sans erreur.
Si un appel de fonction à partir d’un emplacement dans le code source omet un paramètre facultatif avec le CallerLineNumberAttribute, un littéral numérique représentant le numéro de ligne de cet emplacement est utilisé comme argument pour l’appel au lieu de la valeur de paramètre par défaut.
Si l’appel s’étend sur plusieurs lignes, la ligne choisie dépend de l’implémentation.
Le numéro de ligne peut être affecté par #line les directives (§6.5.8).
23.5.6.3 L’attribut CallerFilePath
L’attribut System.Runtime.CompilerServices.CallerFilePathAttribute est autorisé sur les paramètres facultatifs lorsqu’il existe une conversion implicite standard (§10.4.2) du string type du paramètre.
Si un appel de fonction à partir d’un emplacement dans le code source omet un paramètre facultatif avec le CallerFilePathAttribute, un littéral de chaîne représentant le chemin d’accès du fichier de cet emplacement est utilisé comme argument à l’appel au lieu de la valeur de paramètre par défaut.
Le format du chemin d’accès au fichier dépend de l’implémentation.
Le chemin d’accès au fichier peut être affecté par #line les directives (§6.5.8).
23.5.6.4 Attribut CallerMemberName
L’attribut System.Runtime.CompilerServices.CallerMemberNameAttribute est autorisé sur les paramètres facultatifs lorsqu’il existe une conversion implicite standard (§10.4.2) du string type du paramètre.
Si un appel de fonction à partir d’un emplacement dans le corps d’un membre de fonction ou dans un attribut appliqué au membre de la fonction lui-même ou à son type de retour, paramètres ou paramètres de type dans le code source omet un paramètre facultatif avec le CallerMemberNameAttribute, un littéral de chaîne représentant le nom de ce membre est utilisé comme argument de l’appel au lieu de la valeur de paramètre par défaut.
Pour les appels qui se produisent dans les méthodes génériques, seul le nom de la méthode lui-même est utilisé, sans la liste de paramètres de type.
Pour les appels qui se produisent dans des implémentations de membres d’interface explicites, seul le nom de méthode lui-même est utilisé, sans la qualification de l’interface précédente.
Pour les appels qui se produisent dans les accesseurs de propriété ou d’événement, le nom de membre utilisé est celui de la propriété ou de l’événement lui-même.
Pour les appels qui se produisent dans les accesseurs d’indexeur, le nom de membre utilisé est celui fourni par un IndexerNameAttribute (§23.6) sur le membre d’indexeur, le cas échéant ou le nom Item par défaut.
Pour les appels qui se produisent dans les initialiseurs de champ ou d’événement, le nom de membre utilisé est le nom du champ ou de l’événement initialisé.
Pour les appels qui se produisent dans les déclarations des constructeurs d’instance, des constructeurs statiques, des finaliseurs et des opérateurs utilisés, le nom de membre utilisé dépend de l’implémentation.
23.5.7 Attributs d’analyse du code
23.5.7.1 Général
Les attributs de cette sous-clause sont utilisés pour fournir des informations supplémentaires pour prendre en charge un compilateur qui fournit des diagnostics de nullabilité et d'état de nullité (§8.9.5). Un compilateur n’est pas nécessaire pour effectuer des diagnostics d’état Null. La présence ou l’absence de ces attributs n’affectent pas la langue ni le comportement d’un programme. Un compilateur qui ne fournit pas de diagnostics d’état null doit lire et ignorer la présence de ces attributs. Un compilateur qui fournit des diagnostics d’état null doit utiliser la signification définie dans cette sous-clause pour l’un de ces attributs qu’il utilise pour informer ses diagnostics.
Les attributs d’analyse du code sont déclarés dans l’espace de noms System.Diagnostics.CodeAnalysis.
| Attribut | Signification |
|---|---|
AllowNull (§23.5.7.2) |
Un argument non nullable peut être null. |
DisallowNull (§23.5.7.3) |
Un argument nullable ne doit jamais être null. |
MaybeNull (§23.5.7.6) |
Une valeur de retour non nullable peut être null. |
NotNull (§23.5.7.8) |
Une valeur de retour nullable ne sera jamais null. |
MaybeNullWhen (§23.5.7.7) |
Un argument non-nullable peut être null quand la méthode retourne la valeur bool spécifiée. |
NotNullWhen (§23.5.7.10) |
Un argument nullable ne sera pas null lorsque la méthode retourne la valeur spécifiée bool . |
NotNullIfNotNull (§23.5.7.9) |
Une valeur de retour n’est pas null si l’argument du paramètre spécifié n’est pas null. |
DoesNotReturn (§23.5.7.4) |
Cette méthode ne retourne jamais. |
DoesNotReturnIf (§23.5.7.5) |
Cette méthode ne retourne jamais si le paramètre associé bool a la valeur spécifiée. |
Les sous-sections suivantes dans le §23.5.7.1 sont conditionnellement normatives.
23.5.7.2 Attribut AllowNull
Spécifie qu’une valeur Null est autorisée en tant qu’entrée même si le type correspondant l’interdit.
Exemple : considérez la propriété en lecture/écriture suivante qui ne retourne
nulljamais, car elle a une valeur par défaut raisonnable. Toutefois, un utilisateur peut donner la valeur Null au accesseur set pour définir la propriété sur cette valeur par défaut.#nullable enable public class X { [AllowNull] public string ScreenName { get => _screenName; set => _screenName = value ?? GenerateRandomScreenName(); } private string _screenName = GenerateRandomScreenName(); private static string GenerateRandomScreenName() => ...; }Compte tenu de l’utilisation suivante de l’accesseur set de cette propriété
var v = new X(); v.ScreenName = null; // may warn without attribute AllowNullsans l’attribut, un compilateur peut générer un avertissement car la propriété de type non-nullable semble être définie sur une valeur nulle. La présence de l’attribut supprime cet avertissement. exemple de fin
23.5.7.3 L’attribut DisallowNull
Spécifie qu’une valeur null est interdite en tant qu’entrée même si le type correspondant l’autorise.
Exemple : considérez la propriété suivante dans laquelle la valeur null est la valeur par défaut, mais les clients peuvent uniquement la définir sur une valeur non null.
#nullable enable public class X { [DisallowNull] public string? ReviewComment { get => _comment; set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null"); } private string? _comment = default; }L’accesseur get peut renvoyer la valeur par défaut de
null. Par conséquent, un compilateur peut avertir qu’il doit être vérifié avant l’accès. En outre, il avertit les appelants que, même s’il peut s’agir de null, les appelants ne doivent pas le définir explicitement sur Null. exemple de fin
23.5.7.4 L’attribut DoesNotReturn
Spécifie qu’une méthode donnée ne retourne jamais.
Exemple : Tenez compte des éléments suivants :
public class X { [DoesNotReturn] private void FailFast() => throw new InvalidOperationException(); public void SetState(object? containedField) { if ((!isInitialized) || (containedField == null)) { FailFast(); } // null check not needed. _field = containedField; } private bool isInitialized = false; private object _field; }La présence de l’attribut aide un compilateur de plusieurs façons. Tout d’abord, un compilateur peut émettre un avertissement s’il existe un chemin d’accès où la méthode peut quitter sans lever d’exception. Ensuite, un compilateur peut supprimer des avertissements nullables dans n’importe quel code après un appel à cette méthode, jusqu’à ce qu’une clause catch appropriée soit trouvée. Troisièmement, le code inaccessible n’affecte aucun état Null.
L’attribut ne change pas l’accessibilité (§13.2) ou l’analyse de l’affectation définie (§9.4) en fonction de la présence de cet attribut. Il est utilisé uniquement pour affecter les avertissements de nullabilité. exemple de fin
23.5.7.5 L’attribut DoesNotReturnIf
Spécifie qu’une méthode donnée ne retourne jamais si le paramètre associé bool a la valeur spécifiée.
Exemple : Tenez compte des éléments suivants :
#nullable enable public class X { private void ThrowIfNull([DoesNotReturnIf(true)] bool isNull, string argumentName) { if (!isNull) { throw new ArgumentException(argumentName, $"argument {argumentName} can't be null"); } } public void SetFieldState(object containedField) { ThrowIfNull(containedField == null, nameof(containedField)); // unreachable code when "isInitialized" is false: _field = containedField; } private bool isInitialized = false; private object _field = default!; }exemple de fin
23.5.7.6 L’attribut MaybeNull
Spécifie qu’une valeur de retour non nullable peut être null.
Exemple : Considérez la méthode générique suivante :
#nullable enable public T? Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }L’idée de ce code est que si
Telle est remplacée parstring,T?devient une annotation nullable. Toutefois, ce code n’est pas légal, carTil n’est pas contraint d’être un type de référence. Toutefois, l’ajout de cet attribut résout le problème :#nullable enable [return: MaybeNull] public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }L’attribut informe les appelants que le contrat implique un type non nullable, mais que la valeur de retour peut réellement être
null. exemple de fin
23.5.7.7 L’attribut MaybeNullWhen
Spécifie qu’un argument non nullable peut être null lorsque la méthode retourne la valeur spécifiée bool . Cela est similaire à l’attribut MaybeNull (§23.5.7.6), mais inclut un paramètre pour la valeur de retour spécifiée.
23.5.7.8 Attribut NotNull
Spécifie qu’une valeur nullable ne sera null jamais si la méthode retourne (au lieu de lever).
Exemple : Tenez compte des éléments suivants :
#nullable enable public static void ThrowWhenNull([NotNull] object? value, string valueExpression = "") => _ = value ?? throw new ArgumentNullException(valueExpression); public static void LogMessage(string? message) { ThrowWhenNull(message, nameof(message)); Console.WriteLine(message.Length); }Lorsque les types de référence nullables sont activés, la méthode
ThrowWhenNullse compile sans avertissements. Lorsque cette méthode est retournée, l’argumentvalueest garanti ne pasnullêtre . Toutefois, il est acceptable d’appelerThrowWhenNullavec une référence Null. exemple de fin
23.5.7.9 Attribut NotNullIfNotNull
Spécifie qu’une valeur de retour n’est pas null si l’argument du paramètre spécifié n’est pas null.
Exemple : L’état null d’une valeur de retour peut dépendre de l’état Null d’un ou de plusieurs arguments. Pour faciliter l’analyse d’un compilateur lorsqu’une méthode retourne toujours une valeur non null lorsque certains arguments ne sont pas
nulll’attributNotNullIfNotNullpeut être utilisé. Considérez la méthode suivante :#nullable enable string GetTopLevelDomainFromFullUrl(string url) { ... }Si l’argument
urln’est pasnull,nulln’est pas retourné. Lorsque des références nullables sont activées, cette signature fonctionne correctement, à condition que l’API n’accepte jamais d’argument Null. Toutefois, si l’argument peut être null, la valeur de retour peut également être null. Pour exprimer ce contrat correctement, annotez cette méthode comme suit :#nullable enable [return: NotNullIfNotNull("url")] string? GetTopLevelDomainFromFullUrl(string? url) { ... }exemple de fin
23.5.7.10 Attribut NotNullWhen
Spécifie qu’un argument nullable ne sera null pas lorsque la méthode retourne la valeur spécifiée bool .
Exemple : la méthode
String.IsNullOrEmpty(String)de bibliothèque retournetruelorsque l’argument estnullou une chaîne vide. Il s’agit d’une forme de vérification null : les appelants n’ont pas besoin de vérifier null-check l’argument si la méthode retournefalse. Pour faire en sorte qu’une méthode comme celle-ci prenne en compte les valeurs Null, faites en sorte que le type de paramètre soit un type de référence nullable et ajoutez l’attribut NotNullWhen :#nullable enable bool IsNullOrEmpty([NotNullWhen(false)] string? value) { ... }exemple de fin
23.5.8 Attribut EnumeratorCancellation
Spécifie le paramètre représentant l’itérateur CancellationToken asynchrone (§15.15). L’argument de ce paramètre doit être combiné à l’argument passé à IAsyncEnumerable<T>.GetAsyncEnumerator(CancellationToken). Ce jeton combiné doit être interrogé par IAsyncEnumerator<T>.MoveNextAsync() (§15.15.5.2). Les jetons doivent être combinés en un seul jeton comme si et CancellationToken.CreateLinkedTokenSource sa Token propriété. Le jeton combiné est annulé si l’un des deux jetons sources est annulé. Le jeton combiné est considéré comme l’argument de la méthode d’itérateur asynchrone (§15.15) dans le corps de cette méthode.
Il s’agit d’une erreur si l’attribut System.Runtime.CompilerServices.EnumeratorCancellation est appliqué à plusieurs paramètres. Le compilateur peut générer un avertissement si :
- L’attribut
EnumeratorCancellationest appliqué à un paramètre d’un type autre queCancellationToken, - ou si l’attribut
EnumeratorCancellationest appliqué à un paramètre sur une méthode qui n’est pas un itérateur asynchrone (§15.15), - ou si l’attribut
EnumeratorCancellationest appliqué à un paramètre sur une méthode qui retourne une interface énumérable asynchrone (§15.15.3) plutôt qu’une interface d’énumérateur asynchrone (§15.15.2).
L’itérateur n’a pas accès à l’argument CancellationToken pour GetAsyncEnumerator lequel aucun attribut n’a ce paramètre.
Exemple : la méthode
GetStringsAsync()est un itérateur asynchrone. Avant d’effectuer un travail pour récupérer la valeur suivante, il vérifie le jeton d’annulation pour déterminer si l’itération doit être annulée. Si l’annulation est demandée, aucune autre action n’est effectuée.public static async Task ExampleCombination() { var sourceOne = new CancellationTokenSource(); var sourceTwo = new CancellationTokenSource(); await using (IAsyncEnumerator<string> enumerator = GetStringsAsync(sourceOne.Token).GetAsyncEnumerator(sourceTwo.Token)) { while (await enumerator.MoveNextAsync()) { string number = enumerator.Current; if (number == "8") sourceOne.Cancel(); if (number == "5") sourceTwo.Cancel(); Console.WriteLine(number); } } } static async IAsyncEnumerable<string> GetStringsAsync( [EnumeratorCancellation] CancellationToken token) { for (int i = 0; i < 10; i++) { if (token.IsCancellationRequested) yield break; await Task.Delay(1000, token); yield return i.ToString(); } }exemple de fin
23.6 Attributs pour l’interopérabilité
Pour l’interopérabilité avec d’autres langages, un indexeur peut être implémenté à l’aide de propriétés indexées. Si aucun attribut n’est IndexerName présent pour un indexeur, le nom Item est utilisé par défaut. L’attribut IndexerName permet à un développeur de remplacer cette valeur par défaut et de spécifier un autre nom.
Exemple : par défaut, le nom d’un indexeur est
Item. Cela peut être substitué, comme suit :[System.Runtime.CompilerServices.IndexerName("TheItem")] public int this[int index] { get { ... } set { ... } }Maintenant, le nom de l’indexeur est
TheItem.exemple de fin
ECMA C# draft specification