Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
23.1 Obecné
Velká část jazyka C# umožňuje programátorovi určit deklarativní informace o entitách definovaných v programu. Například přístupnost metody ve třídě je určena dekorací s method_modifiers public, protected, internala private.
Jazyk C# umožňuje programátorům vymyslet nové druhy deklarativních informací označovaných jako atributy. Programátoři pak můžou připojit atributy k různým entitám programu a načíst informace o atributech v prostředí za běhu.
Poznámka: Například architektura může definovat
HelpAttributeatribut, který lze umístit na určité prvky programu (například třídy a metody), aby bylo možné poskytnout mapování z těchto prvků programu do jejich dokumentace. koncová poznámka
Atributy jsou definovány prostřednictvím deklarace tříd atributů (§23.2), které mohou mít poziční a pojmenované parametry (§23.2.3). Atributy jsou připojeny k entitám v programu jazyka C# pomocí specifikací atributů (§23.3) a lze je načíst za běhu jako instance atributů (§23.4).
23.2 Třídy atributů
23.2.1 Obecné
Třída, která je odvozena z abstraktní třídy System.Attribute, ať už přímo nebo nepřímo, je třída atributu. Deklarace třídy atributu definuje nový druh atributu, který lze umístit do programových entit. Podle konvence jsou třídy atributů pojmenovány příponou Attribute. Použití atributu může obsahovat nebo vynechat tuto příponu.
Obecná deklarace třídy se nepoužije System.Attribute jako přímá nebo nepřímá základní třída.
Příklad:
public class B : Attribute {} public class C<T> : B {} // Error – generic cannot be an attributekonec příkladu
23.2.2 Použití atributu
Atribut AttributeUsage (§23.5.2) slouží k popisu způsobu použití třídy atributů.
AttributeUsage má poziční parametr (§23.2.3), který umožňuje třídě atributu určit druhy entit programu, pro které lze použít.
Příklad: Následující příklad definuje třídu atributů pojmenovanou
SimpleAttribute, která se dá umístit pouze na class_declarations a interface_declarations, a zobrazí několik použití atributuSimple.[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class SimpleAttribute : Attribute { ... } [Simple] class Class1 {...} [Simple] interface Interface1 {...}I když je tento atribut definován s názvem
SimpleAttribute, při použití tohoto atributuAttributemůže být přípona vynechána, což vede k krátkému názvuSimple. Výše uvedený příklad je tedy sémanticky ekvivalentní následujícímu:[SimpleAttribute] class Class1 {...} [SimpleAttribute] interface Interface1 {...}konec příkladu
AttributeUsage má pojmenovaný parametr (§23.2.3), který AllowMultipleoznačuje, zda lze atribut zadat více než jednou pro danou entitu. Pokud AllowMultiple je třída atributu pravdivá, pak tato třída atributu je třída atributu s více použitím a lze ji zadat více než jednou u entity. Pokud AllowMultiple je třída atributu false nebo je nezadaná, pak je tato třída atributu třída atributu s jedním použitím a lze ji zadat maximálně na entitě.
Příklad: Následující příklad definuje třídu atributu s více použitím s názvem
AuthorAttributea zobrazuje deklaraci třídy se dvěma použitími atributuAuthor:[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 { ... }konec příkladu
AttributeUsage má jiný pojmenovaný parametr (§23.2.3), který Inheritedoznačuje, zda je atribut, pokud je zadán v základní třídě, zděděna také třídami odvozenými z této základní třídy. Pokud Inherited je třída atributu true, pak je tento atribut zděděný. Pokud Inherited je třída atributu false, tento atribut není zděděný. Pokud není zadána, její výchozí hodnota je true.
Třída X atributu AttributeUsage , která nemá k němu připojený atribut, jako v
class X : Attribute { ... }
odpovídá následujícímu:
[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false,
Inherited = true)
]
class X : Attribute { ... }
23.2.3 Poziční a pojmenované parametry
Třídy atributů mohou mít poziční parametrs a pojmenovaný parametrs. Každý konstruktor veřejné instance pro třídu atributu definuje platnou posloupnost pozičních parametrů pro danou třídu atributu. Každé nestatické veřejné pole pro čtení a zápis a vlastnost třídy atributu definuje pojmenovaný parametr pro třídu atributu. Aby vlastnost definovala pojmenovaný parametr, musí mít vlastnost veřejný přístup get i veřejný přístup k objektu set.
Příklad: Následující příklad definuje třídu atributu,
HelpAttributekterá má jeden poziční parametrurl, a jeden pojmenovaný parametr,Topic. I když není statický a veřejný, vlastnostUrlnedefinuje pojmenovaný parametr, protože není pro čtení i zápis. Zobrazí se také dva použití tohoto atributu:[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 { }konec příkladu
23.2.4 Typy parametrů atributů
Typy pozičních a pojmenovaných parametrů pro třídu atributů jsou omezeny na typ parametru atributus, které jsou:
- Jeden z následujících typů:
bool, ,byte,chardouble,float,int,long,sbyte,short,string, ,uint, ,ulong.ushort - Typ
object. - Typ
System.Type. - Typy výčtů.
- Jednorozměrná pole výše uvedených typů.
- Argument konstruktoru nebo veřejné pole, které nemá jeden z těchto typů, nesmí být použit jako poziční nebo pojmenovaný parametr ve specifikaci atributu.
Specifikace atributu 23.3
Použití dříve definovaného atributu pro entitu programu se nazývá specifikace atributů. Atribut je část dalších deklarativních informací určených pro entitu programu. Atributy lze zadat v globálním rozsahu (k určení atributů pro obsahující montáž nebo modul) a pro type_declarations (§14.7), class_member_declarations (§15.3), interface_member_declarations (§19.) 4), struct_member_declarations (§16.3), enum_member_declarations (§20.2), accessor_declarations (§15.7.3), event_accessor_declarations (§15.8)), prvky parameter_lists (§15.6.2) a prvky type_parameter_lists (§15.2.3).
Atributy jsou zadány v oddílu atributůs. Oddíl atributu se skládá z dvojice hranatých závorek, které obklopují čárkami oddělený seznam jednoho nebo více atributů. Pořadí, ve kterém jsou atributy zadány v tomto seznamu a pořadí, ve kterém jsou oddíly připojené ke stejné entitě programu uspořádané, není významné. Například specifikace [A][B]atributů , [B][A], [A, B]a [B, A] jsou ekvivalentní.
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
;
Pro výrobní global_attribute_target a v následujícím textu musí mít identifikátor pravopis stejný assembly nebo module, pokud je rovnost definována v §6.4.3. Pro výrobní attribute_target a v následujícím textu musí mít identifikátor pravopis, který není roven assembly nebo module, s použitím stejné definice rovnosti jako výše.
Atribut se skládá z attribute_name a volitelného seznamu pozičních a pojmenovaných argumentů. Poziční argumenty (pokud nějaké) předchází pojmenované argumenty. Poziční argument se skládá z attribute_argument_expression; pojmenovaný argument se skládá z názvu, za kterým následuje rovnítko následované attribute_argument_expression, které jsou společně omezeny stejnými pravidly jako jednoduché přiřazení. Pořadí pojmenovaných argumentů není významné.
Poznámka: Pro usnadnění práce je koncový čárka povolena v global_attribute_section a attribute_section, stejně jako je povolena v array_initializer (§17.7). koncová poznámka
Attribute_name identifikuje třídu atributů.
Pokud je atribut umístěn na globální úrovni, je vyžadován global_attribute_target_specifier . Pokud se global_attribute_target rovná:
-
assembly— cílem je obsahující sestavení. -
module— cílem je obsahující modul.
Nejsou povoleny žádné jiné hodnoty pro global_attribute_target .
Standardizované názvy attribute_target jsou event, , fieldmethodparam, , property, return, , type, a .typevar Tyto cílové názvy se použijí pouze v následujících kontextech:
-
event— událost. -
field— pole. Událost podobná poli (tj. jedna bez příslušenství) (§15.8.2) a automaticky implementovaná vlastnost (§15.7.4) může mít také atribut s tímto cílem. -
method— konstruktor, finalizátor, metoda, operátor, vlastnost get a set accessors, indexer get a set accessors, and event add and remove accessors. Událost podobná poli (tj. jedna bez přístupových objektů) může mít také atribut s tímto cílem. -
param— přístupové objekty sady vlastností, přístupové objekty sady indexerů, přidání a odebrání přístupových objektů a parametr v konstruktoru, metodě a operátoru. -
property— vlastnost a indexer. -
return— delegát, metoda, operátor, vlastnost get accessor a indexer get accessor. -
type— delegát, třída, struktura, výčet a rozhraní. -
typevar— parametr typu.
Určité kontexty umožňují specifikaci atributu pro více než jeden cíl. Program může explicitně určit cíl zahrnutím attribute_target_specifier. Bez attribute_target_specifier se použije výchozí nastavení, ale attribute_target_specifier lze použít k potvrzení nebo přepsání výchozího nastavení. Kontexty jsou vyřešeny následujícím způsobem:
- Pro atribut deklarace delegáta je výchozím cílem delegát. V opačném případě, pokud je attribute_target rovna:
-
type— cílem je delegát -
return— cílem je návratová hodnota.
-
- Pro atribut deklarace metody výchozí cíl je metoda. V opačném případě, pokud je attribute_target rovna:
-
method— cílem je metoda -
return— cílem je návratová hodnota.
-
- Pro atribut deklarace operátoru je výchozím cílem operátor. V opačném případě, pokud je attribute_target rovna:
-
method— cílem je operátor -
return— cílem je návratová hodnota.
-
- Pro atribut pro get accessor deklarace pro vlastnost nebo indexer deklarace výchozí cíl je přidružená metoda. V opačném případě, pokud je attribute_target rovna:
-
method— cílem je přidružená metoda. -
return— cílem je návratová hodnota.
-
- Pro atribut zadaný u objektu set pro vlastnost nebo deklaraci indexeru je výchozí cíl přidružená metoda. V opačném případě, pokud je attribute_target rovna:
-
method— cílem je přidružená metoda. -
param— cílem je lone implicitní parametr.
-
- Pro atribut u automaticky implementované deklarace vlastnosti je výchozí cíl vlastnost. V opačném případě, pokud je attribute_target rovna:
-
field— cílem je pole backingu vygenerované kompilátorem pro vlastnost.
-
- Pro atribut zadaný v deklaraci události, která vynechá event_accessor_declarations výchozí cíl je deklarace události. V opačném případě, pokud je attribute_target rovna:
-
event— cílem je deklarace události. -
field— cíl je pole -
method— cíle jsou metody
-
- V případě deklarace události, která vynechá event_accessor_declarations výchozí cíl je metoda.
-
method— cílem je přidružená metoda. -
param— cílem je lone parametr.
-
Ve všech ostatních kontextech je zahrnutí attribute_target_specifier povoleno, ale nepotřebné.
Příklad: Deklarace třídy může obsahovat nebo vynechat specifikátor
type:[type: Author("Brian Kernighan")] class Class1 {} [Author("Dennis Ritchie")] class Class2 {}koncový příklad.
Implementace může přijmout jiné attribute_targets, jejichž účelem je definována implementace. Implementace, která takové attribute_target nerozpozná, vydá upozornění a ignoruje attribute_section obsahující.
Podle konvence jsou třídy atributů pojmenovány příponou Attribute. Attribute_name může tuto příponu zahrnout nebo vynechat.
Konkrétně se attribute_name vyřeší následujícím způsobem:
- Pokud je identifikátor attribute_name nejvíce vpravo doslovný identifikátor (§6.4.3), je attribute_name vyřešen jako type_name (§7.8). Pokud výsledek není typem odvozeným z
System.Attribute, dojde k chybě v době kompilace. - Jinak
- Attribute_name se vyřeší jako type_name (§7.8), s výjimkou jakýchkoli chyb. Pokud je toto řešení úspěšné a výsledkem je typ odvozený od
System.Attributetohoto typu, je výsledkem tohoto kroku. - Znaky
Attributejsou v attribute_name připojeny k identifikátoru nejvíce vpravo a výsledný řetězec tokenů je vyřešen jako type_name (§7.8) s výjimkou chyb, které jsou potlačeny. Pokud je toto řešení úspěšné a výsledkem je typ odvozený odSystem.Attributetohoto typu, je výsledkem tohoto kroku.
- Attribute_name se vyřeší jako type_name (§7.8), s výjimkou jakýchkoli chyb. Pokud je toto řešení úspěšné a výsledkem je typ odvozený od
Pokud přesně jeden ze dvou kroků výše vede k typu odvozeného z System.Attribute, pak tento typ je výsledkem attribute_name. V opačném případě dojde k chybě v době kompilace.
Příklad: Pokud je třída atributu nalezena s i bez této přípony, je přítomna nejednoznačnost a výsledky chyb v době kompilace. Pokud je attribute_name napsaný tak, aby jeho identifikátor nejvíce vpravo byl doslovný identifikátor (§6.4.3), pak se shoduje pouze atribut bez přípony, takže je možné takovou nejednoznačnost vyřešit. Příklad
[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 {}zobrazuje dvě třídy atributů s názvem
ExampleaExampleAttribute. Atribut[Example]je nejednoznačný, protože by mohl odkazovat na buďExampleneboExampleAttribute. Použití doslovného identifikátoru umožňuje přesné určení záměru v takových vzácných případech. Atribut[ExampleAttribute]není nejednoznačný (i když by šlo o třídu atributů s názvemExampleAttributeAttribute!). Pokud je deklarace třídyExampleodebrána, oba atributy odkazují na třídu atributu pojmenovanouExampleAttributenásledujícím způsobem:[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 {}konec příkladu
Jedná se o chybu v době kompilace, kdy se ve stejné entitě používá třída atributu s jedním použitím více než jednou.
Příklad: Příklad
[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 {}výsledkem chyby v době kompilace, protože se pokouší použít
HelpString, což je třída atributu s jedním použitím, více než jednou v deklaraciClass1.konec příkladu
Výraz E je attribute_argument_expression , pokud jsou splněny všechny následující příkazy:
- Typ je typ parametru
Eatributu (§23.2.4). - V době kompilace lze hodnotu přeložit na jednu z následujících možností
E:
Příklad:
[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; }konec příkladu
Atributy typu deklarované ve více částech jsou určeny kombinováním atributů jednotlivých částí v nezadaném pořadí. Pokud je stejný atribut umístěn na více částí, je ekvivalentní k zadání daného atributu vícekrát u typu.
Příklad: Dvě části:
[Attr1, Attr2("hello")] partial class A {} [Attr3, Attr2("goodbye")] partial class A {}jsou ekvivalentní následující jediné deklaraci:
[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")] class A {}konec příkladu
Atributy parametrů typu se stejným způsobem kombinují.
23.4 Instance atributů
23.4.1 Obecné
Instance atributu je instance, která představuje atribut za běhu. Atribut je definován pomocí třídy atributů, pozičních argumentů a pojmenovaných argumentů. Instance atributu je instance třídy atributu, která je inicializována pozičními a pojmenovanými argumenty.
Načtení instance atributu zahrnuje kompilaci i zpracování za běhu, jak je popsáno v následujících dílčích náclausech.
23.4.2 Kompilace atributu
Kompilace atributu s třídami Tatributů , , P a určená pro entitu N programu je zkompilována do sestavení E pomocí následujících kroků:
- Při kompilaci object_creation_expression nového
T(P)formuláře postupujte podle kroků pro zpracování doby kompilace. Tyto kroky buď způsobí chybu v době kompilace, nebo určí konstruktorCinstance, kterýTlze vyvolat za běhu. - Pokud
Cnemá veřejnou přístupnost, dojde k chybě v době kompilace. - Pro každou named_argument
ArgvN:- Nechejte
Nameidentifikátornamed_argument .Arg -
Nameurčí nestatické veřejné pole nebo vlastnostTpro čtení i zápis . PokudTtakové pole nebo vlastnost neobsahuje, dojde k chybě v době kompilace.
- Nechejte
- Pokud je některá z hodnot v rámci positional_argument_list
Pnebo jedné z hodnot v rámci named_argument_listNtypuSystem.Stringa hodnota není správně vytvořená podle standardu Unicode, je definována implementací, zda je zkompilovaná hodnota rovna načtené hodnotě za běhu (§23.4.3).Poznámka: Například řetězec, který obsahuje vysokou náhradní jednotku kódu UTF-16, která není okamžitě následovaná nízkou náhradní jednotkou kódu, není dobře formátovaná. koncová poznámka
- Do výstupu sestavení kompilátoru uložte následující informace (pro vytvoření instance atributu za běhu) v důsledku kompilace programu obsahujícího atribut: třída
Tatributu , konstruktorCinstance naT, positional_argument_listP, named_argument_listNa přidružená programová entitaEs hodnotami vyřešenými v době kompilace.
23.4.3 Načtení instance atributu za běhu
Pomocí termínů definovaných v §23.4.2 lze z sestavení T načíst instanci atributu reprezentovanou C, PNa Epřidruženou A k ní za běhu pomocí následujících kroků:
- Postupujte podle kroků zpracování za běhu pro spuštění object_creation_expression formuláře
new T(P)pomocí konstruktoruCinstance a hodnot určených v době kompilace. Tyto kroky buď způsobí výjimku, nebo vytvoří instanciOT. - Pro každou named_argument
ArgvNpořadí:- Nechejte
Nameidentifikátornamed_argument .ArgPokudNameneidentifikuje nestatické veřejné pole nebo vlastnostOpro čtení i zápis , vyvolá se výjimka. - Pojďme
Valuebýt výsledkem vyhodnocení attribute_argument_expression .Arg - Pokud
Nameidentifikuje pole zapnutoO, nastavte toto pole naValuehodnotu . - V opačném případě Název identifikuje vlastnost
O. Nastavte tuto vlastnost na Hodnotu. - Výsledkem je
Oinstance třídyTatributu, která byla inicializována s aP.
- Nechejte
Poznámka: Formát pro ukládání
T, ,C, ,PN(a přidružováníE) vAa mechanismus k určeníEa načteníT,C,PNzA(a proto, jak je instance atributu získána za běhu) je nad rámec této specifikace. koncová poznámka
23.5 Rezervované atributy
23.5.1 Obecné
Určitý počet atributů má vliv na jazyk nějakým způsobem. K těmto atributům patří:
-
System.AttributeUsageAttribute(§23.5.2), který slouží k popisu způsobů použití třídy atributu. -
System.Diagnostics.ConditionalAttribute(§23.5.3) je třída atributů s více použitím, která slouží k definování podmíněných metod a tříd podmíněného atributu. Tento atribut označuje podmínku testováním symbolu podmíněné kompilace. -
System.ObsoleteAttribute(§23.5.4), který slouží k označení člena jako zastaralého. -
System.Runtime.CompilerServices.AsyncMethodBuilderAttribute(§23.5.5), který slouží k vytvoření tvůrce úloh pro asynchronní metodu. -
System.Runtime.CompilerServices.CallerLineNumberAttribute(§23.5.6.2),System.Runtime.CompilerServices.CallerFilePathAttribute(§23.5.6.3) aSystem.Runtime.CompilerServices.CallerMemberNameAttribute(§23.5.6.4), které slouží k poskytování informací o volajícím kontextu volitelným parametrům. -
System.Runtime.CompilerServices.EnumeratorCancellationAttribute(§23.5.8), který slouží k určení parametru pro token zrušení v asynchronním iterátoru.
Atributy statické analýzy s možnou hodnotou null (§23.5.7) mohou zlepšit správnost upozornění generovaných pro závazky null a stavy null (§8.9.5).
Spouštěcí prostředí může poskytovat další atributy definované implementací, které ovlivňují provádění programu jazyka C#.
23.5.2 AtributUsage
AttributeUsage Atribut se používá k popisu způsobu, jakým lze třídu atributu použít.
Třída, která je zdobena AttributeUsage atributem, musí být odvozena buď System.Attributepřímo nebo nepřímo. V opačném případě dojde k chybě kompilace.
Poznámka: Příklad použití tohoto atributu naleznete v §23.2.2. koncová poznámka
23.5.3 Podmíněný atribut
23.5.3.1 Obecné
Atribut Conditional umožňuje definici podmíněné metodys a třídy podmíněného atributu.
23.5.3.2 Podmíněné metody
Metoda zdobená atributem Conditional je podmíněná metoda. Každá podmíněná metoda je tedy přidružena k symbolům podmíněné kompilace deklarovaným ve svých Conditional atributech.
Příklad:
class Eg { [Conditional("ALPHA")] [Conditional("BETA")] public static void M() { // ... } }deklaruje
Eg.Mjako podmíněnou metodu přidruženou ke dvěma symbolůmALPHApodmíněné kompilace aBETA.konec příkladu
Volání podmíněné metody je zahrnuto, pokud je v okamžiku volání definován jeden nebo více jeho přidružených symbolů podmíněné kompilace, jinak je volání vynecháno.
Podmíněná metoda podléhá následujícím omezením:
- Podmíněná metoda je metoda v class_declaration nebo struct_declaration. K chybě v době kompilace dochází v případě, že
Conditionalje atribut zadán v metodě v deklaraci rozhraní. - Podmíněná metoda nesmí být příslušenstvím vlastnosti, indexeru nebo události.
- Podmíněná metoda musí mít návratový
voidtyp . - Podmíněná metoda nesmí být označena modifikátorem
override. Podmíněnou metodu lze však označit modifikátoremvirtual. Přepsání takové metody jsou implicitně podmíněná a nesmí být explicitně označena atributemConditional. - Podmíněná metoda nesmí být implementací metody rozhraní. V opačném případě dojde k chybě kompilace.
- Parametry podmíněné metody nesmí být výstupními parametry.
Poznámka: Atributy s
AttributeUsage(§23.2.2) včetněAttributeTargets.Methodmohou být obvykle použity pro přístupové objekty vlastností, indexerů a událostí. Výše uvedená omezení zakazují toto použití atributuConditional. koncová poznámka
Kromě toho dojde k chybě kompilace v případě, že je delegát vytvořen z podmíněné metody.
Příklad: Příklad
#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(); } }deklaruje
Class1.Mjako podmíněnou metodu.Class2TestMetoda volá tuto metodu. Vzhledem k tomu, že je definován symbolDEBUGpodmíněné kompilace, pokudClass2.Testje volána, bude volatM. Pokud symbolDEBUGnebyl definován, pakClass2.Testby nebylo voláníClass1.M.konec příkladu
Je důležité pochopit, že zahrnutí nebo vyloučení volání podmíněné metody je řízeno symboly podmíněné kompilace v okamžiku volání.
Příklad: V následujícím kódu
// 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 } }třídy
Class2aClass3každá obsahují volání podmíněné metodyClass1.F, která je podmíněná na základě toho, zda je definována nebo neníDEBUGdefinována. Vzhledem k tomu, že tento symbol je definován v kontextuClass2, ale neClass3, je zahrnuta voláníFClass2, zatímco voláníFdoClass3je vynecháno.konec příkladu
Použití podmíněných metod v řetězu dědičnosti může být matoucí. Volání podmíněné metody prostřednictvím baseformuláře base.Mpodléhají normálním pravidlům volání podmíněné metody.
Příklad: V následujícím kódu
// 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 } }
Class2obsahuje voláníMdefinované v její základní třídě. Toto volání je vynecháno, protože základní metoda je podmíněná na základě přítomnosti symboluDEBUG, který není definován. Proto metoda zapisuje pouze do konzoly "Class2.M executed". Uvážlivé použití pp_declarations může tyto problémy odstranit.konec příkladu
23.5.3.3 Třídy podmíněného atributu
Třída atributu (§23.2) zdobená jedním nebo více Conditional atributy je třída podmíněného atributu. Podmíněná třída atributu je tedy přidružena k symbolům podmíněné kompilace deklarovaným v jeho Conditional atributech.
Příklad:
[Conditional("ALPHA")] [Conditional("BETA")] public class TestAttribute : Attribute {}deklaruje
TestAttributejako třídu podmíněného atributu asociované se symboly podmíněných kompilacíALPHAaBETA.konec příkladu
Specifikace atributů (§23.3) podmíněného atributu jsou zahrnuty, pokud je v místě specifikace definován jeden nebo více jeho přidružených symbolů podmíněné kompilace, jinak je specifikace atributu vynechána.
Je důležité si uvědomit, že zahrnutí nebo vyloučení specifikace atributu třídy podmíněného atributu je řízeno podmíněnými symboly kompilace v okamžiku specifikace.
Příklad: V příkladu
// 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 {}třídy
Class1aClass2jsou každý zdoben atributemTest, který je podmíněný na základě toho, zda je definován nebo neníDEBUGdefinován. Vzhledem k tomu, že tento symbol je definován v kontextuClass1, ale neClass2, specifikace test atributuClass1je zahrnuta, zatímco specifikace atributuTestonClass2je vynechána.konec příkladu
23.5.4 Zastaralý atribut
Obsolete Atribut se používá k označení typů a členů typů, které by se už neměly používat.
Pokud program používá typ nebo člen, který je zdoben atributem Obsolete, kompilátor vydá upozornění nebo chybu. Konkrétně kompilátor vydá upozornění, pokud není zadaný žádný parametr chyby, nebo pokud je zadaný parametr chyby a má hodnotu false. Kompilátor vydá chybu, pokud je zadán parametr chyby a má hodnotu true.
Příklad: V následujícím kódu
[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(); } }třída
Aje zdobena atributemObsolete. Každé použitíAvMaindůsledku toho způsobí upozornění, které obsahuje zadanou zprávu: "Tato třída je zastaralá; použít místo toho tříduB".konec příkladu
23.5.5 Atribut AsyncMethodBuilder
Tento atribut je popsán v §15.14.1.
23.5.6 Atributy informace o volajícím
23.5.6.1 Obecné
Pro účely, jako je protokolování a generování sestav, je někdy užitečné, aby člen funkce získal určité informace o době kompilace o volajícím kódu. Atributy informací o volajícím poskytují způsob, jak tyto informace transparentně předat.
Pokud je volitelný parametr anotován jedním z atributů caller-info, vynechání odpovídajícího argumentu ve volání nemusí nutně způsobit nahrazení výchozí hodnoty parametru. Místo toho, pokud jsou k dispozici zadané informace o volajícím kontextu, předají se jako hodnota argumentu.
Příklad:
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); }Volání
Log()bez argumentů by vytisklo číslo řádku a cestu k souboru volání a také název člena, ve kterém došlo k volání.konec příkladu
Atributy informace o volajícím můžou nastat na volitelných parametrech kdekoli, včetně delegování deklarací. Konkrétní atributy volajícího-info však mají omezení na typy parametrů, které mohou atributovat, takže vždy bude existovat implicitní převod z nahrazené hodnoty na typ parametru.
Jedná se o chybu, která má stejný atribut caller-info pro parametr definování i implementaci části deklarace částečné metody. Použijí se pouze atributy informací o volajícím v definující části, zatímco atributy informací o volajícím, ke kterým dochází pouze v implementující části, se ignorují.
Informace o volajícím nemají vliv na rozlišení přetížení. Vzhledem k tomu, že atributy volitelné parametry jsou stále vynechány ze zdrojového kódu volajícího, rozlišení přetížení ignoruje tyto parametry stejným způsobem, jakým ignoruje jiné nepovinné parametry (§12.6.4).
Informace o volajícím se nahradí pouze v případech, kdy je funkce explicitně vyvolána ve zdrojovém kódu. Implicitní vyvolání, jako jsou implicitní volání nadřazeného konstruktoru, nemají zdrojové umístění a nenahrazují informace o volajícím. Volání dynamicky svázaná také nenahrazují informace o volajícím. Pokud je v takových případech vynechán parametr s atributem caller-info, použije se místo toho zadaná výchozí hodnota parametru.
Jednou výjimkou jsou výrazy dotazu. Jsou považovány za syntaktické rozšíření a pokud volání rozbalí, aby vynechaly volitelné parametry s atributy volajícího-informace, budou informace volajícího nahrazeny. Použité umístění je umístění klauzule dotazu, ze které bylo volání vygenerováno.
Pokud je pro daný parametr zadáno více než jeden atribut informace o volajícím, jsou rozpoznány v následujícím pořadí: CallerLineNumber, , CallerFilePathCallerMemberName. Představte si následující deklaraci parametru:
[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ...
CallerLineNumber má přednost a ostatní dva atributy jsou ignorovány. Pokud CallerLineNumber by byl vynechán, CallerFilePath měl by přednost a CallerMemberName byl by ignorován. Lexikální řazení těchto atributů je irelevantní.
23.5.6.2 Atribut CallerLineNumber
Atribut System.Runtime.CompilerServices.CallerLineNumberAttribute je povolen pro volitelné parametry, pokud existuje standardní implicitní převod (§10.4.2) z konstantní hodnoty int.MaxValue na typ parametru. Tím zajistíte, že jakékoli nezáporné číslo řádku až do této hodnoty bude možné předat bez chyby.
Pokud funkce vyvolání z umístění ve zdrojovém kódu vynechá volitelný parametr s parametrem CallerLineNumberAttribute, číselný literál představující číslo řádku tohoto umístění se použije jako argument pro vyvolání místo výchozí hodnoty parametru.
Pokud vyvolání zahrnuje více řádků, zvolená čára je závislá na implementaci.
Číslo řádku může být ovlivněno direktivou #line (§6.5.8).
23.5.6.3 Atribut CallerFilePath
System.Runtime.CompilerServices.CallerFilePathAttribute Atribut je povolen pro volitelné parametry, pokud existuje standardní implicitní převod (§10.4.2) od string typu parametru.
Pokud funkce vyvolání z umístění ve zdrojovém kódu vynechá volitelný parametr s parametrem CallerFilePathAttribute, pak řetězcový literál představující cestu k souboru umístění se použije jako argument pro vyvolání místo výchozí hodnoty parametru.
Formát cesty k souboru je závislý na implementaci.
Cesta k souboru může být ovlivněna direktivou #line (§6.5.8).
23.5.6.4 Atribut CallerMemberName
System.Runtime.CompilerServices.CallerMemberNameAttribute Atribut je povolen pro volitelné parametry, pokud existuje standardní implicitní převod (§10.4.2) od string typu parametru.
Pokud vyvolání funkce z umístění v těle člena funkce nebo v atributu použitém pro samotný člen funkce nebo jeho návratový typ, parametry nebo parametry typu ve zdrojovém kódu vynechá volitelný parametr s CallerMemberNameAttributeřetězcový literál představující název tohoto členu se použije jako argument pro vyvolání místo výchozí hodnoty parametru.
Pro vyvolání, ke kterým dochází v rámci obecných metod, se použije pouze samotný název metody bez seznamu parametrů typu.
Pro vyvolání, ke kterým dochází v rámci explicitních implementací členů rozhraní, se použije pouze samotný název metody bez předchozí kvalifikace rozhraní.
Pro vyvolání, ke kterým dochází v rámci vlastnosti nebo přístupových objektů událostí, je použitý název členu vlastnost nebo samotná událost.
Pro vyvolání, ke kterým dochází v rámci přístupových objektů indexeru, je použitý název členu zadaný (IndexerNameAttribute§23.6) na členu indexeru, pokud existuje, nebo výchozí název Item jinak.
Pro vyvolání, ke kterým dochází v rámci inicializátorů polí nebo událostí, je použitý název členu název pole nebo události, která se inicializuje.
Pro vyvolání, ke kterým dochází v rámci deklarací konstruktorů instancí, statických konstruktorů, finalizátorů a operátorů, je použitý název členu závislý na implementaci.
23.5.7 Atributy analýzy kódu
23.5.7.1 Obecné
Atributy v tomto pododdílu slouží k poskytování dalších informací k podpoření kompilátoru, díky nimž je umožněna diagnostika ohledně nulovatelnosti a stavu nulové hodnoty (§8.9.5). Kompilátor není nutný k provedení diagnostiky stavu null. Přítomnost nebo absence těchto atributů neovlivňuje jazyk ani chování programu. Kompilátor, který neposkytuje diagnostiku stavu null, musí číst a ignorovat přítomnost těchto atributů. Kompilátor, který poskytuje diagnostiku stavu null, použije význam definovaný v této podklize pro kterýkoli z těchto atributů, které používá k informování své diagnostiky.
Atributy analýzy kódu jsou deklarovány v oboru názvů System.Diagnostics.CodeAnalysis.
| Atribut | Význam |
|---|---|
AllowNull (§23.5.7.2) |
Argument, který není null, může mít hodnotu null. |
DisallowNull (§23.5.7.3) |
Argument s možnou hodnotou null by nikdy neměl být null. |
MaybeNull (§23.5.7.6) |
Návratová hodnota, která není null, může mít hodnotu null. |
NotNull (§23.5.7.8) |
Návratová hodnota s možnou hodnotou null nikdy nebude null. |
MaybeNullWhen (§23.5.7.7) |
Pokud metoda vrátí zadanou bool hodnotu, může být argument nenulový. |
NotNullWhen (§23.5.7.10) |
Argument s možnou hodnotou null nebude null, pokud metoda vrátí zadanou bool hodnotu. |
NotNullIfNotNull (§23.5.7.9) |
Návratová hodnota není null, pokud argument pro zadaný parametr nemá hodnotu null. |
DoesNotReturn (§23.5.7.4) |
Tato metoda nikdy nevrátí. |
DoesNotReturnIf (§23.5.7.5) |
Tato metoda nikdy nevrátí, pokud přidružený bool parametr má zadanou hodnotu. |
Následující dílčí části v §23.5.7.1 jsou podmíněně normativní.
23.5.7.2 Atribut AllowNull
Určuje, že hodnota null je povolena jako vstup, i když odpovídající typ tuto hodnotu zakáže.
Příklad: Vezměte v úvahu následující vlastnost pro čtení a zápis, která se nikdy nevrátí
null, protože má rozumnou výchozí hodnotu. Uživatel však může dát hodnotu null objektu set, který nastaví vlastnost na tuto výchozí hodnotu.#nullable enable public class X { [AllowNull] public string ScreenName { get => _screenName; set => _screenName = value ?? GenerateRandomScreenName(); } private string _screenName = GenerateRandomScreenName(); private static string GenerateRandomScreenName() => ...; }Vzhledem k následujícímu použití přístupového objektu sady této vlastnosti
var v = new X(); v.ScreenName = null; // may warn without attribute AllowNullBez atributu může kompilátor vygenerovat upozornění, protože vlastnost s ne-nullovými typy se zdá být nastavena na hodnotu null. Přítomnost atributu potlačí toto upozornění. konec příkladu
23.5.7.3 Atribut DisallowNull
Určuje, že hodnota null je zakázána jako vstup, i když to odpovídající typ umožňuje.
Příklad: Vezměte v úvahu následující vlastnost, ve které je null výchozí hodnota, ale klienti ji mohou nastavit pouze na hodnotu, která není 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; }Přístupový objekt get by mohl vrátit výchozí hodnotu
null, takže kompilátor může upozornit, že musí být zkontrolován před přístupem. Kromě toho varuje volající, že i když může mít hodnotu null, volající by ji neměl explicitně nastavit na hodnotu null. konec příkladu
23.5.7.4 Atribut DoesNotReturn
Určuje, že daná metoda nikdy nevrátí.
Příklad: Zvažte následující:
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; }Přítomnost atributu pomáhá kompilátoru mnoha způsoby. Nejprve může kompilátor vydat upozornění, pokud existuje cesta, kde metoda může ukončit bez vyvolání výjimky. Za druhé může kompilátor potlačit upozornění s možnou hodnotou null v jakémkoli kódu po volání této metody, dokud se nenajde příslušná klauzule catch. Za třetí, nedostupný kód nebude mít vliv na žádné stavy null.
Atribut nemění dosažitelnost (§13.2) ani určitou analýzu přiřazení (§9.4) na základě přítomnosti tohoto atributu. Používá se jenom k ovlivnění upozornění s nulovou dostupností. konec příkladu
23.5.7.5 Atribut DoesNotReturnIf
Určuje, že daná metoda nikdy nevrátí, pokud přidružený bool parametr má zadanou hodnotu.
Příklad: Zvažte následující:
#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!; }konec příkladu
23.5.7.6 Atribut MaybeNull
Určuje, že návratová hodnota, která není null, může mít hodnotu null.
Příklad: Zvažte následující obecnou metodu:
#nullable enable public T? Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }Myšlenka tohoto kódu je, že pokud
Tje nahrazenastring,T?stane se nullable anotace. Tento kód však není právní, protožeTnení omezen na odkazový typ. Přidáním tohoto atributu ale problém vyřešíte:#nullable enable [return: MaybeNull] public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }Atribut informuje volající, že kontrakt znamená non-nullable typ, ale návratová hodnota může být
nullskutečně . konec příkladu
23.5.7.7 Atribut MaybeNullWhen
Určuje, že nenulový argument může být null , když metoda vrátí zadanou bool hodnotu. Podobá se atributu MaybeNull (§23.5.7.6), ale obsahuje parametr pro zadanou návratovou hodnotu.
23.5.7.8 Atribut NotNull
Určuje, že hodnota s možnou hodnotou null nikdy nebude null , pokud metoda vrátí (místo vyvolání).
Příklad: Zvažte následující:
#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); }Pokud jsou povoleny odkazové typy s možnou hodnotou null, metoda
ThrowWhenNullse zkompiluje bez upozornění. Pokud tato metoda vrátí,valueargument je zaručen, že nenínull. Je však přijatelné volatThrowWhenNulls nulovým odkazem. konec příkladu
23.5.7.9 Atribut NotNullIfNotNull
Určuje, že návratová hodnota není null , pokud argument pro zadaný parametr není null.
Příklad: Stav null návratové hodnoty může záviset na stavu null jednoho nebo více argumentů. Chcete-li pomoci s analýzou kompilátoru, můžete použít atribut
null, když metoda vždy vrátí nenulovou hodnotu, pokud určité argumenty nejsouNotNullIfNotNull. Zvažte následující metodu:#nullable enable string GetTopLevelDomainFromFullUrl(string url) { ... }
urlPokud argument nenínull,nullnevrátí se. Pokud jsou povoleny odkazy s možnou hodnotou null, funguje tento podpis správně, pokud rozhraní API nikdy nepřijímá argument null. Pokud však argument může mít hodnotu null, může být vrácená hodnota také null. Chcete-li tento kontrakt správně vyjádřit, anotace této metody následujícím způsobem:#nullable enable [return: NotNullIfNotNull("url")] string? GetTopLevelDomainFromFullUrl(string? url) { ... }konec příkladu
23.5.7.10 Atribut NotNullWhen
Určuje, že argument s možnou hodnotou null nebude null , když metoda vrátí zadanou bool hodnotu.
Příklad: Metoda
String.IsNullOrEmpty(String)knihovny vrátítrue, pokud jenullargument nebo prázdný řetězec. Jedná se o formu kontroly null: Volající nemusí argument null-check argument, pokud metoda vrátífalse. Pokud chcete vytvořit metodu, jako je tato s možnou hodnotou null, nastavte typ parametru jako typ odkazu s možnou hodnotou null a přidejte atribut NotNullWhen:#nullable enable bool IsNullOrEmpty([NotNullWhen(false)] string? value) { ... }konec příkladu
23.5.8 Atribut EnumeratorCancellation
Určuje parametr představující CancellationToken asynchronní iterátor (§15.15). Argument pro tento parametr se zkombinuje s argumentem předaným IAsyncEnumerable<T>.GetAsyncEnumerator(CancellationToken). Tento kombinovaný token je dotazován IAsyncEnumerator<T>.MoveNextAsync()§15.15.5.2). Tokeny se zkombinují do jednoho tokenu, jako by CancellationToken.CreateLinkedTokenSource a jeho Token vlastnost. Kombinované tokeny budou zrušeny, pokud dojde ke zrušení některého ze dvou zdrojových tokenů. Kombinovaný token se považuje za argument asynchronní metody iterátoru (§15.15) v těle této metody.
Jedná se o chybu, pokud System.Runtime.CompilerServices.EnumeratorCancellation se atribut použije na více než jeden parametr. Kompilátor může vygenerovat upozornění, pokud:
- Atribut
EnumeratorCancellationse použije na parametr jiného typu nežCancellationToken, - nebo je-li
EnumeratorCancellationatribut použit na parametr metody, která není asynchronním iterátorem (§15.15), - nebo je-li
EnumeratorCancellationatribut použit na parametr metody, která vrací asynchronní výčet rozhraní (§15.15.3) namísto asynchronního rozhraní enumerátoru (§15.15.2).
Iterátor nebude mít přístup k argumentu CancellationToken , pokud GetAsyncEnumerator tento parametr nemají žádné atributy.
Příklad: Metoda
GetStringsAsync()je asynchronní iterátor. Než provedete jakoukoli práci na načtení další hodnoty, zkontroluje token zrušení a určí, jestli má být iterace zrušena. Pokud je požadováno zrušení, nebude provedena žádná další akce.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(); } }konec příkladu
23.6 Atributy pro spolupráci
Pro spolupráci s jinými jazyky může být indexer implementován pomocí indexovaných vlastností. Pokud pro indexer neexistuje žádný IndexerName atribut, použije se název Item ve výchozím nastavení. Tento IndexerName atribut umožňuje vývojáři přepsat toto výchozí nastavení a zadat jiný název.
Příklad: Ve výchozím nastavení je
Itemnázev indexeru . Můžete ho přepsat následujícím způsobem:[System.Runtime.CompilerServices.IndexerName("TheItem")] public int this[int index] { get { ... } set { ... } }Teď je název
TheItemindexeru .konec příkladu
ECMA C# draft specification