23 Attribútumok

23.1 Általános

A C#-nyelv nagy része lehetővé teszi, hogy a programozó deklaratív információkat adjon meg a programban meghatározott entitásokról. Egy osztályban például egy metódus akadálymentességét úgy adhatja meg, hogy az method_modifiers public, protected, internalés private.

A C# lehetővé teszi a programozók számára, hogy új deklaratív információkat, úgynevezett attribútumokattaláljanak ki. A programozók ezután attribútumokat csatolhatnak a különböző programentitásokhoz, és lekérhetik az attribútuminformációkat egy futásidejű környezetben.

Megjegyzés: Előfordulhat például, hogy egy keretrendszer meghatároz egy HelpAttribute attribútumot, amely elhelyezhető bizonyos programelemeken (például osztályokon és metódusokon), hogy megfeleltetést biztosítson ezekből a programelemekből a dokumentációjukba. végjegyzet

Az attribútumokat az attribútumosztályok deklarációja (23.2. §) határozza meg, amelyek helymeghatározó és elnevezett paraméterekkel rendelkezhetnek (23.2.3. §). Az attribútumok attribútumspecifikációk (23.3.§) használatával kapcsolódnak egy C#-program entitásaihoz, és futtatáskor attribútumpéldányokként kérhetők le (23.4. §).

23.2 Attribútumosztályok

23.2.1 Általános

Az absztrakt osztályból System.Attributeszármazó osztály – akár közvetlenül, akár közvetve – attribútumosztály. Az attribútumosztály deklarálása egy új típusú attribútumot határoz meg, amely programentitásokon helyezhető el. Konvenció szerint az attribútumosztályok neve a következő utótaggal Attributevan elnevezve: . Az attribútumok használata magában foglalhatja vagy kihagyhatja ezt az utótagot.

Az általános osztály deklarációja nem használható System.Attribute közvetlen vagy közvetett alaposztályként.

Példa:

public class B : Attribute {}
public class C<T> : B {} // Error – generic cannot be an attribute

záró példa

23.2.2 Attribútumhasználat

Az attribútum AttributeUsage (23.5.2. §) az attribútumosztályok felhasználási módjának leírására szolgál.

AttributeUsage rendelkezik egy pozícióparaméterrel (23.2.3. §), amely lehetővé teszi, hogy az attribútumosztály meghatározza azokat a programentitások típusait, amelyeken használható.

Példa: Az alábbi példa egy attribútumosztályt SimpleAttribute határoz meg, amely csak class_declaration s és interface_declarations-n helyezhető el, és az Simple attribútum több használatát is megjeleníti.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class SimpleAttribute : Attribute
{ 
    ... 
}

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

Bár ez az attribútum a névvel SimpleAttributevan definiálva , az attribútum használatakor előfordulhat, hogy az Attribute utótag hiányzik, ami a rövid nevet Simpleeredményezi. Így a fenti példa szemantikailag egyenértékű a következő

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

záró példa

AttributeUsage nevű paramétere (23.2.3. §), AllowMultipleamely azt jelzi, hogy az attribútum egy adott entitáshoz többször is megadható-e. Ha AllowMultiple egy attribútumosztály értéke igaz, akkor az attribútumosztály egy többfelhasználós attribútumosztály, amely egy entitáson többször is megadható. Ha AllowMultiple egy attribútumosztály értéke hamis vagy nincs meghatározva, akkor az attribútumosztály egy egyszer használatos attribútumosztály, és legfeljebb egyszer adható meg egy entitáson.

Példa: Az alábbi példa egy elnevezett AuthorAttribute többfelhasználós attribútumosztályt határoz meg, és egy osztálydeklarációt jelenít meg az Author attribútum két használatával:

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

záró példa

AttributeUsage van egy másik elnevezett paramétere (23.2.3. §), Inheritedamely azt jelzi, hogy az attribútumot az alaposztályban megadottak is öröklik-e az adott alaposztályból származó osztályok. Ha Inherited egy attribútumosztály értéke igaz, akkor az attribútum öröklődik. Ha Inherited egy attribútumosztály hamis, akkor az attribútum nem öröklődik. Ha nincs meghatározva, az alapértelmezett értéke igaz.

Olyan attribútumosztály X , amelyhez AttributeUsage nincs attribútum csatolva, mint a

class X : Attribute { ... }

egyenértékű a következő értékével:

[AttributeUsage(
   AttributeTargets.All,
   AllowMultiple = false,
   Inherited = true)
]
class X : Attribute { ... }

23.2.3 Pozíció- és elnevezett paraméterek

Az attribútumosztályok s pozícióparamétertés elnevezett paraméterttartalmazhatnak. Az attribútumosztály minden nyilvános példány-konstruktora az adott attribútumosztályhoz tartozó pozícióparaméterek érvényes sorozatát határozza meg. Az attribútumosztály minden nem statikus nyilvános olvasási-írási mezője és nem statikus nyilvános írási vagy olvasási init tulajdonsága egy elnevezett paramétert határoz meg az attribútumosztályhoz. Ahhoz, hogy egy tulajdonság megnevezett paramétert határozzon meg, az adott tulajdonságnak nyilvános lekéréses tartozékot és nyilvános készletet vagy init tartozékot is tartalmaznia kell.

Példa: Az alábbi példa egy egy pozícióparamétert HelpAttributeés egy elnevezett paramétert urltartalmazó attribútumosztályt Topic határoz meg. Bár nem statikus és nyilvános, a tulajdonság Url nem definiál elnevezett paramétert, mivel nem írási vagy olvasási init. Az attribútum két felhasználási módja is látható:

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

záró példa

23.2.4 Attribútumparaméter-típusok

Az attribútumosztály helymeghatározó és elnevezett paramétereinek típusai az s attribútumparaméter-típusrakorlátozódnak, amelyek a következők:

  • Az alábbi típusok egyike: , , , , , bool, bytechar, double, floatint, , long, . sbyteshortstringuintulongushort
  • A típus object.
  • A típus System.Type.
  • Enumerálási típusok.
  • A fenti típusok egydimenziós tömbjei.
  • Egy konstruktor argumentum vagy nyilvános mező, amely nem rendelkezik ilyen típussal, nem használható pozíció- vagy elnevezett paraméterként az attribútum specifikációjában.

23.3 Attribútum specifikációja

A korábban definiált attribútumokat attribútumspecifikációnak nevezzük. Az attribútum egy programentitáshoz megadott további deklaratív információ. Az attribútumok a globális hatókörben (a szerelvény vagy modul attribútumainak meghatározásához) és type_declarations (14.8.§), class_member_declarations (15.3. §), interface_member_declarations (19. §) esetében adhatók meg. 4), struct_member_declarations (16.3. §), enum_member_declarations (20.2. §), accessor_declarations (15.7.3. §), event_accessor_declarations (15.8. §)), local_function_declarations (13.6.4. §), parameter_lists elemei (15.6.2. §), type_parameter_listelemei (15.2.3.§), lambda_expressions (12.22.1. §), valamint explicit_anonymous_function_parameterés implicit_anonymous_function_parameterelemei (12.22.1. §).

Az attribútumok az s attribútumszakaszbanvannak megadva. Az attribútumszakaszok szögletes zárójelekből állnak, amelyek egy vagy több attribútum vesszővel tagolt listáját veszik körül. Az attribútumok ilyen listában való megadásának sorrendje, valamint az a sorrend, amelyben az adott programentitásához csatolt szakaszok rendezve vannak, nem jelentős. Az attribútum-specifikációk például egyenértékűek[A][B][B][A][A, B][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
    ;

Az éles global_attribute_target és az alábbi szövegben az azonosítónak a assemblymodule6.4.3. Az éles attribute_target és az alábbi szövegben az azonosítónak olyan helyesírással kell rendelkeznie, amely nem egyenlő assembly vagy module– az egyenlőség fenti definícióját használva .

Az attribútumok attribute_name és a pozíció- és elnevezett argumentumok választható listájából állnak. A pozícióargumentumok (ha vannak ilyenek) megelőzik az elnevezett argumentumokat. A pozícióargumentumok egy attribute_argument_expression, egy elnevezett argumentum egy névből, majd egy egyenlőségjelből állnak, majd egy attribute_argument_expression, amelyet együtt ugyanazok a szabályok korlátoznak, mint az egyszerű hozzárendelés. Az elnevezett argumentumok sorrendje nem jelentős.

Megjegyzés: Az egyszerűség kedvéért a záró vesszők global_attribute_section és attribute_section is megengedettek, ahogyan az array_initializer megengedett (17.7. §). végjegyzet

A attribute_name egy attribútumosztályt azonosít.

Ha egy attribútum globális szinten van elhelyezve, global_attribute_target_specifier kell megadni. Ha a global_attribute_target egyenlő:

  • assembly — a cél a tárolószerelvény
  • module — a cél az azt tartalmazó modul

A global_attribute_target más értékei nem engedélyezettek.

A szabványosított attribute_target nevek a következők event: , field, method, param, property, return, typeés typevar. Ezeket a célneveket csak a következő kontextusokban szabad használni:

  • event – esemény.
  • field – mező. Egy mezőszerű eseménynek (azaz tartozék nélkülinek) (15.8.2. §) és egy automatikusan implementált tulajdonságnak (15.7.4. §) is lehet attribútuma ezzel a célval.
  • method – konstruktor; finalizer; Módszer; Üzemeltető; helyi függvény, tulajdonság lekérése, beállítása és inicializálása; indexelő lekérése, beállítása és inicializálása; event add and remove accessors; és lambda kifejezések. Egy mezőszerű esemény (azaz tartozék nélküli) attribútummal is rendelkezhet ezzel a céllal.
  • param — tulajdonságkészlet és init tartozék, indexelőkészlet és init tartozék, esemény hozzáadása és eltávolítása, valamint egy paraméter egy konstruktorban, metódusban, helyi füstölésben és operátorban.
  • property — tulajdonság és indexelő.
  • return — delegált, metódus, helyi függvény, operátor, tulajdonság get tartozék, indexelő kap tartozék, és lambda kifejezés.
  • type — delegált, osztály, struct, enum és interfész.
  • typevar — típusparaméter.

Bizonyos környezetek lehetővé teszik egy attribútum specifikációját egynél több célon. A program egy attribute_target_specifier beleszámítva explicit módon megadhatja a célt. Attribute_target_specifier nélkül az alapértelmezett érték van alkalmazva, de egy attribute_target_specifier is használható az alapértelmezett beállítás megerősítésére vagy felülbírálására. A környezetek a következőképpen lesznek feloldva:

  • A delegált deklarációban szereplő attribútumok esetében az alapértelmezett cél a meghatalmazott. Ellenkező esetben, ha a attribute_target egyenlő:
    • type — a cél a meghatalmazott
    • return — a cél a visszatérési érték
  • Egy metódusdeklaráció egyik attribútuma esetében az alapértelmezett cél a metódus. Ellenkező esetben, ha a attribute_target egyenlő:
    • method — a cél a módszer
    • return — a cél a visszatérési érték
  • Egy helyi függvény deklarációján lévő attribútum esetében az alapértelmezett cél a helyi függvény. Ellenkező esetben, ha a attribute_target egyenlő:
    • method — a cél a helyi függvény
    • return — a cél a visszatérési érték
  • Egy operátor-deklaráció egyik attribútuma esetében az alapértelmezett cél az operátor. Ellenkező esetben, ha a attribute_target egyenlő:
    • method — a cél az operátor
    • return — a cél a visszatérési érték
  • Tulajdonság vagy indexelő deklarációhoz tartozó beolvasási kiegészítő deklaráción szereplő attribútum esetében az alapértelmezett cél a társított módszer. Ellenkező esetben, ha a attribute_target egyenlő:
    • method — a cél a társított módszer
    • return — a cél a visszatérési érték
  • Egy tulajdonság vagy indexelő deklaráció készletén vagy init tartozékán megadott attribútum esetében az alapértelmezett cél a társított módszer. Ellenkező esetben, ha a attribute_target egyenlő:
    • method — a cél a társított módszer
    • param — a cél a magányos implicit paraméter
  • Az automatikusan implementált tulajdonságdeklarációban szereplő attribútumok esetében az alapértelmezett cél a tulajdonság. Ellenkező esetben, ha a attribute_target egyenlő:
    • field — a cél a tulajdonság fordító által létrehozott háttérmezője
  • Egy eseménydeklarációban megadott attribútum esetében, amely kihagyja event_accessor_declarations az alapértelmezett cél az eseménydeklaráció. Ellenkező esetben, ha a attribute_target egyenlő:
    • event – a cél az eseménydeklaráció
    • field — a cél a mező
    • method — a célok a módszerek
  • Olyan eseménydeklaráció esetén, amely nem hagyja ki event_accessor_declarations az alapértelmezett cél a metódus.
    • method — a cél a társított módszer
    • param — a cél a magányos paraméter
  • Egy lambda_expression attribútum esetében az alapértelmezett cél a metódus. Ellenkező esetben, ha a attribute_target egyenlő:
    • method — a cél a módszer
    • return — a cél a visszatérési érték

Minden más kontextusban a attribute_target_specifier felvétele engedélyezett, de szükségtelen.

Példa: az osztálydeklaráció tartalmazhat vagy kihagyhat egy kijelölőt type:

[type: Author("Brian Kernighan")]
class Class1 {}

[Author("Dennis Ritchie")]
class Class2 {}

záró példa.

A implementációk más attribute_targetis elfogadhatnak, amelyek céljait a megvalósítás határozza meg. Az ilyen attribute_target nem felismerő végrehajtás figyelmeztetést ad ki, és figyelmen kívül hagyja a attribute_section.

Konvenció szerint az attribútumosztályok neve a következő utótaggal Attributevan elnevezve: . Egy attribute_name belefoglalhatja vagy kihagyhatja ezt az utótagot. A attribute_name az alábbiak szerint oldjuk fel:

  • Ha a attribute_name jobb oldali azonosítója szó szerinti azonosító (6.4.3. §), akkor a attribute_name type_name (7.8. §) oldja fel. Ha az eredmény nem a következő típusból System.Attributeszármazik, fordítási időhiba lép fel.
  • Egyébként
    • A attribute_name type_name (7.8. §) oldja fel, kivéve a hibákat. Ha ez a megoldás sikeres, és egy származtatott System.Attribute típust eredményez, akkor a típus ennek a lépésnek az eredménye.
    • A karakterek Attribute hozzá vannak fűzve a attribute_name jobb oldali azonosítóhoz, és az eredményül kapott jogkivonat-sztring type_name ként (7.8. §) lesz feloldva, kivéve a hibákat. Ha ez a megoldás sikeres, és egy származtatott System.Attribute típust eredményez, akkor a típus ennek a lépésnek az eredménye.

Ha a fenti két lépés közül pontosan az egyik származik System.Attribute, akkor ez a típus a attribute_name eredménye. Ellenkező esetben fordítási időhiba lép fel.

Példa: Ha egy attribútumosztály található az utótaggal együtt és nélkül is, akkor kétértelműség és fordítási idő hibát eredményez. Ha a attribute_name úgy van beírva, hogy a jobb szélső azonosító egy szó szerinti azonosító (6.4.3. §), akkor csak utótag nélküli attribútummal egyeznek meg, ami lehetővé teszi az ilyen kétértelműség feloldását. A példa

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

két attribútumosztály neve Example és ExampleAttribute. Az attribútum [Example] nem egyértelmű, mivel hivatkozhat rá vagy ExampleExampleAttribute. A szó szerinti azonosító használata lehetővé teszi a pontos szándék megadását ilyen ritka esetekben. Az attribútum [ExampleAttribute] nem egyértelmű (bár az lenne, ha lenne egy !nevű ExampleAttributeAttributeattribútumosztály). Ha az osztály Example deklarációja el lett távolítva, akkor mindkét attribútum a következő nevű ExampleAttributeattribútumosztályra hivatkozik:

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

záró példa

Fordítási időhiba, ha egy egyszer használt attribútumosztályt többször is használ ugyanazon az entitáson.

Példa: A példa

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

fordítási idő hibát eredményez, mert az egyszer használatos attribútumosztályt többször is megpróbálja használni HelpStringa deklaráción Class1.

záró példa

A kifejezés E egy attribute_argument_expression , ha az alábbi állítások mindegyike igaz:

  • A típus E egy attribútumparaméter típusa (23.2.4. §).
  • Fordításkor az érték E az alábbiak egyikével oldható fel:
    • Állandó érték.
    • Olyan System.Type objektum, amely typeof_expression (12.8.18. §) használatával kapott, amely nem általános típust, zárt építésű típust (8.4.3. §) vagy kötetlen általános típust (8.4.4. §) határoz meg, de nyílt típust nem (8.4.3. §).
    • Attribute_argument_expression egydimenziós tömbje.

Példa:

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

záró példa

A több részben deklarált típus attribútumait úgy határozzuk meg, hogy meghatározatlan sorrendben egyesítjük az egyes részek attribútumait. Ha ugyanazt az attribútumot több részre helyezi, az egyenértékű azzal, hogy az attribútumot többször is megadja a típuson.

Példa: A két rész:

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

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

egyenértékűek a következő egyetlen deklarációval:

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

záró példa

A típusparaméterek attribútumai ugyanúgy egyesíthetők.

23.4 Attribútumpéldányok

23.4.1 Általános

Az attribútumpéldányok olyan példányok, amelyek futásidőben egy attribútumot jelölnek. Az attribútumokat attribútumosztály, pozícióargumentumok és elnevezett argumentumok határozzák meg. Az attribútumpéldány annak az attribútumosztálynak a példánya, amely a pozíció- és elnevezett argumentumokkal inicializálva van.

Az attribútumpéldányok lekérése a fordítási és a futásidejű feldolgozást is magában foglalja az alábbi alklámokban leírtak szerint.

23.4.2 Attribútum összeállítása

A programentitásokon megadott és positional_argument_list, T attribútumosztályt Ptartalmazó attribútumNa következő lépések végrehajtásával lesz összeállítva egy szerelvénybeE:A

  • Kövesse a fordítási idő feldolgozási lépéseit összeállításához. Ezek a lépések fordítási időt eredményeznek, vagy meghatároznak egy példánykonstruktort CT , amely futásidőben hívható meg.
  • Ha C nem rendelkezik nyilvános akadálymentességgel, fordítási időhiba lép fel.
  • Minden named_argument Arg a következőbenN:
    • Legyen Name a .
    • Name azonosítja a nem statikus olvasási-írási nyilvános mezőt vagy a nyilvános, nem statikus írási vagy olvasási init tulajdonságot a következő helyen T: . Ha T nincs ilyen mezője vagy tulajdonsága, akkor fordítási időhiba lép fel.
  • Ha a positional_argument_listP vagy a named_argument_listN valamelyik értéke típusú System.String , és az érték nem megfelelően van formázva a Unicode Szabványban meghatározottak szerint, akkor implementálási meghatározást kell alkalmazni, hogy a lefordított érték megegyezik-e a lekért futásidejű értékkel (23.4.3. §).

    Megjegyzés: Például egy sztring, amely egy magas helyettesítő UTF-16 kódegységet tartalmaz, amelyet nem követ azonnal egy alacsony helyettesítő kódegység, nem jól formázott. végjegyzet

  • Tárolja a következő információkat (az attribútum futásidejű példányosításához) a fordító által a szerelvény kimenetében az attribútumot tartalmazó program összeállítása eredményeképpen: az attribútumosztályT, a példánykonstruktor CT, , a P és a kapcsolódó programentitássalN, az értékek fordítási időpontban teljesen feloldva.

23.4.3 Attribútumpéldány futásidejű lekérése

A 23.4.2. §-ban meghatározott kifejezések használatával a , T, C, és , attribútumpéldány által Pképviselt és Ntársított E attribútumpéldány futásidőben lekérhető a szerelvényből A az alábbi lépések végrehajtásával:

  • Kövesse az űrlap egy object_creation_expression végrehajtásának futásidejű feldolgozási lépéseit a példánykonstruktor new T(P) és a fordítási időpontban Cmeghatározott értékek használatával. Ezek a lépések kivételt eredményeznek, vagy létrehoznak egy példánytO.T
  • Minden named_argument ArgNsorrendben:
    • Legyen Name a . Ha Name nem azonosít nem statikus nyilvános olvasási-írási mezőt vagy nem statikus nyilvános olvasási-írási vagy olvasási init tulajdonságot O, akkor kivétel történik.
    • Nézzük Value meg a attribute_argument_expression kiértékelésének Argeredményét.
    • Ha Name azonosít egy mezőt O, akkor állítsa ezt a mezőt a következőre Value: .
    • Ellenkező esetben a Name (Név) tulajdonság azonosítja a (z) tulajdonságot O. Állítsa ezt a tulajdonságot Érték értékre.
    • Az eredmény a Opositional_argument_list és a named_argument_listT inicializált attribútumosztály P egy példánya.

Megjegyzés: A tárolás, , T, C, P (és társításNE) formátumaA, valamint a megadhatja E és lekérni Tkívánt mechanizmus, a , C, PNA , a (és így az attribútumpéldány futásidőben történő lekérésének módja) túllépi a specifikáció hatókörét. végjegyzet

23.5 Fenntartott attribútumok

23.5.1 Általános

Számos attribútum valamilyen módon befolyásolja a nyelvet. Ezek az attribútumok a következők:

  • System.AttributeUsageAttribute (23.5.2. §), amely az attribútumosztályok felhasználási módjait ismerteti.
  • System.Diagnostics.ConditionalAttribute (23.5.3. §) egy többfelhasználós attribútumosztály, amely feltételes metódusok és feltételes attribútumosztályok definiálására szolgál. Ez az attribútum egy feltételes fordítási szimbólum tesztelésével jelez egy feltételt.
  • System.ObsoleteAttribute (23.5.4. §), amely egy tag elavultként való megjelölésére szolgál.
  • System.Runtime.CompilerServices.AsyncMethodBuilderAttribute (23.5.5.5. §), amely egy aszinkron metódus feladatszerkesztőjének létrehozására szolgál.
  • System.Runtime.CompilerServices.CallerLineNumberAttribute (23.5.6.2. §), System.Runtime.CompilerServices.CallerFilePathAttribute (23.5.6.3. §), System.Runtime.CompilerServices.CallerMemberNameAttribute (23.5.6.4. §) és System.Runtime.CompilerServices.CallerArgumentExpressionAttribute (23.5.6.5.5. §), amelyek a hívó környezettel kapcsolatos információk opcionális paramétereknek való megadására szolgálnak.
  • System.Runtime.CompilerServices.EnumeratorCancellationAttribute (23.5.8. §), amely a lemondási jogkivonat paraméterének megadására szolgál egy aszinkron iterátorban.
  • System.Runtime.CompilerServices.ModuleInitializer (23.5.9. §), amely egy metódus modul inicializálójaként való megjelölésére szolgál.
  • System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute és System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute, amelyek egy egyéni interpolált sztringkifejezés-kezelő deklarálásához (§23.5.9.1) és annak egyik konstruktorának meghívásához használatosak.

A null értékű statikus elemzési attribútumok (23.5.7.§) javíthatják a nulla képességekre és nullállapotokra generált figyelmeztetések helyességét (8.9.5. §).

A végrehajtási környezetek további implementáció által definiált attribútumokat biztosíthatnak, amelyek befolyásolják a C#-programok végrehajtását.

23.5.2 Az AttributeUsage attribútum

Az attribútum AttributeUsage az attribútumosztály használható módjának leírására szolgál.

Az attribútummal AttributeUsage ellátott osztálynak közvetlenül vagy közvetve kell származnie System.Attribute. Ellenkező esetben fordítási időhiba lép fel.

Megjegyzés: Az attribútum használatára példa: §23.2.2. végjegyzet

23.5.3 A feltételes attribútum

23.5.3.1 Általános

Az attribútum Conditional lehetővé teszi az s feltételes metódus, a feltételes helyi függvényés a feltételes attribútum osztályes definícióját.

23.5.3.2 Feltételes módszerek

Az attribútummal Conditional díszített metódus feltételes módszer. Így minden feltételes módszer társítva van az attribútumaiban Conditional deklarált feltételes fordítási szimbólumokkal.

Példa:

class Eg
{
    [Conditional("ALPHA")]
    [Conditional("BETA")]
    public static void M()
    {
        // ...
    }
}

feltételes metódusként deklarál Eg.M a két feltételes fordítási ALPHA szimbólumhoz és BETA.

záró példa

Egy feltételes metódus hívása akkor jelenik meg, ha egy vagy több társított feltételes fordítási szimbólum a hívás időpontjában van definiálva, ellenkező esetben a hívás nem lesz megadva.

A feltételes módszerekre a következő korlátozások vonatkoznak:

  • A feltételes módszer egy class_declaration vagy struct_declaration metódusa. Fordítási időhiba akkor fordul elő, ha az Conditional attribútum egy felületi deklarációban megadott metóduson van megadva.
  • A feltételes módszer nem lehet tulajdonság, indexelő vagy esemény tartozéka.
  • A feltételes módszer visszatérési voidtípusa .
  • A feltételes módszert nem szabad megjelölni a override módosítóval. A feltételes metódusok azonban megjelölhetők a virtual módosítóval. Az ilyen módszer felülbírálása implicit módon feltételes, és nem jelölhető meg explicit módon attribútummal Conditional .
  • A feltételes módszer nem lehet interfészmetódus alkalmazása. Ellenkező esetben fordítási időhiba lép fel.
  • A feltételes módszer paraméterei nem lehetnek kimeneti paraméterek.

Megjegyzés: A (AttributeUsage) attribútumok általában AttributeTargets.Method alkalmazhatók a tulajdonságok, indexelők és események tartozékaira. A fenti korlátozások tiltják az Conditional attribútum használatát. végjegyzet

Emellett fordítási idő hibát is tapasztal, ha egy meghatalmazott feltételes metódusból jön létre.

Példa: A példa

#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();
    }
}

feltételes módszerként deklarál Class1.M . Class2's metódus meghívja Test ezt a metódust. Mivel a feltételes fordítási szimbólum DEBUG definiálva van, ha Class2.Test a rendszer meghívja, meghívja M. Ha a szimbólum DEBUG nincs definiálva, akkor Class2.Test nem hívná meg Class1.M.

záró példa

Fontos tisztában lenni azzal, hogy a feltételes metódusra irányuló hívás felvételét vagy kizárását a hívás helyén lévő feltételes fordítási szimbólumok szabályozzák.

Példa: Az alábbi kódban

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

az osztályok Class2 és Class3 mindegyik a feltételes metódus Class1.Fhívásait tartalmazza, amely feltételes attól függően, hogy definiálva van-e.DEBUG Mivel ez a szimbólum a környezetében Class2 van definiálva, de nemClass3, a rendszer a benne lévő F hívást Class2 is tartalmazza, míg a betárcsázható FClass3 hívás nincs megadva.

záró példa

A feltételes módszerek használata az öröklési láncban zavaró lehet. A feltételes metódushoz az űrlapon basekeresztül base.Mindított hívásokra a normál feltételes metódushívási szabályok vonatkoznak.

Példa: Az alábbi kódban

// 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 A M /> az alaposztályban meghatározott hívásokat tartalmazza. Ez a hívás ki van hagyva, mert az alapmetódus feltételes, a szimbólum DEBUGjelenléte alapján, amely nincs meghatározva. Így a metódus csak "Class2.M executed" a konzolra ír. Az pp_declaration-kmegfontolt használata kiküszöbölheti az ilyen problémákat.

záró példa

23.5.3.3 Feltételes helyi függvények

A statikus helyi függvények feltételessé tehetők a feltételes módszerrel azonos értelemben (23.5.3.2. §).

Fordítási időhiba akkor fordul elő, ha egy nem statikus helyi függvény feltételessé lett téve.

23.5.3.4 Feltételes attribútumosztályok

Egy vagy több attribútummal dekorált attribútumosztály (Conditional) feltételes attribútumosztály. A feltételes attribútumosztály így társítva van az attribútumaiban Conditional deklarált feltételes fordítási szimbólumokkal.

Példa:

[Conditional("ALPHA")]
[Conditional("BETA")]
public class TestAttribute : Attribute {}

feltételes attribútumosztályként deklarálja TestAttribute a feltételes fordítási szimbólumokhoz ALPHA és BETAa .

záró példa

A feltételes attribútum attribútumspecifikációi (23.3. §) akkor szerepelnek, ha egy vagy több társított feltételes fordítási szimbólum a specifikáció helyén van meghatározva, ellenkező esetben az attribútum specifikációja nem szerepel.

Fontos megjegyezni, hogy a feltételes attribútumosztály attribútumspecifikációjának felvételét vagy kizárását a specifikáció helyén található feltételes fordítási szimbólumok szabályozzák.

Példa: A példában

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

osztályokat Class1 , és Class2 mindegyik attribútummal Testvan dekorálva, amely feltételes attól függően, hogy definiálva van-e vagy sem DEBUG . Mivel ez a szimbólum a kontextusban Class1 van definiálva, de nem Class2, a tesztattribútum Class1 specifikációja is szerepel, míg a Test rajta lévő Class2 attribútum specifikációja nem szerepel.

záró példa

23.5.4 Az elavult attribútum

Az attribútum Obsolete olyan típusok és tagok megjelölésére szolgál, amelyeket már nem szabad használni.

Ha egy program olyan típust vagy tagot használ, amelyet a Obsolete attribútum díszít, a fordító figyelmeztetést vagy hibát ad ki. A fordítónak figyelmeztetést kell adnia, ha nem ad meg hibaparamétert, vagy ha a hibaparaméter meg van adva, és falseértékkel rendelkezik. A fordító hibát ad ki, ha a hibaparaméter meg van adva, és trueértékkel rendelkezik.

Példa: Az alábbi kódban

[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();
    }
}

az osztályt A az Obsolete attribútum díszíti. Minden egyes használat A egy figyelmeztetésben Main jelenik meg, amely tartalmazza a megadott üzenetet: "Ez az osztály elavult; helyett használja az osztályt B ".

záró példa

23.5.5 Az AsyncMethodBuilder attribútum

Ezt az attribútumot a 15.14.1 szakaszban leírják.

23.5.6 Hívóinformációs attribútumok

23.5.6.1 Általános

A naplózáshoz és a jelentéskészítéshez néha hasznos, ha a függvénytagok bizonyos fordítási időre vonatkozó információkat szereznek be a hívó kódról. A hívóinformációs attribútumok lehetővé teszik az információk transzparens átadását.

Ha egy választható paramétert a hívó-info attribútumok egyikével jegyzetel, a hívás megfelelő argumentumának kihagyása nem feltétlenül eredményezi az alapértelmezett paraméterérték helyettesítését. Ehelyett, ha a hívási környezettel kapcsolatos megadott információk rendelkezésre állnak, a rendszer argumentumértékként adja át az adatokat.

Példa:

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

Az argumentumok nélküli hívás Log() kinyomtatja a hívás sorszámát és fájl elérési útját, valamint annak a tagnak a nevét, amelyen belül a hívás történt.

záró példa

A hívóinformációs attribútumok bárhol előfordulhatnak választható paramétereken, beleértve a delegált deklarációkat is. Az adott hívóinformációs attribútumok azonban korlátozzák az attribútumok típusait, így mindig implicit átalakítás lesz a helyettesítő értékről a paramétertípusra.

Hiba, ha ugyanazzal a hívó-info attribútummal rendelkezik egy részleges metódusdeklaráció definiáló és implementáló részének paraméterén. A rendszer csak a definiáló rész hívóinformációs attribútumait alkalmazza, míg a csak a megvalósító részben előforduló hívó-info attribútumokat a rendszer figyelmen kívül hagyja.

A hívó adatai nem befolyásolják a túlterhelés feloldását. Mivel az attribútumként megadott opcionális paraméterek továbbra is kimaradnak a hívó forráskódjából, a túlterhelés feloldása ugyanúgy figyelmen kívül hagyja ezeket a paramétereket, mint a többi kihagyott opcionális paramétert (12.6.4. §).

A hívóadatok csak akkor lesznek helyettesítve, ha a forráskód kifejezetten meghív egy függvényt. Az implicit meghívások, például az implicit szülőkonstruktorhívások nem rendelkeznek forráshellyel, és nem helyettesítik a hívó adatait. Emellett a dinamikusan kötött hívások nem helyettesítik a hívó adatait. Ha ilyen esetekben egy hívóinformációs attribútummal rendelkező paramétert hagy ki, a rendszer ehelyett a paraméter megadott alapértelmezett értékét használja.

Az egyik kivétel a lekérdezési kifejezések. Ezek szintaktikai expanzióknak minősülnek, és ha az általuk kibontott hívások nem kötelező paramétereket hagynak ki hívóinformációs attribútumokkal, a hívó információi lecserélődnek. A használt hely annak a lekérdezési záradéknak a helye, ahonnan a hívást létrehozták.

Ha egy adott paraméteren egynél több hívó-info attribútum van megadva, a rendszer a következő sorrendben ismeri fel őket: CallerLineNumber, CallerFilePath, CallerMemberName, CallerArgumentExpression. Vegye figyelembe a következő paraméterdeklarációt:

[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ...

CallerLineNumber elsőbbséget élvez, a másik három attribútumot pedig figyelmen kívül hagyja. Ha CallerLineNumber nem lett volna megadva, CallerFilePath elsőbbséget élvezne, és CallerMemberNameCallerArgumentExpression figyelmen kívül hagyná. Ezeknek az attribútumoknak a lexikális sorrendje irreleváns.

23.5.6.2 A CallerLineNumber attribútum

Az attribútum System.Runtime.CompilerServices.CallerLineNumberAttribute akkor engedélyezhető választható paramétereken, ha az állandó értékről a paraméter típusára standard implicit átalakítás (int.MaxValue. §) van. Ez biztosítja, hogy az adott értékig nem negatív sorszám hiba nélkül átadható legyen.

Ha a forráskód egy helyéről történő függvényhívás kihagy egy választható paramétert a CallerLineNumberAttributeforráskódban, akkor a rendszer az alapértelmezett paraméterérték helyett argumentumként az adott hely sorszámát képviselő numerikus literált használja.

Ha a meghívás több sorra is kiterjed, a kiválasztott sor implementációfüggő.

A sorszámot az irányelvek befolyásolhatják #line (6.5.8. §).

23.5.6.3 A CallerFilePath attribútum

Az attribútum System.Runtime.CompilerServices.CallerFilePathAttribute akkor engedélyezhető választható paramétereken, ha a paraméter típusához standard implicit átalakítás (string. §) tartozik.

Ha a forráskód egy helyéről történő függvényhívás kihagy egy választható paramétert a forráskódban, akkor az CallerFilePathAttributealapértelmezett paraméterérték helyett az adott hely fájlútvonalát képviselő sztringkonstanst használ a rendszer a meghívás argumentumaként.

A fájl elérési útjának formátuma implementációfüggő.

A fájl elérési útját az irányelvek befolyásolhatják #line (6.5.8. §).

23.5.6.4 A CallerMemberName attribútum

Az attribútum System.Runtime.CompilerServices.CallerMemberNameAttribute akkor engedélyezhető választható paramétereken, ha a paraméter típusához standard implicit átalakítás (string. §) tartozik.

Ha egy függvényhívás egy függvénytag törzsén belüli helyről vagy a függvénytagra alkalmazott attribútumon belül, vagy annak visszatérési típusa, a forráskódban szereplő paraméterek vagy típusparaméterek kihagynak egy választható paramétert a CallerMemberNameAttributekövetkezővel, akkor a rendszer az alapértelmezett paraméterérték helyett argumentumként az adott tag nevét képviselő karakterlánc-literált használja a meghíváshoz. (Legfelső szintű utasításból (7.1.3.§) származó függvényhívás esetén a tag neve a végrehajtás által generált.)

Az általános metódusokon belül előforduló meghívások esetén a típusparaméter-lista nélkül csak a metódus nevét használja a rendszer.

Az explicit felülettag-implementációkban előforduló meghívások esetében a rendszer csak magát a metódusnevet használja az előző felületi minősítés nélkül.

A tulajdonságon vagy eseményen belüli meghívások esetén a használt tagnév maga a tulajdonság vagy az esemény neve.

Az indexelő tartozékaiban előforduló meghívások esetén a használt tagnév az indexelőtagon (IndexerNameAttribute) megadott név, ha van ilyen, vagy az alapértelmezett névItem.

A mező- vagy esemény-inicializálókban előforduló meghívások esetén a használt tagnév az inicializálandó mező vagy esemény neve.

A példánykonstruktorok, statikus konstruktorok, véglegesítők és operátorok deklarációiban előforduló meghívások esetében a használt tagnév implementációfüggő.

Helyi vagy névtelen függvényen belüli meghívás esetén a rendszer annak a tagmetódusnak a nevét használja, amely meghívja a függvényt.

Példa: Fontolja meg a következőket:

class Program
{
    static void Main()
    {
        F1();

        void F1([CallerMemberName] string? name = null)
        {
            Console.WriteLine($"F1 MemberName: |{name}|");
            F2();
        }

        static void F2([CallerMemberName] string? name = null)
        {
            Console.WriteLine($"F2 MemberName: |{name}|");
        }
    }
}

amely létrehozza a kimenetet

F1 MemberName: |Main|
F2 MemberName: |Main|

Ez az attribútum adja meg a hívó függvény tagjának nevét, amely a helyi függvény F1 esetében a metódus Main. Annak ellenére F2 , hogy a függvény meghívja F1, a helyi függvény nem függvénytag, így a jelentett hívó F2 is Main. záró példa

23.5.6.5 A CallerArgumentExpression attribútum

Az attribútum System.Runtime.CompilerServices.CallerArgumentExpressionAttribute egy célparaméterre van alkalmazva, és egy testvérparaméter argumentumának forráskódszövegét sztringként rögzíti, amelyet itt rögzített sztringnek nevezünk.

A bővítménymetódus első paraméterének kivételével a célparaméternek default_argument kell rendelkeznie.

Vegye figyelembe a következő módszerdeklarációt:

using System;
using System.Runtime.CompilerServices;
#nullable enable
class Test
{
    public static void M(int val = 0, [CallerArgumentExpression("val")] string? text = null)
    {
        Console.WriteLine($"val = {val}, text = <{text}>");
    }
}

amelyben a célparaméter és text a testvérparaméter van val, amelynek a megfelelő argumentum forráskódszövege rögzíthető text a meghíváskor M .

Az attribútumkonstruktor típus argumentumot stringvesz fel. Ez a sztring

  • A testvérparaméter nevét kell tartalmaznia; ellenkező esetben a rendszer figyelmen kívül hagyja az attribútumot.
  • Kihagyja a bevezetőt @ egy olyan paraméternévből, amely rendelkezik ezzel az előtaggal.

Egy parameter_list több célparamétert is tartalmazhat.

A célparaméter típusának szabványos átalakítással kell rendelkeznie string.

Megjegyzés: Ez azt jelenti, hogy a felhasználó által definiált konverziók nem engedélyezettek string , és a gyakorlatban azt jelenti, hogy az ilyen paraméter típusának kell lennie string, objectvagy egy felületnek, amelyet a stringrendszer implementál. végjegyzet

Ha explicit argumentumot ad át a célparaméterhez, a rendszer nem rögzíti a sztringet, és a paraméter felveszi az argumentum értékét. Ellenkező esetben a testvérparaméternek megfelelő argumentum szövege rögzített sztringgé alakul az alábbi szabályok szerint:

  • A vezető és a záró fehér tér el lesz távolítva a legkülső csoportosítási zárójelek eltávolítása előtt és után is.
  • Minden legkülső csoportosítási zárójel el lesz távolítva a kezdő és a záró szóköz eltávolítása előtt és után is.
  • Az összes többi input_elementszó szerint megmarad (beleértve a szóközöket, a megjegyzéseket, a Unicode_Escape_Sequences-eket és @ az azonosítók előtagjait).

A rögzített sztring ezután a célparaméternek megfelelő argumentumként lesz átadva. Ha azonban a testvérparaméter argumentuma nincs megadva, a célparaméter a default_argument értékét veszi fel.

Példa: Tekintettel a fenti deklarációra M , vegye figyelembe a következő hívásokat M:

Test.M();
Test.M(123);
Test.M(123, null);
Test.M(123, "xyz");
Test.M(  1  +      2 );
Test.M(( ( (123) + 0) ) );
int local = 10;
Test.M(l\u006fcal /*...*/ + // xxx
  5);

a létrehozott kimenet

val = 0, text = <>
val = 123, text = <123>
val = 123, text = <>
val = 123, text = <xyz>
val = 3, text = <1  +      2>
val = 123, text = <(123) + 0>
val = 15, text = <l\u006fcal /*...*/ + // xxx
  5>

záró példa

23.5.7 Kódelemzési attribútumok

23.5.7.1 Általános

Az alcím attribútumai további információkat nyújtanak a nullabilitás és null állapotú diagnosztikát biztosító fordítók támogatásához (§8.9.5). A nullállapotú diagnosztika elvégzéséhez nincs szükség fordítóra. Ezeknek az attribútumoknak a jelenléte vagy hiánya nem befolyásolja a program nyelvét és viselkedését. A nullállapot-diagnosztikát nem biztosító fordítónak be kell olvasnia és figyelmen kívül kell hagynia ezeknek az attribútumoknak a jelenlétét. A nullállapot-diagnosztikát biztosító fordítónak az ebben az albekezdésben meghatározott jelentést kell használnia azokra az attribútumokra, amelyeket diagnosztikai célból használ.

A kódelemzési attribútumok névtérben System.Diagnostics.CodeAnalysisvannak deklarálva.

Attribútum Jelentés
AllowNull (23.5.7.2. §) A nem null értékű argumentumok lehetnek null értékűek.
DisallowNull (23.5.7.3. §) A null értékű argumentumok soha nem lehetnek null értékűek.
MaybeNull (23.5.7.6. §) A nem null értékű visszatérési érték null értékű lehet.
NotNull (23.5.7.10. §) A null értékű visszatérési érték soha nem lesz null értékű.
MaybeNullWhen (23.5.7.7. §) A nem null értékű argumentum null értékű lehet, ha a metódus a megadott bool értéket adja vissza.
NotNullWhen (23.5.7.12. §) A null értékű argumentumok nem lesznek null értékűek, ha a metódus a megadott bool értéket adja vissza.
NotNullIfNotNull (23.5.7.11. §) A visszatérési érték nem null, ha a megadott paraméter argumentuma nem null.
MemberNotNull (23.5.7.8. §) A listában szereplő tag nem lesz null értékű, amikor a metódus visszatér.
MemberNotNullWhen (23.5.7.9. §) A listában szereplő tag nem lesz null értékű, ha a metódus a megadott bool értéket adja vissza.
DoesNotReturn (23.5.7.4. §) Ez a metódus soha nem tér vissza.
DoesNotReturnIf (23.5.7.5. §) Ez a metódus soha nem ad vissza értéket, ha a társított bool paraméter a megadott értékkel rendelkezik.

A 23.5.7. § -ban a következő alklámok feltételesen normatívak.

23.5.7.2 Az AllowNull attribútum

Megadja, hogy a null érték akkor is engedélyezett bemenetként, ha a megfelelő típus nem engedélyezi azt.

Példa: Fontolja meg az alábbi olvasási/írási null tulajdonságot, amely soha nem ad vissza, mert ésszerű alapértelmezett értékkel rendelkezik. A felhasználó azonban null értéket adhat a beállított tartozéknak, hogy a tulajdonságot az alapértelmezett értékre állítsa.

#nullable enable
public class X
{
    [AllowNull]
    public string ScreenName
    {
        get => _screenName;
        set => _screenName = value ?? GenerateRandomScreenName();
    }
    private string _screenName = GenerateRandomScreenName();
    private static string GenerateRandomScreenName() => ...;
}

Tekintettel a tulajdonság készlettartozékának alábbi használatára

var v = new X();
v.ScreenName = null;   // may warn without attribute AllowNull

az attribútum nélkül a fordító figyelmeztetést generálhat, mert úgy tűnik, hogy a nem null értékű tulajdonság null értékűre van állítva. Az attribútum jelenléte letiltja ezt a figyelmeztetést. záró példa

23.5.7.3 A DisallowNull attribútum

Megadja, hogy a null érték nem engedélyezett bemenetként, még akkor is, ha a megfelelő típus engedélyezi.

Példa: Vegye figyelembe az alábbi tulajdonságot, amelyben a null az alapértelmezett érték, de az ügyfelek csak nem null értékűre állíthatják be.

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

A lekérő metódus visszaadhatja a nullalapértelmezett értékét, ezért a fordító figyelmeztetheti, hogy a hozzáférést megelőzően ellenőrizni kell. Emellett arra is figyelmezteti a hívókat, hogy annak ellenére, hogy null értékű lehet, a hívónak nem szabad explicit módon null értékre állítania. záró példa

23.5.7.4 A DoesNotReturn attribútum

Azt adja meg, hogy egy adott metódus soha nem ad vissza.

Példa: Fontolja meg a következőket:

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

Az attribútum jelenléte számos módon segíti a fordítót. Először is a fordító figyelmeztetést adhat ki, ha van olyan elérési út, ahol a metódus kivétel nélkül kiléphet. Másodszor, a fordító el tudja tiltani a null értékű figyelmeztetéseket bármely kódban az adott metódus meghívása után, amíg meg nem találja a megfelelő fogási záradékot. Harmadszor, a nem elérhető kód semmilyen null állapotra nem lesz hatással.

Az attribútum nem változtatja meg az elérhetőséget (13.2.§) vagy a határozott hozzárendelést (§9.4) az attribútum jelenléte alapján. Csak a nullitásra vonatkozó figyelmeztetések befolyásolására használható. záró példa

23.5.7.5 A DoesNotReturnIf attribútum

Azt adja meg, hogy egy adott metódus soha nem ad vissza értéket, ha a társított bool paraméter a megadott értékkel rendelkezik.

Példa: Fontolja meg a következőket:

#nullable enable
public class X
{
    private void ThrowIfNull([DoesNotReturnIf(true)] bool isNull, string argumentName)
    {
        if (isNull)
        {
            throw new ArgumentException(argumentName,
              $"argument {argumentName} cannot 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!;
}

záró példa

23.5.7.6 A MaybeNull attribútum

Azt határozza meg, hogy a nem null értékű visszatérési érték null értékű lehet.

Példa: Fontolja meg a következő általános módszert:

#nullable enable
[return: MaybeNull]
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }

Az attribútum nélkül a fordító figyelmeztetést generálhat, ha a metódus vissza tud térni null. Az attribútum jelenléte letiltja ezt a figyelmeztetést. záró példa

23.5.7.7 A MaybeNullWhen attribútum

Azt adja meg, hogy nem null értékű argumentum lehet null , ha a metódus a megadott bool értéket adja vissza. Ez hasonló az MaybeNull attribútumhoz (23.5.7.6. §), de tartalmaz egy paramétert a megadott visszatérési értékhez.

23.5.7.8 A MemberNotNull attribútum

Azt adja meg, hogy az adott tag nem lesz null , amikor a metódus visszatér.

Példa: Egy segédmetódus tartalmazhat olyan attribútumot, amely felsorolja az MemberNotNull adott metódus nem null értékéhez rendelt mezőket. Egy olyan fordító, amely konstruktorokat elemez annak megállapítására, hogy az összes nem null értékű referenciamező inicializálva lett-e, ezt az attribútumot használhatja annak felderítésére, hogy mely mezők lettek beállítva ezek a segédmeteorizálási módszerek. Vegye figyelembe a következő példát:

#nullable enable
public class Container
{
    private string _uniqueIdentifier; // must be initialized.
    private string? _optionalMessage;

    public Container()
    {
        Helper();
    }

    public Container(string message)
    {
        Helper();
        _optionalMessage = message;
    }

    [MemberNotNull(nameof(_uniqueIdentifier))]
    private void Helper()
    {
        _uniqueIdentifier = DateTime.Now.Ticks.ToString();
    }
}

Több mezőnév is megadható argumentumként az attribútum konstruktorának. záró példa

23.5.7.9 A MemberNotNullWhen attribútum

Azt adja meg, hogy a listatag nem lesz null , amikor a metódus visszaadja a megadott bool értéket.

Példa: Ez az attribútum hasonló MemberNotNull (23.5.7.8. §), kivéve, ha MemberNotNullWhen argumentumot bool vesz fel. MemberNotNullWhen olyan helyzetekben való használatra szolgál, amikor egy segédmetódus azt bool jelzi, hogy inicializálta-e a mezőket. záró példa

23.5.7.10 A NotNull attribútum

Azt adja meg, hogy a null értékű érték soha nem lesz null , ha a metódus (a dobás helyett) ad vissza.

Példa: Fontolja meg a következőket:

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

Ha engedélyezve vannak a null értékű hivatkozástípusok, a metódus ThrowWhenNull figyelmeztetések nélkül fordítható le. Ha ez a metódus visszatér, az value argumentum garantáltan nem nulllesz . A null referenciával történő hívás ThrowWhenNull azonban elfogadható. záró példa

23.5.7.11 A NotNullIfNotNull attribútum

Azt adja meg, hogy a visszaadott érték nem null akkor legyen, ha a megadott paraméter argumentuma nem null.

Példa: Egy visszatérési érték null állapota egy vagy több argumentum null állapotától függhet. A fordítóelemzés segítése érdekében a null attribútum használható, ha egy metódus mindig nem null értéket ad vissza, amikor bizonyos argumentumok nem NotNullIfNotNull értékűek. Fontolja meg a következő módszert:

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

Ha az url argumentum nem null, null akkor a függvény nem adja vissza. Ha engedélyezve vannak a null értékű hivatkozások, az aláírás megfelelően működik, feltéve, hogy az API soha nem fogad el null argumentumot. Ha azonban az argumentum null értékű lehet, akkor a visszatérési érték is null lehet. A szerződés helyes kifejezéséhez fűzze megjegyzésként ezt a módszert az alábbiak szerint:

#nullable enable
[return: NotNullIfNotNull("url")]
string? GetTopLevelDomainFromFullUrl(string? url) { ... }

záró példa

23.5.7.12 A NotNullWhen attribútum

Azt adja meg, hogy a null értékű argumentum nem lesz null akkor, ha a metódus a megadott bool értéket adja vissza.

Példa: A kódtár metódus String.IsNullOrEmpty(String) akkor ad true vissza, ha az argumentum null vagy egy üres sztring. Ez a null-ellenőrzés egyik formája: A hívóknak nem kell null-ellenőrzéssel ellenőrizni az argumentumot false, ha a metódus visszatér. Ha egy ilyen null értékű metódust szeretne felismerni, állítsa a paramétertípust null értékű hivatkozástípussá, és adja hozzá a NotNullWhen attribútumot:

#nullable enable
bool IsNullOrEmpty([NotNullWhen(false)] string? value) { ... }

záró példa

23.5.8 Az EnumeratorCancellation attribútum

Megadja az aszinkron CancellationToken iterátor paraméterét (15.15. §). Ennek a paraméternek az argumentumát össze kell kapcsolni a megadott IAsyncEnumerable<T>.GetAsyncEnumerator(CancellationToken)argumentummal. Ezt az egyesített jogkivonatot IAsyncEnumerator<T>.MoveNextAsync() a (15.15.5.2. §) kell lekérdezni. A jogkivonatokat egyetlen jogkivonatba kell egyesíteni, mintha az CancellationToken.CreateLinkedTokenSource adott jogkivonat és annak tulajdonsága Token lenne. Az egyesített jogkivonat akkor lesz megszakítva, ha a két forrásjogkivonat egyikét megszakítja. Az egyesített jogkivonat az aszinkron iterátormetódus (15.15.15. §) argumentuma a metódus törzsében.

Hiba, ha az System.Runtime.CompilerServices.EnumeratorCancellation attribútum egynél több paraméterre van alkalmazva. A fordító figyelmeztetést okozhat, ha:

  • Az EnumeratorCancellation attribútum egy olyan paraméterre lesz alkalmazva, amely nem CancellationTokenaz ,
  • vagy ha az EnumeratorCancellation attribútum nem aszinkron iterátort használó metódus paraméterére van alkalmazva (15.15.§),
  • vagy ha az EnumeratorCancellation attribútum egy olyan metódus paraméterére van alkalmazva, amely aszinkron enumerátorfelületet (15.15.2. §) aszinkron számba vevő interfész helyett aszinkron számbavételi felületet ad vissza (15.15.3. §).

Az iterátor nem fér hozzá az CancellationToken argumentumhoz GetAsyncEnumerator , ha egyetlen attribútum sem rendelkezik ezzel a paramétersel.

Példa: A metódus GetStringsAsync() egy aszinkron iterátor. Mielőtt bármilyen munkát végez a következő érték lekérése érdekében, ellenőrzi a lemondási jogkivonatot annak megállapításához, hogy az iterációt le kell-e mondani. Ha a rendszer lemondást kér, nem történik további művelet.

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

záró példa

23.5.9 A ModuleInitializer attribútum

Az attribútum ModuleInitializer egy metódus modul inicializálójaként való megjelölésére szolgál. Ezt a metódust a modul inicializálása során hívjuk meg. Egy modul több inicializálóval is rendelkezhet, amelyeket implementáció által meghatározott sorrendben hív meg.

A modul inicializálójában engedélyezett kódra nincs korlátozás.

A modul inicializálójának a következő jellemzőkkel kell rendelkeznie:

  • A method_modifierstatic.
  • Nincs parameter_list.
  • A return_type .void
  • Nincs type_parameter_list.
  • Nem deklarálható class_declarationtype_parameter_list.
  • Legyen elérhető a tartalmazó modulból (azaz rendelkezik hozzáférés-módosítóval internal vagy public).
  • Nem lehet helyi függvény.

23.5.9.1 Egyéni interpolált sztringkifejezés-kezelők

23.5.9.1.1 Egyéni kezelő deklarálása

Fontolja meg a következő programot, amely egy egyszerű üzenetnaplózót implementál:

using System;
public class Logger
{
    public void LogMessage(string msg)
    {
        Console.WriteLine(msg);
    }
}
public class Program
{
    static void Main()
    {
        var logger = new Logger();
        int val = 255;
        logger.LogMessage($"val = {{{val,4:X}}}; 2 * val = {2 * val}.");
    }
}

A létrehozott kimenet a következő:

val = {  FF}; 2 * val = 510.

A hívásban LogMessageaz interpolált sztringkifejezés argumentumának célja a paraméter msg, amelynek típusa van string. Ennek megfelelően a 12.8.3. § szerint a rendszer meghívja az alapértelmezett interpolált sztringkifejezés-kezelőt. Az alábbi alklám (23.5.9.1.1.1.) bemutatja az egyéni kezelő használatát.

Ahhoz, hogy a fenti program egyéni feldolgozást biztosíthasson, egyéni interpolált sztringkifejezés-kezelőre van szükség. Ekkor megjelenik az üzenetnaplózó egy egyéni kezelővel (amely bár nem tesz mást, mint az alapértelmezett kezelőhöz hasonlóan viselkedik, a testreszabáshoz biztosítja a horgokat):

using System;
using System.Text;
using System.Runtime.CompilerServices;

[InterpolatedStringHandler]
public ref struct LogInterpolatedStringHandler
{
    StringBuilder builder; // Storage for the built-up string
    public LogInterpolatedStringHandler(int literalLength, int formattedCount)
    {
        builder = new StringBuilder(literalLength);
    }
    public void AppendLiteral(string s)
    {
        builder.Append(s);
    }
    public void AppendFormatted<T>(T t)
    {
        builder.Append(t?.ToString());
    }
    public void AppendFormatted<T>(T t, string format) where T : IFormattable
    {
        builder.Append(t?.ToString(format, null));
    }
    public void AppendFormatted<T>(T t, int alignment, string format)
        where T : IFormattable
    {
        builder.Append(String.Format("{0" + "," + alignment + ":" + format + "}", t));
    }
    public override string ToString() => builder.ToString();
}

public class Logger
{
    public void LogMessage(string msg)
    {
        Console.WriteLine(msg);
    }
    public void LogMessage(LogInterpolatedStringHandler builder)
    {
        Console.WriteLine(builder.ToString());
    }
}

public class Program
{
    static void Main()
    {
        var logger = new Logger();
        int val = 255;
        logger.LogMessage($"val = {{{val,4:X}}}; 2 * val = {2 * val}.");
    }
}

A létrehozott kimenet a következő:

val = {  FF}; 2 * val = 510.

Az attribútummal System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute rendelkező típus állítólag egy alkalmazható interpolált sztringkezelő típus.

Az egyéni interpolált sztringkifejezés-kezelőnek való minősítéshez egy osztály- vagy struktúratípusnak a következő jellemzőkkel kell rendelkeznie:

  • Legyen megjelölve az attribútummal System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute.
  • Legyen egy akadálymentes konstruktor, amelynek első két paramétere típussal intrendelkezik. (Egyéb paraméterek is követhetők, amelyek a kezelőnek vagy a kezelőtől érkező információk átadására szolgálnak. Ezekről a 23.5.9.1.3. Deklarálható egy opcionális végső paraméter, amely meggátolja a kezelőt az interpolált sztring feldolgozásában. Ezt a 23.5.9.1.2.

Amikor a fordító által létrehozott kód meghívja a konstruktort, az első paraméter az interpolált sztringkifejezések (12.8.3.§) hosszának összegére van beállítva az interpolált sztringkifejezésben, a második paraméter pedig az interpolációk számának. (Ezek az értékek a ($"val = {{{val,4:X}}}; 2 * val = {2 * val}."21- és a 2-et jelölik.)

  • Legyen elérhető metódusa az aláírással void AppendLiteral(string s), amelyet egyetlen interpolált sztringkifejezés literális szegmensének feldolgozására hívunk fel.
  • Legyen egy akadálymentes túlterhelt metódus AppendFormatted, amelynek egyikét egyetlen interpoláció feldolgozására hívjuk fel az interpoláció tartalma alapján. Aláírásuk a következő:
    • void AppendFormatted<T>(T t)elemet, amely az olyan interpolációkkal foglalkozik, amelyek nem rendelkeznek explicit formátummal vagy igazítással {2 * val}, mint például a .
    • void AppendFormatted<T>(T t, string format) where T : System.IFormattable, amely explicit formátumú interpolációkkal foglalkozik, de nem igazítással, mint például a {val:X4}.
    • void AppendFormatted<T>(T t, int alignment, string format) where T : System.IFormattable, amely explicit formátumú és igazítású interpolációkkal foglalkozik, ahogyan az {val,4:X}a .
  • Legyen egy nyilvános metódusa az aláírással override string ToString(), amely a beépített sztringet adja vissza.

Megjegyzés: A túlterhelések kihagyása AppendFormatted nem fordítási idejű hiba, de ha a kezelő maximálisan robusztus, az alapértelmezett kezelő által felismert összes formátumot támogatnia kell. végjegyzet

Az új túlterhelés LogMessage helyett stringegy egyéni kezelőt használ, és a sztringet a kezelő által formázott módon kéri le. Ilyen túlterhelések esetén, ha létezik egy megfelelő kezelő, és az interpolált sztringkifejezés nem állandó (12.8.3. §), a fordító kódot hoz létre, amely meghívja a kezelőt. Ilyen esetekben a fordító olyan kódot hoz létre, amely

  • a kezelő konstruktor meghívása
  • lexikális sorrendben az interpolált sztringkifejezésen belül
    • adja át az egyes interpolált sztringkifejezés-szegmenseket a AppendLiteral
    • adja át az egyes interpolációt a megfelelő módszernek AppendFormatted .
  • az interpolált sztringkifejezés értékeként adja vissza az utolsó sztringet.
  • hajtsa végre a következő törzsét LogMessage: .
23.5.9.1.2 Egyéni kezelő akadályozása

Ha egy kezelőkonstruktor rendelkezik egy kimenő paramétert bool tartalmazó végső paraméterrel, akkor a konstruktor neve annak a paraméternek az értéke lesz tesztelve. Ha ez igaz, a viselkedés olyan, mintha a paramétert kihagyták volna. Ha azonban hamis, az interpolált sztringkifejezés feldolgozása nem történik meg tovább; vagyis a kezelő gátlásos. Az interpolációs kifejezések kiértékelése nem történik meg, a metódusok AppendLiteral pedig AppendFormatted nem lesznek meghívva.

public LogInterpolatedStringHandler(int literalLength, int formattedCount,
    out bool processString)
{
    if (some_condition)
    {
        processString = false;
        return;
    }
    else 
    {
        processString = true;
        // continue construction
    }
}

Megjegyzés: Az interpolált sztringkifejezések interpolációi mellékhatásokat tartalmazhatnak (a , , --hozzárendelés és néhány metódushívás eredményeként++). Ha egy kezelőt gátol, az interpolált sztringkifejezés egyik mellékhatását sem értékeli ki a rendszer. Ha a kezelő nem gátolja, az interpolált sztringkifejezés összes mellékhatását kiértékeli a rendszer. végjegyzet

23.5.9.1.3 Adatok továbbítása egyéni kezelőknek vagy onnan

Hasznos lehet, ha más információkat ad át az egyéni kezelőnek, és visszafogadja az adatokat. Ez az attribútumon System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentkeresztül történik. Vegye figyelembe az alábbi új túlterheléseket az üzenetnaplózó program számára:

public class Logger
{
    // …
    public void LogMessage(bool flag, int count,
        [InterpolatedStringHandlerArgument("count","flag","")] 
        LogInterpolatedStringHandler builder)
    {
        // …
    }
}

public ref struct LogInterpolatedStringHandler
{
    // …
    public LogInterpolatedStringHandler(int literalLength, int formattedCount,
        int count, bool flag, Logger logger)
    {
        // …
    }
}

Az attribútumot InterpolatedStringHandlerArgument a kezelőparaméterre alkalmazza a rendszer, amely a kezelőnek átadni kívánt paraméterek deklarációit követi. Az attribútumkonstruktor argumentuma nulla vagy több olyan sztring vesszővel tagolt listája, amely az átadott paramétereket és azok sorrendjét adja meg. Egy üres sztring jelöli ki azt a példányt, amelyből a kezelőt meghívják. Ezért a fenti "count","flag","" attribútumkonstruktor-hívás egyező kezelőkonstruktort igényel. Ha az attribútumkonstruktor argumentumlistája üres, a viselkedés olyan, mintha az attribútum ki lett hagyva.

Ha egy out bool paramétert úgy is deklarálnak, hogy lehetővé teszi a kezelő akadályozását (23.5.9.1.2. §), akkor ez a paraméter lesz az utolsó.

23.6 Az együttműködés attribútumai

Más nyelvekkel való együttműködés esetén az indexelő indexelt tulajdonságok használatával implementálható. Ha egy indexelőhöz nincs IndexerName attribútum, a rendszer alapértelmezés szerint a nevet Item használja. Az IndexerName attribútum lehetővé teszi, hogy a fejlesztő felülbírálja ezt az alapértelmezett értéket, és adjon meg egy másik nevet.

Példa: Alapértelmezés szerint az indexelő neve .Item Ezt felül lehet bírálni az alábbiak szerint:

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
    get { ... }
    set { ... }
}

Most az indexelő neve .TheItem

záró példa