Sdílet prostřednictvím


22 Atributy

22.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 HelpAttribute atribut, 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ů (§22.2), které mohou mít poziční a pojmenované parametry (§22.2.3). Atributy jsou připojeny k entitám v programu jazyka C# pomocí specifikací atributů (§22.3) a lze je načíst za běhu jako instance atributů (§22.4).

22.2 Třídy atributů

22.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 attribute

end example

22.2.2 Použití atributů

Atribut AttributeUsage (§22.5.2) slouží k popisu způsobu použití třídy atributů.

AttributeUsage má poziční parametr (§22.2.3), který umožňuje třídě atributů 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í atributu Simple .

[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 atributu Attribute může být přípona vynechána, což vede k krátkému názvu Simple. Výše uvedený příklad je tedy sémanticky ekvivalentní následujícímu:

[SimpleAttribute] class Class1 {...}
[SimpleAttribute] interface Interface1 {...}

end example

AttributeUsage má pojmenovaný parametr (§22.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 AuthorAttribute a zobrazuje deklaraci třídy se dvěma použitími atributu Author :

[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 
{
    ...
}

end example

AttributeUsage má jiný pojmenovaný parametr (§22.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 { ... }

22.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, HelpAttribute která má jeden poziční parametr url, a jeden pojmenovaný parametr, Topic. I když není statický a veřejný, vlastnost Url nedefinuje 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
{
}

end example

22.2.4 Typy parametrů atributů

Typy pozičních a pojmenovaných parametrů pro třídu atributů jsou omezené na typy parametrů atributů, 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 22.3

Specifikace atributu je použití dříve definovaného atributu pro entitu programu. Atribut je část dalších deklarativních informací určených pro entitu programu. Atributy lze zadat v globálním rozsahu (pro určení atributů pro obsahující sestavení nebo modul) a pro type_declaration s (§14.7), class_member_declarations (§15.3), interface_member_declarations (§§15.3) 18.4), struct_member_declarations (§16.3), enum_member_declarations (§19.2), accessor_declarations (§15.7.3), event_accessor_ prohlášení (§15.8), prvky parameter_lists (§15.6.2) a prvky type_parameter_lists (§15.2.3).

Atributy jsou zadané v oddílech atributů. 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 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_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.Attribute tohoto typu, je výsledkem tohoto kroku.
    • Znaky Attribute jsou 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ý od System.Attribute tohoto typu, je výsledkem tohoto kroku.

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 Example a ExampleAttribute. Atribut [Example] je nejednoznačný, protože by mohl odkazovat na buď Example nebo ExampleAttribute. 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 se jednalo o třídu atributu s názvem ExampleAttributeAttribute!). Pokud je deklarace třídy Example odebrána, oba atributy odkazují na třídu atributu pojmenovanou ExampleAttributená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 {}

end example

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 deklaraci Class1.

end example

Výraz E je attribute_argument_expression , pokud jsou splněny všechny následující příkazy:

  • Typ je typ parametru E atributu (§22.2.4).
  • V době kompilace lze hodnotu přeložit na jednu z následujících možností E :
    • Konstantní hodnota.
    • Objekt System.Type získaný pomocí typeof_expression (§12.8.18) určující ne generický typ, uzavřený konstruovaný typ (§8.4.3) nebo nevázaný obecný typ (§8.4.4), ale ne otevřený typ (§8.4.3).
    • Jednorozměrné pole attribute_argument_expressions.

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;
}

end example

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 {}

end example

Atributy parametrů typu se stejným způsobem kombinují.

22.4 Instance atributů

22.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.

22.4.2 Kompilace atributu

Kompilace atributu s třídami Tatributů , P , named_argument_listN a určená pro entitu E programu je zkompilována do sestavení A 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čí konstruktor C instance, který T lze vyvolat za běhu.
  • Pokud C nemá veřejnou přístupnost, dojde k chybě v době kompilace.
  • Pro každou named_argumentArg v N:
    • Nechejte Name identifikátornamed_argument .Arg
    • Name určí nestatické veřejné pole nebo vlastnost Tpro čtení i zápis . Pokud T takové pole nebo vlastnost neobsahuje, dojde k chybě v době kompilace.
  • Pokud je některá z hodnot v rámci positional_argument_listP nebo jedné z hodnot v rámci named_argument_listN typu System.String a hodnota není správně vytvořená podle standardu Unicode, je definována implementací, zda je zkompilovaná hodnota rovna načtené hodnotě za běhu (§22.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 , konstruktor C instance na T, positional_argument_listP, named_argument_listN a přidružená programová entita Es hodnotami vyřešenými v době kompilace.

22.4.3 Načtení instance atributu za běhu

Pomocí termínů definovaných v §22.4.2 lze z sestavení T načíst instanci atributu reprezentovanou parametrem C, PN, a Epřidružené 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í konstruktoru C instance a hodnot určených v době kompilace. Tyto kroky buď způsobí výjimku, nebo vytvoří instanci OT.
  • Pro každou named_argumentArg v Npořadí:
    • Nechejte Name identifikátornamed_argument .Arg Pokud Name neidentifikuje nestatické veřejné pole nebo vlastnost Opro čtení i zápis , vyvolá se výjimka.
    • Pojďme Value být výsledkem vyhodnocení attribute_argument_expression .Arg
    • Pokud Name identifikuje pole zapnuto O, nastavte toto pole na Valuehodnotu .
    • V opačném případě Název identifikuje vlastnost O. Nastavte tuto vlastnost na Hodnotu.
    • Výsledkem je Oinstance třídy T atributu, která byla inicializována s P a named_argument_listN.

Poznámka: Formát pro ukládání T, , C, , PN (a přidružování E) v A a mechanismus k určení E a načtení T, C, PN z A (a proto, jak je instance atributu získána za běhu) je nad rámec této specifikace. koncová poznámka

Příklad: V implementaci rozhraní příkazového řádku Help lze instance atributů v sestavení vytvořené kompilací ukázkového programu v §22.2.3 načíst pomocí následujícího programu:

public sealed class InterrogateHelpUrls
{
    public static void Main(string[] args)
    {
        Type helpType = typeof(HelpAttribute);
        string assemblyName = args[0];
        foreach (Type t in Assembly.Load(assemblyName).GetTypes()) 
        {
            Console.WriteLine($"Type : {t}");
            var attributes = t.GetCustomAttributes(helpType, false);
            var helpers = (HelpAttribute[]) attributes;
            foreach (var helper in helpers)
            {
                Console.WriteLine($"\tUrl : {helper.Url}");
            }
        }
    }
}

end example

22.5 Rezervované atributy

22.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 (§22.5.2), který se používá k popisu způsobů použití třídy atributu.
  • System.Diagnostics.ConditionalAttribute (§22.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 (§22.5.4), který slouží k označení člena jako zastaralého.
  • System.Runtime.CompilerServices.AsyncMethodBuilderAttribute (§22.5.5), který slouží k vytvoření tvůrce úloh pro asynchronní metodu.
  • System.Runtime.CompilerServices.CallerLineNumberAttribute (§22.5.6.2), System.Runtime.CompilerServices.CallerFilePathAttribute (§22.5.6.3) a System.Runtime.CompilerServices.CallerMemberNameAttribute (§22.5.6.4), které slouží k poskytování informací o volajícím kontextu volitelným parametrům.

Atributy statické analýzy s možnou hodnotou null (§22.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#.

22.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 § 22.2.2. koncová poznámka

22.5.3 Podmíněný atribut

22.5.3.1 Obecné

Atribut Conditional umožňuje definici podmíněných metod a tříd podmíněného atributu.

22.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.M jako podmíněnou metodu přidruženou ke dvěma symbolům ALPHA podmíněné kompilace a BETA.

end example

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 Conditional je atribut zadán v metodě v deklaraci rozhraní.
  • 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átorem virtual . Přepsání takové metody jsou implicitně podmíněná a nesmí být explicitně označena atributem Conditional .
  • 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.

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.M jako podmíněnou metodu. Class2 Test Metoda volá tuto metodu. Vzhledem k tomu, že je definován symbol DEBUG podmíněné kompilace, pokud Class2.Test je volána, bude volat M. Pokud symbol DEBUG nebyl definován, pak Class2.Test by nebylo volání Class1.M.

end example

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 Class2 a Class3 každá obsahují volání podmíněné metody Class1.F, která je podmíněná na základě toho, zda je definována nebo není DEBUG definována. Vzhledem k tomu, že tento symbol je definován v kontextu Class2 , ale ne Class3, je zahrnuta volání FClass2 , zatímco volání F do Class3 je vynecháno.

end example

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

Class2 obsahuje volání M definované 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 symbolu DEBUG, 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.

end example

22.5.3.3 Třídy podmíněného atributu

Třída atributu (§22.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 TestAttribute jako třídu podmíněného atributu asociované se symboly podmíněných kompilací ALPHA a BETA.

end example

Specifikace atributů (§22.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 Class1 a Class2 jsou každý zdoben atributem Test, který je podmíněný na základě toho, zda je definován nebo není DEBUG definován. Vzhledem k tomu, že tento symbol je definován v kontextu Class1 , ale ne Class2, specifikace test atributu Class1 je zahrnuta, zatímco specifikace atributu Test on Class2 je vynechána.

end example

22.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 A je zdobena atributem Obsolete . Každé použití A v Main důsledku toho způsobí upozornění, které obsahuje zadanou zprávu: "Tato třída je zastaralá; použít místo toho třídu B ".

end example

22.5.5 Atribut AsyncMethodBuilder

Tento atribut je popsán v §15.15.1.

22.5.6 Atributy informace o volajícím

22.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í.

end example

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í.

22.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).

22.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).

22.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 člena zadaný (IndexerNameAttribute§22.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.

22.5.7 Atributy analýzy kódu

22.5.7.1 Obecné

Atributy v této části slouží k poskytování dalších informací pro podporu kompilátoru, který poskytuje diagnostiku stavu null a stavu null (§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 části 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 (§22.5.7.2) Argument, který není null, může mít hodnotu null.
DisallowNull (§22.5.7.3) Argument s možnou hodnotou null by nikdy neměl být null.
MaybeNull (§22.5.7.6) Návratová hodnota, která není null, může mít hodnotu null.
NotNull (§22.5.7.8) Návratová hodnota s možnou hodnotou null nikdy nebude null.
MaybeNullWhen (§22.5.7.7) Pokud metoda vrátí zadanou bool hodnotu, může být argument nenulový.
NotNullWhen (§22.5.7.10) Argument s možnou hodnotou null nebude null, pokud metoda vrátí zadanou bool hodnotu.
NotNullIfNotNull (§22.5.7.9) Návratová hodnota není null, pokud argument pro zadaný parametr nemá hodnotu null.
DoesNotReturn (§22.5.7.4) Tato metoda nikdy nevrátí.
DoesNotReturnIf (§22.5.7.5) Tato metoda nikdy nevrátí, pokud přidružený bool parametr má zadanou hodnotu.

Následující části § 22.5.7.1 jsou podmíněně normativní.

22.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 AllowNull

Bez 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í. end example

22.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. end example

22.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í. end example

22.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!;
}

end example

22.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 T je nahrazena string, T? stane se nullable anotace. Tento kód však není právní, protože T není 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ě . end example

22.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 (§22.5.7.6), ale obsahuje parametr pro zadanou návratovou hodnotu.

22.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 null, metoda ThrowWhenNull se zkompiluje bez upozornění. Pokud tato metoda vrátí, value argument je zaručen, že není null. Je však přijatelné volat ThrowWhenNull s nulovým odkazem. end example

22.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 NotNullIfNotNull, když metoda vždy vrátí nenulovou hodnotu, pokud určité argumenty nejsou null. Zvažte následující metodu:

#nullable enable
string GetTopLevelDomainFromFullUrl(string url) { ... }

url Pokud argument není null, null nevrá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) { ... }

end example

22.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 je null argument 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) { ... }

end example

22.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 .

end example