22 attribut
22.1 Allmänt
Mycket av C#-språket gör det möjligt för programmeraren att ange deklarativ information om de entiteter som definierats i programmet. Till exempel anges tillgängligheten för en metod i en klass genom att den dekoreras med method_modifiers public
, protected
, internal
och private
.
C# gör det möjligt för programmerare att uppfinna nya typer av deklarativ information, som kallas attribut. Programmerare kan sedan koppla attribut till olika programentiteter och hämta attributinformation i en körningsmiljö.
Obs! Ett ramverk kan till exempel definiera ett
HelpAttribute
attribut som kan placeras på vissa programelement (till exempel klasser och metoder) för att tillhandahålla en mappning från dessa programelement till dokumentationen. slutkommentar
Attribut definieras genom deklarationen av attributklasser (§22.2), som kan ha positionella och namngivna parametrar (§22.2.3). Attribut kopplas till entiteter i ett C#-program med hjälp av attributspecifikationer (§22.3) och kan hämtas vid körning som attributinstanser (§22.4).
22.2 Attributklasser
22.2.1 Allmänt
En klass som härleds från den abstrakta klassen System.Attribute
, antingen direkt eller indirekt, är en attributklass. Deklarationen av en attributklass definierar en ny typ av attribut som kan placeras på programentiteter. Enligt konventionen namnges attributklasser med suffixet Attribute
. Användning av ett attribut kan antingen innehålla eller utelämna det här suffixet.
En generisk klassdeklaration får inte användas System.Attribute
som en direkt eller indirekt basklass.
Exempel:
public class B : Attribute {} public class C<T> : B {} // Error – generic cannot be an attribute
slutexempel
22.2.2 Attributanvändning
Attributet AttributeUsage
(§22.5.2) används för att beskriva hur en attributklass kan användas.
AttributeUsage
har en positionsparameter (§22.2.3) som gör det möjligt för en attributklass att ange vilka typer av programentiteter som den kan användas på.
Exempel: I följande exempel definieras en attributklass med namnet
SimpleAttribute
som kan placeras på class_declarations och endast interface_declarations och visar flera användningsområden förSimple
attributet.[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class SimpleAttribute : Attribute { ... } [Simple] class Class1 {...} [Simple] interface Interface1 {...}
Även om det här attributet definieras med namnet
SimpleAttribute
, när det här attributet används, kan suffixetAttribute
utelämnas, vilket resulterar i det korta namnetSimple
. Exemplet ovan är alltså semantiskt likvärdigt med följande[SimpleAttribute] class Class1 {...} [SimpleAttribute] interface Interface1 {...}
slutexempel
AttributeUsage
har en namngiven parameter (§22.2.3), med namnet AllowMultiple
, som anger om attributet kan anges mer än en gång för en viss entitet. Om AllowMultiple
för en attributklass är sant är den attributklassen en attributklass med flera användningsområden och kan anges mer än en gång i en entitet. Om AllowMultiple
för en attributklass är false eller om den är ospecificerad är den attributklassen en attributklass för en enda användning och kan anges högst en gång i en entitet.
Exempel: I följande exempel definieras en attributklass med flera användningsområden med namnet
AuthorAttribute
och en klassdeklaration med två användningsområden förAuthor
attributet:[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 { ... }
slutexempel
AttributeUsage
har en annan namngiven parameter (§22.2.3), kallad Inherited
, som anger om attributet, när det anges i en basklass, också ärvs av klasser som härleds från den basklassen. Om Inherited
attributet för en attributklass är sant ärvs det attributet. Om Inherited
attributet för en attributklass är falskt ärvs inte det attributet. Om det är ospecificerat är standardvärdet sant.
En attributklass X
som inte har ett AttributeUsage
attribut kopplat till sig, som i
class X : Attribute { ... }
motsvarar följande:
[AttributeUsage(
AttributeTargets.All,
AllowMultiple = false,
Inherited = true)
]
class X : Attribute { ... }
22.2.3 Positionella och namngivna parametrar
Attributklasser kan ha positionsparametraroch namngivna parameterer. Varje offentlig instanskonstruktor för en attributklass definierar en giltig sekvens med positionsparametrar för den attributklassen. Varje icke-statiskt offentligt skrivfält och -egenskap för en attributklass definierar en namngiven parameter för attributklassen. För att en egenskap ska definiera en namngiven parameter ska den egenskapen ha både en offentlig get-accessor och en offentlig set-accessor.
Exempel: I följande exempel definieras en attributklass med namnet
HelpAttribute
som har en positionsparameter,url
, och en namngiven parameter,Topic
. Även om det är icke-statiskt och offentligt, definierar egenskapenUrl
inte en namngiven parameter, eftersom den inte är skrivskyddad. Två användningsområden för det här attributet visas också:[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 { }
slutexempel
22.2.4 Attributparametertyper
Typerna av positionella och namngivna parametrar för en attributklass är begränsade till attributparametertyperna, som är:
- En av följande typer: , , , , ,
bool
byte
, ,char
,double
,float
, ,int
,long
, ,sbyte
.short
string
uint
ulong
ushort
- Typen
object
. - Typen
System.Type
. - Uppräkningstyper.
- Endimensionella matriser av ovanstående typer.
- Ett konstruktorargument eller offentligt fält som inte har någon av dessa typer ska inte användas som en positionell eller namngiven parameter i en attributspecifikation.
22.3 Attributspecifikation
Attributspecifikation är tillämpningen av ett tidigare definierat attribut för en programentitet. Ett attribut är en del av ytterligare deklarativ information som anges för en programentitet. Attribut kan anges i globalt omfång (för att ange attribut för den innehållande sammansättningen eller modulen) och för type_declaration s (§14.7), class_member_declaration s (§15.3), interface_member_declaration s (§14.7), class_member_declarations (§15.3), interface_member_declarations (§18.4), struct_member_declarations (§16.3), enum_member_declarations (§19.2), accessor_declarations (§15.7.3), event_accessor_ deklarations (§15.8), element i parameter_lists (§15.6.2) och element i type_parameter_lists (§15.2.3).
Attribut anges i attributavsnitt. Ett attributavsnitt består av ett par hakparenteser som omger en kommaavgränsad lista med ett eller flera attribut. Den ordning i vilken attribut anges i en sådan lista och i vilken ordning avsnitt som är kopplade till samma programentitet är ordnade är inte betydande. Attributspecifikationerna [A][B]
, [B][A]
, [A, B]
och [B, A]
är till exempel likvärdiga.
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
;
För produktion global_attribute_target, och i texten nedan, ska identifieraren ha en stavning som är lika med eller assembly
, där likhet är den som definieras i module
. För produktion attribute_target, och i texten nedan, ska identifieraren ha en stavning som inte är lika med assembly
eller module
, med samma definition av likhet som ovan.
Ett attribut består av en attribute_name och en valfri lista med positionella och namngivna argument. Positionsargumenten (om några) föregår de namngivna argumenten. Ett positionsargument består av en attribute_argument_expression. Ett namngivet argument består av ett namn följt av ett likhetstecken följt av en attribute_argument_expression, som tillsammans begränsas av samma regler som enkel tilldelning. Ordningen på namngivna argument är inte betydande.
Obs! För enkelhetens skull tillåts ett avslutande kommatecken i en global_attribute_section och en attribute_section, precis som en tillåts i en array_initializer (§17.7). slutkommentar
Attribute_name identifierar en attributklass.
När ett attribut placeras på global nivå krävs en global_attribute_target_specifier . När global_attribute_target är lika med:
-
assembly
— Målet är den innehållande sammansättningen -
module
— målet är den modul som innehåller
Inga andra värden för global_attribute_target tillåts.
De standardiserade attribute_target namnen är event
, field
, method
, param
, property
, return
, , type
och typevar
. Dessa målnamn får endast användas i följande sammanhang:
-
event
— en händelse. -
field
— ett fält. En fältliknande händelse (dvs. en utan accessorer) (§15.8.2) och en automatiskt implementerad egenskap (§15.7.4) kan också ha ett attribut med detta mål. -
method
— en konstruktor, slutförare, metod, operatör, egenskaps hämta och ange accessorer, indexerare hämta och ange accessorer och händelse lägga till och ta bort accessorer. En fältliknande händelse (t.ex. en utan accessorer) kan också ha ett attribut med det här målet. -
param
– en egenskapsuppsättningsåtkomstor, en indexerare, händelsetillägg och borttagning av accessorer samt en parameter i en konstruktor, metod och operator. -
property
— en egenskap och en indexerare. -
return
— en ombud, metod, operatör, egenskap få accessor och indexerare få accessor. -
type
— ombud, klass, struct, uppräkning och gränssnitt. -
typevar
– en typparameter.
Vissa kontexter tillåter specifikationen av ett attribut på mer än ett mål. Ett program kan uttryckligen ange målet genom att inkludera en attribute_target_specifier. Utan en attribute_target_specifier tillämpas ett standardvärde, men en attribute_target_specifier kan användas för att bekräfta eller åsidosätta standardvärdet. Kontexterna löses på följande sätt:
- För ett attribut i en delegatdeklaration är standardmålet ombudet. Annars när attribute_target är lika med:
-
type
— Målet är ombudet -
return
— Målet är returvärdet
-
- För ett attribut i en metoddeklaration är standardmålet metoden. Annars när attribute_target är lika med:
-
method
— Målet är metoden -
return
— Målet är returvärdet
-
- För ett attribut i en operatordeklaration är standardmålet operatorn. Annars när attribute_target är lika med:
-
method
— Målet är operatorn -
return
— Målet är returvärdet
-
- För ett attribut på en get-åtkomstdeklaration för en egenskap eller indexeringsdeklaration är standardmålet den associerade metoden. Annars när attribute_target är lika med:
-
method
— målet är den associerade metoden -
return
— Målet är returvärdet
-
- För ett attribut som anges för en uppsättningsåtkomst för en egenskap eller indexeringsdeklaration är standardmålet den associerade metoden. Annars när attribute_target är lika med:
-
method
— målet är den associerade metoden -
param
— målet är den ensamma implicita parametern
-
- För ett attribut på en automatiskt implementerad egenskapsdeklaration är standardmålet egenskapen. Annars när attribute_target är lika med:
-
field
– målet är det kompilatorgenererade bakgrundsfältet för egenskapen
-
- För ett attribut som anges i en händelsedeklaration som utelämnar event_accessor_declarations är standardmålet händelsedeklarationen. Annars när attribute_target är lika med:
-
event
— Målet är händelsedeklarationen -
field
— Målet är fältet -
method
— Målen är metoderna
-
- När det gäller en händelsedeklaration som inte utelämnar event_accessor_declarations är standardmålet metoden.
-
method
— målet är den associerade metoden -
param
— målet är den ensamma parametern
-
I alla andra sammanhang är inkludering av en attribute_target_specifier tillåten men onödig.
Exempel: en klassdeklaration kan antingen inkludera eller utelämna specificeraren
type
:[type: Author("Brian Kernighan")] class Class1 {} [Author("Dennis Ritchie")] class Class2 {}
slutexempel.
En implementering kan acceptera andra attribute_target, vars syften är implementeringsdefinierade. En implementering som inte känner igen en sådan attribute_target ska utfärda en varning och ignorera den innehållande attribute_section.
Enligt konventionen namnges attributklasser med suffixet Attribute
. En attribute_name kan antingen inkludera eller utelämna det här suffixet. Mer specifikt löses en attribute_name på följande sätt:
- Om den mest högra identifieraren för attribute_name är en ordagrann identifierare (§6.4.3) löses attribute_name som en type_name (§7.8). Om resultatet inte är en typ som härleds från
System.Attribute
uppstår ett kompileringsfel. - Annars
-
Attribute_name löses som en type_name (§7.8) förutom att eventuella fel undertrycks. Om den här lösningen lyckas och resulterar i en typ som härletts från
System.Attribute
är typen resultatet av det här steget. - Tecknen
Attribute
läggs till i den högra identifieraren i attribute_name och den resulterande strängen med token löses som en type_name (§7.8) förutom att eventuella fel ignoreras. Om den här lösningen lyckas och resulterar i en typ som härletts frånSystem.Attribute
är typen resultatet av det här steget.
-
Attribute_name löses som en type_name (§7.8) förutom att eventuella fel undertrycks. Om den här lösningen lyckas och resulterar i en typ som härletts från
Om exakt ett av de två stegen ovan resulterar i en typ som härletts från System.Attribute
är den typen resultatet av attribute_name. Annars uppstår ett kompileringsfel.
Exempel: Om en attributklass hittas både med och utan det här suffixet finns en tvetydighet och ett kompileringsfelresultat. Om attribute_name stavas så att dess mest högra identifierare är en ordagrann identifierare (§6.4.3), matchas endast ett attribut utan suffix, vilket gör det möjligt att lösa en sådan tvetydighet. Exemplet
[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 {}
visar två attributklasser med namnet
Example
ochExampleAttribute
. Attributet[Example]
är tvetydigt eftersom det kan referera till antingenExample
ellerExampleAttribute
. Med en ordagrann identifierare kan den exakta avsikten anges i sådana sällsynta fall. Attributet[ExampleAttribute]
är inte tvetydigt (även om det skulle vara om det fanns en attributklass med namnetExampleAttributeAttribute
!). Om deklarationen för klassenExample
tas bort refererar båda attributen till attributklassen med namnetExampleAttribute
, enligt följande:[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 {}
slutexempel
Det är ett kompileringsfel att använda en attributklass för en enda användning mer än en gång på samma entitet.
Exempel: Exemplet
[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 {}
resulterar i ett kompileringsfel eftersom det försöker använda
HelpString
, vilket är en attributklass för en enda användning, mer än en gång i deklarationen avClass1
.slutexempel
Ett uttryck E
är en attribute_argument_expression om alla följande instruktioner är sanna:
- Typen av
E
är en attributparametertyp (§22.2.4). - Vid kompilering kan värdet
E
för matchas till något av följande:- Ett konstant värde.
- Ett
System.Type
objekt som erhålls med hjälp av en typeof_expression (§12.8.18) som anger en icke-generisk typ, en sluten konstruktionstyp (§8.4.3) eller en obundna generisk typ (§8.4.4), men inte en öppen typ (§8.4.3). - En endimensionell matris med attribute_argument_expressions.
Exempel:
[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; }
slutexempel
Attributen för en typ som deklareras i flera delar bestäms genom att attributen för var och en av dess delar kombineras i en ospecificerad ordning. Om samma attribut placeras på flera delar motsvarar det att ange attributet flera gånger för typen.
Exempel: De två delarna:
[Attr1, Attr2("hello")] partial class A {} [Attr3, Attr2("goodbye")] partial class A {}
motsvarar följande enda deklaration:
[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")] class A {}
slutexempel
Attribut för typparametrar kombineras på samma sätt.
22.4 Attributinstanser
22.4.1 Allmänt
En attributinstans är en instans som representerar ett attribut vid körning. Ett attribut definieras med en attributklass, positionsargument och namngivna argument. En attributinstans är en instans av attributklassen som initieras med de positionella och namngivna argumenten.
Hämtning av en attributinstans omfattar både kompileringstid och körningsbearbetning, enligt beskrivningen i följande underklienter.
22.4.2 Kompilering av ett attribut
Kompilering av ett attribut med attributklassen T
, positional_argument_listP
, och som anges i en programentitet N
kompileras till en sammansättning E
via följande steg:
- Följ stegen för kompileringstidsbearbetning för att kompilera en object_creation_expression av formulärets nya
T(P)
. De här stegen resulterar antingen i ett kompileringsfel eller fastställer en instanskonstruktorC
T
som kan anropas vid körning. - Om
C
det inte finns någon offentlig tillgänglighet uppstår ett kompileringsfel. - För varje named_argument
Arg
iN
:- Låt oss
Name
vara identifierarenför named_argumentArg
. -
Name
ska identifiera ett offentligt fält eller en egenskap för icke-statiskt skrivskyddat fält eller en egenskap påT
. OmT
det inte finns något sådant fält eller en sådan egenskap uppstår ett kompileringsfel.
- Låt oss
- Om något av värdena inom eller något av värdena inom
P
är av typenN
och värdet inte är välformat enligt Unicode Standard definieras det om det kompilerade värdet är lika med det körningsvärde som hämtas (System.String
).Obs! Till exempel är en sträng som innehåller en kodenhet med hög surrogattyp UTF-16 som inte omedelbart följs av en låg surrogatkodenhet inte välformulerad. slutkommentar
- Lagra följande information (för körningsinstansiering av attributet) i sammansättningsutdata av kompilatorn som ett resultat av kompileringen av programmet som innehåller attributet: attributklassen
T
, instanskonstruktornC
påT
, positional_argument_listP
, named_argument_listN
och den associerade programentitetenE
, med värdena lösta helt vid kompileringstid.
22.4.3 Körningshämtning av en attributinstans
Med hjälp av de termer som definieras i §22.4.2 kan attributinstansen som representeras av , T
, C
och och P
som är associerad med N
hämtas vid körning från sammansättningen E
med hjälp av A
följande steg:
- Följ stegen för körningsbearbetning för att köra en . De här stegen resulterar antingen i ett undantag eller skapar en instans
O
avT
. - För varje named_argument
Arg
iN
, i ordning:- Låt oss
Name
vara identifierarenför named_argumentArg
. OmName
inte identifierar ett icke-statiskt offentligt läs-skrivfält eller -egenskap påO
genereras ett undantag. - Låt oss
Value
vara resultatet av utvärderingen av attribute_argument_expression avArg
. - Om
Name
identifierar ett fält påO
anger du fältet tillValue
. - Annars identifierar Name en egenskap på
O
. Ange den här egenskapen till Värde. - Resultatet är
O
, en instans av attributklassenT
som har initierats med positional_argument_listP
och named_argument_listN
.
- Låt oss
Obs! Formatet för att
T
lagra ,C
,P
(N
och associera det medE
) iA
och mekanismen för att angeE
och hämtaT
,C
,P
frånN
A
(och därmed hur en attributinstans hämtas vid körning) ligger utanför omfånget för den här specifikationen. slutkommentar
Exempel: I en implementering av CLI
Help
kan attributinstanserna i sammansättningen som skapats genom kompilering av exempelprogrammet i §22.2.3 hämtas med följande program: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}"); } } } }
slutexempel
22.5 Reserverade attribut
22.5.1 Allmänt
Ett antal attribut påverkar språket på något sätt. Dessa attribut är:
-
System.AttributeUsageAttribute
(§22.5.2), som används för att beskriva hur en attributklass kan användas. -
System.Diagnostics.ConditionalAttribute
(§22.5.3), är en attributklass med flera användningsområden som används för att definiera villkorsstyrda metoder och klasser för villkorsstyrda attribut. Det här attributet anger ett villkor genom att testa en villkorlig kompileringssymbol. -
System.ObsoleteAttribute
(§22.5.4), som används för att markera en medlem som föråldrad. -
System.Runtime.CompilerServices.AsyncMethodBuilderAttribute
(§22.5.5), som används för att upprätta en aktivitetsbyggare för en asynkron metod. -
System.Runtime.CompilerServices.CallerLineNumberAttribute
(§22.5.6.2),System.Runtime.CompilerServices.CallerFilePathAttribute
(§22.5.6.3) ochSystem.Runtime.CompilerServices.CallerMemberNameAttribute
(§22.5.6.4), som används för att tillhandahålla information om samtalskontexten till valfria parametrar.
Attributen för statisk analys som kan ogiltigförklaras (§22.5.7) kan förbättra korrektheten i varningar som genereras för null-värden och nulltillstånd (§8.9.5).
En körningsmiljö kan ge ytterligare implementeringsdefinierade attribut som påverkar körningen av ett C#-program.
22.5.2 AttributeUsage-attributet
Attributet AttributeUsage
används för att beskriva hur attributklassen kan användas.
En klass som är dekorerad med AttributeUsage
attributet ska härledas från System.Attribute
, antingen direkt eller indirekt. Annars uppstår ett kompileringsfel.
Obs! Ett exempel på hur du använder det här attributet finns i §22.2.2. slutkommentar
22.5.3 Villkorsattributet
22.5.3.1 Allmänt
Attributet Conditional
aktiverar definitionen av villkorsstyrda metoder och klasser för villkorsstyrda attribut.
22.5.3.2 Villkorsstyrda metoder
En metod som är dekorerad med Conditional
attributet är en villkorsstyrd metod. Varje villkorsstyrd metod associeras därför med de villkorsstyrda kompileringssymbolerna som deklareras i dess Conditional
attribut.
Exempel:
class Eg { [Conditional("ALPHA")] [Conditional("BETA")] public static void M() { // ... } }
deklarerar som en villkorsstyrd metod som är associerad med de två villkorliga kompileringssymbolerna
Eg.M
ALPHA
ochBETA
.slutexempel
Ett anrop till en villkorsstyrd metod ingår om en eller flera av dess associerade villkorsstyrda kompileringssymboler definieras vid anropspunkten, annars utelämnas anropet.
En villkorsstyrd metod omfattas av följande begränsningar:
- Villkorsmetoden ska vara en metod i en class_declaration eller struct_declaration. Ett kompileringsfel inträffar om
Conditional
attributet anges på en metod i en gränssnittsdeklaration. - Villkorsmetoden ska ha en returtyp av
void
. - Den villkorsstyrda metoden får inte markeras med
override
modifieraren. En villkorsstyrd metod kan dock markeras medvirtual
modifieraren. Åsidosättningar av en sådan metod är implicit villkorade och ska inte uttryckligen markeras med ettConditional
attribut. - Den villkorsstyrda metoden får inte vara en implementering av en gränssnittsmetod. Annars uppstår ett kompileringsfel.
- Parametrarna för den villkorsstyrda metoden får inte vara utdataparametrar.
Dessutom uppstår ett kompileringsfel om ett ombud skapas från en villkorsstyrd metod.
Exempel: Exemplet
#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(); } }
deklareras som en villkorsstyrd
Class1.M
metod.Class2
's-metodenTest
anropar den här metoden. Eftersom den villkorliga kompileringssymbolenDEBUG
har definierats anropas omClass2.Test
denM
anropas . Om symbolenDEBUG
inte hade definierats skulle denClass2.Test
inte anropaClass1.M
.slutexempel
Det är viktigt att förstå att inkludering eller exkludering av ett anrop till en villkorsstyrd metod styrs av de villkorsstyrda kompileringssymbolerna vid tidpunkten för anropet.
Exempel: I följande kod
// 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 } }
klasserna
Class2
och var ochClass3
en innehåller anrop till den villkorsstyrda metodenClass1.F
, som är villkorad baserat på om har definierats eller inteDEBUG
. Eftersom den här symbolen definieras i kontexten förClass2
men inteClass3
inkluderas anropet tillF
iClass2
, medan anropet tillF
iClass3
utelämnas.slutexempel
Användningen av villkorsstyrda metoder i en arvskedja kan vara förvirrande. Anrop som görs till en villkorsstyrd metod via base
, i formuläret base.M
, omfattas av de normala anropsreglerna för villkorsstyrd metod.
Exempel: I följande kod
// 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
innehåller ett anrop till denM
definierade i basklassen. Det här anropet utelämnas eftersom basmetoden är villkorad baserat på förekomsten av symbolenDEBUG
, som är odefinierad. Metoden skriver därför endast till -konsolenClass2.M executed
. Omdömesgill användning av pp_declarations kan eliminera sådana problem.slutexempel
22.5.3.3 Villkorliga attributklasser
En attributklass (§22.2) som är dekorerad med ett eller flera Conditional
attribut är en villkorsstyrd attributklass. En villkorsstyrd attributklass associeras därför med de villkorsstyrda kompileringssymbolerna som deklareras i dess Conditional
attribut.
Exempel:
[Conditional("ALPHA")] [Conditional("BETA")] public class TestAttribute : Attribute {}
deklarerar som en villkorsstyrd
TestAttribute
attributklass som är associerad med de villkorsstyrda kompileringssymbolernaALPHA
ochBETA
.slutexempel
Attributspecifikationer (§22.3) för ett villkorsattribut inkluderas om en eller flera av dess associerade villkorsstyrda kompileringssymboler definieras vid specifikationspunkten, annars utelämnas attributspecifikationen.
Det är viktigt att observera att inkludering eller exkludering av en attributspecifikation för en villkorsstyrd attributklass styrs av de villkorsstyrda kompileringssymbolerna vid specifikationens punkt.
Exempel: I exemplet
// 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 {}
klasserna
Class1
ochClass2
är dekorerade med attributetTest
, som är villkorsstyrd baserat på om de har definierats eller inteDEBUG
. Eftersom den här symbolen definieras i kontexten förClass1
men inteClass2
inkluderas specifikationen för testattributet påClass1
, medan specifikationenTest
för attributet påClass2
utelämnas.slutexempel
22.5.4 Attributet föråldrad
Attributet Obsolete
används för att markera typer och medlemmar av typer som inte längre ska användas.
Om ett program använder en typ eller medlem som är dekorerad med attributet Obsolete
, ska en kompilator utfärda en varning eller ett fel. Mer specifikt ska en kompilator utfärda en varning om det inte finns någon felparameter eller om felparametern anges och har värdet false
. En kompilator ska utfärda ett fel om felparametern anges och har värdet true
.
Exempel: I följande kod
[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(); } }
klassen
A
är dekorerad med attributetObsolete
. Varje användning avA
i resulterar iMain
en varning som innehåller det angivna meddelandet "Den här klassen är föråldrad; använda klassenB
i stället".slutexempel
22.5.5 Attributet AsyncMethodBuilder
Detta attribut beskrivs i §15.15.1.
22.5.6 Caller-info-attribut
22.5.6.1 Allmänt
För ändamål som loggning och rapportering är det ibland användbart för en funktionsmedlem att hämta viss kompileringstidsinformation om den anropande koden. Attributen caller-info ger ett sätt att skicka sådan information transparent.
När en valfri parameter kommenteras med något av anropar-info-attributen, innebär utelämnande av motsvarande argument i ett anrop inte nödvändigtvis att standardparametervärdet ersätts. Om den angivna informationen om anropskontexten i stället är tillgänglig skickas den informationen som argumentvärde.
Exempel:
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); }
Ett anrop till utan argument skulle skriva ut linjenumret och filsökvägen för
Log()
anropet, samt namnet på medlemmen där anropet inträffade.slutexempel
Anropar-info-attribut kan förekomma på valfria parametrar var som helst, inklusive i ombudsdeklarationer. De specifika anropar-info-attributen har dock begränsningar för vilka typer av parametrar de kan tillskriva, så att det alltid blir en implicit konvertering från ett ersatt värde till parametertypen.
Det är ett fel att ha samma anropar-info-attribut på en parameter för både definiera och implementera en del av en partiell metoddeklaration. Endast anropar-info-attribut i den definierande delen tillämpas, medan attribut för anropare-info som endast förekommer i implementeringsdelen ignoreras.
Uppringarens information påverkar inte överbelastningsupplösningen. Eftersom de tillskrivna valfria parametrarna fortfarande utelämnas från anroparens källkod ignorerar överlagringsmatchningen dessa parametrar på samma sätt som andra utelämnade valfria parametrar (§12.6.4).
Uppringarinformation ersätts endast när en funktion uttryckligen anropas i källkoden. Implicita anrop, till exempel implicita överordnade konstruktoranrop, har ingen källplats och ersätter inte uppringarinformation. Dessutom ersätter inte anrop som är dynamiskt bundna uppringarinformation. När en tillskriven parameter för anropare-info utelämnas i sådana fall används det angivna standardvärdet för parametern i stället.
Ett undantag är frågeuttryck. Dessa betraktas som syntaktiska expansioner, och om anropen de expanderar för att utelämna valfria parametrar med anropar-info-attribut ersätts uppringarens information. Platsen som används är platsen för frågesatsen som anropet genererades från.
Om fler än ett anropar-info-attribut anges för en viss parameter identifieras de i följande ordning: CallerLineNumber
, CallerFilePath
, CallerMemberName
. Överväg följande parameterdeklaration:
[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ...
CallerLineNumber
har företräde och de andra två attributen ignoreras. Om CallerLineNumber
utelämnades skulle CallerFilePath
ha företräde och CallerMemberName
ignoreras. Den lexikala ordningen på dessa attribut är irrelevant.
22.5.6.2 Attributet CallerLineNumber
Attributet System.Runtime.CompilerServices.CallerLineNumberAttribute
tillåts för valfria parametrar när det finns en implicit standardkonvertering (§10.4.2) från konstantvärdet int.MaxValue
till parameterns typ. Detta säkerställer att alla icke-negativa radnummer upp till det värdet kan skickas utan fel.
Om ett funktionsanrop från en plats i källkod utelämnar en valfri parameter med CallerLineNumberAttribute
, används en numerisk literal som representerar platsens radnummer som ett argument till anropet i stället för standardparametervärdet.
Om anropet sträcker sig över flera rader är den valda raden implementeringsberoende.
Linjenumret kan påverkas av #line
direktiv (§6.5.8).
22.5.6.3 Attributet CallerFilePath
Attributet System.Runtime.CompilerServices.CallerFilePathAttribute
tillåts för valfria parametrar när det finns en implicit standardkonvertering (§10.4.2) från string
parameterns typ.
Om en funktionsanrop från en plats i källkod utelämnar en valfri parameter med CallerFilePathAttribute
, används en strängliteral som representerar platsens filsökväg som ett argument till anropet i stället för standardparametervärdet.
Filsökvägens format är implementeringsberoende.
Filsökvägen kan påverkas av #line
direktiv (§6.5.8).
22.5.6.4 Attributet CallerMemberName
Attributet System.Runtime.CompilerServices.CallerMemberNameAttribute
tillåts för valfria parametrar när det finns en implicit standardkonvertering (§10.4.2) från string
parameterns typ.
Om ett funktionsanrop från en plats i en funktionsmedlems brödtext eller inom ett attribut som tillämpas på själva funktionsmedlemmen eller dess returtyp utelämnar parametrar eller typparametrar i källkoden en valfri parameter med CallerMemberNameAttribute
, används en strängliteral som representerar namnet på medlemmen som ett argument till anropet i stället för standardparametervärdet.
För anrop som inträffar inom generiska metoder används endast själva metodnamnet, utan listan över typparametrar.
För anrop som sker inom explicita gränssnittsmedlemimplementeringar används endast själva metodnamnet, utan föregående gränssnittskvalifikation.
För anrop som inträffar i egenskaps- eller händelseåtkomster är det medlemsnamn som används egenskapen eller själva händelsen.
För anrop som inträffar inom indexerare är det medlemsnamn som används det som tillhandahålls av en IndexerNameAttribute
(§22.6) på indexerarens medlem, om det finns, eller standardnamnet Item
på annat sätt.
För anrop som inträffar inom fält- eller händelseinitierare är det medlemsnamn som används namnet på fältet eller händelsen som initieras.
För anrop som inträffar inom deklarationer av instanskonstruktorer, statiska konstruktorer, finalizers och operatorer är medlemsnamnet som används implementeringsberoende.
22.5.7 Kodanalysattribut
22.5.7.1 Allmänt
Attributen i det här avsnittet används för att ge ytterligare information för att stödja en kompilator som tillhandahåller null- och null-tillståndsdiagnostik (§8.9.5). En kompilator krävs inte för att utföra någon null-tillståndsdiagnostik. Förekomsten eller frånvaron av dessa attribut påverkar inte språket eller beteendet för ett program. En kompilator som inte tillhandahåller null-tillståndsdiagnostik ska läsa och ignorera förekomsten av dessa attribut. En kompilator som tillhandahåller null-tillståndsdiagnostik ska använda den betydelse som definieras i det här avsnittet för något av dessa attribut som används för att informera dess diagnostik.
Kodanalysattributen deklareras i namnområdet System.Diagnostics.CodeAnalysis
.
Attribut | Betydelse |
---|---|
AllowNull (§22.5.7.2) |
Ett argument som inte kan null-värdet kan vara null. |
DisallowNull (§22.5.7.3) |
Ett null-argument får aldrig vara null. |
MaybeNull (§22.5.7.6) |
Ett icke-nullbart returvärde kan vara null. |
NotNull (§22.5.7.8) |
Ett null-returvärde blir aldrig null. |
MaybeNullWhen (§22.5.7.7) |
Ett icke-nullbart argument kan vara null när metoden returnerar det angivna bool värdet. |
NotNullWhen (§22.5.7.10) |
Ett null-argument är inte null när metoden returnerar det angivna bool värdet. |
NotNullIfNotNull (§22.5.7.9) |
Ett returvärde är inte null om argumentet för den angivna parametern inte är null. |
DoesNotReturn (§22.5.7.4) |
Den här metoden returnerar aldrig. |
DoesNotReturnIf (§22.5.7.5) |
Den här metoden returnerar aldrig om den associerade bool parametern har det angivna värdet. |
Följande avsnitt i §22.5.7.1 är villkorligt normativa.
22.5.7.2 Attributet AllowNull
Anger att ett null-värde tillåts som indata även om motsvarande typ inte tillåter det.
Exempel: Överväg följande läs-/skrivegenskap som aldrig returneras
null
eftersom den har ett rimligt standardvärde. En användare kan dock ge null till den angivna åtkomstorn för att ange egenskapen till det standardvärdet.#nullable enable public class X { [AllowNull] public string ScreenName { get => _screenName; set => _screenName = value ?? GenerateRandomScreenName(); } private string _screenName = GenerateRandomScreenName(); private static string GenerateRandomScreenName() => ...; }
Med tanke på följande användning av egenskapens uppsättningsåtkomstor
var v = new X(); v.ScreenName = null; // may warn without attribute AllowNull
utan attributet kan en kompilator generera en varning eftersom egenskapen av typen icke-nullbar verkar vara inställd på ett null-värde. Förekomsten av attributet undertrycker varningen. slutexempel
22.5.7.3 Attributet DisallowNull
Anger att ett null-värde inte tillåts som indata även om motsvarande typ tillåter det.
Exempel: Tänk på följande egenskap där null är standardvärdet, men klienter kan bara ange värdet som ett värde som inte är 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; }
Get-accessorn kan returnera standardvärdet för
null
, så en kompilator kan varna för att den måste kontrolleras innan åtkomst. Dessutom varnar den anropare för att anropare inte uttryckligen ska ställa in värdet null, även om det kan vara null. slutexempel
22.5.7.4 Attributet DoesNotReturn
Anger att en viss metod aldrig returnerar.
Exempel: Tänk på följande:
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; }
Förekomsten av attributet hjälper en kompilator på flera olika sätt. Först kan en kompilator utfärda en varning om det finns en kodväg där metoden kan avslutas utan att utlösa ett undantag. För det andra kan en kompilator ignorera nullbara varningar i valfri kod efter ett anrop till metoden tills en lämplig catch-sats hittas. För det tredje påverkar inte den oåtkomliga koden några null-tillstånd.
Attributet ändrar inte nåbarheten (§13.2) eller en bestämd tilldelningsanalys (§9.4) baserat på förekomsten av detta attribut. Det används endast för att påverka nullabilitetsvarningar. slutexempel
22.5.7.5 Attributet DoesNotReturnIf
Anger att en viss metod aldrig returnerar om den associerade bool
parametern har det angivna värdet.
Exempel: Tänk på följande:
#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!; }
slutexempel
22.5.7.6 Attributet MaybeNull
Anger att ett icke-nullbart returvärde kan vara null.
Exempel: Överväg följande generiska metod:
#nullable enable public T? Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }
Tanken med den här koden är att om
T
ersätts avstring
blirT?
en null-kommentar. Den här koden är dock inte laglig eftersomT
den inte är begränsad till en referenstyp. Men att lägga till det här attributet löser problemet:#nullable enable [return: MaybeNull] public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }
Attributet informerar anropare om att kontraktet innebär en icke-nullbar typ, men returvärdet kan faktiskt vara
null
. slutexempel
22.5.7.7 Attributet MaybeNullWhen
Anger att ett icke-nullbart argument kan vara null
när metoden returnerar det angivna bool
värdet. Detta liknar MaybeNull
attributet (§22.5.7.6), men innehåller en parameter för det angivna returvärdet.
22.5.7.8 Attributet NotNull
Anger att ett null-värde aldrig null
blir om metoden returnerar (i stället för att kasta).
Exempel: Tänk på följande:
#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); }
När null-referenstyper är aktiverade kompileras metoden
ThrowWhenNull
utan varningar. När metoden returnerasvalue
är argumentet garanterat intenull
. Det är dock acceptabelt att anropaThrowWhenNull
med en null-referens. slutexempel
22.5.7.9 Attributet NotNullIfNotNull
Anger att ett returvärde inte null
är om argumentet för den angivna parametern inte null
är .
Exempel: Null-tillståndet för ett returvärde kan bero på nulltillståndet för ett eller flera argument. För att hjälpa en kompilators analys när en metod alltid returnerar ett värde som inte är null när vissa argument inte
null
kan attributetNotNullIfNotNull
användas. Tänk på följande metod:#nullable enable string GetTopLevelDomainFromFullUrl(string url) { ... }
Om argumentet
url
intenull
är returnerasnull
inte. När null-referenser är aktiverade fungerar signaturen korrekt, förutsatt att API:et aldrig accepterar ett null-argument. Men om argumentet kan vara null kan returvärdet också vara null. Om du vill uttrycka kontraktet korrekt kommenterar du den här metoden på följande sätt:#nullable enable [return: NotNullIfNotNull("url")] string? GetTopLevelDomainFromFullUrl(string? url) { ... }
slutexempel
22.5.7.10 Attributet NotNullWhen
Anger att ett null-argument inte kommer att vara null
när metoden returnerar det angivna bool
värdet.
Exempel: Biblioteksmetoden
String.IsNullOrEmpty(String)
returnerartrue
när argumentet ärnull
eller en tom sträng. Det är en form av null-check: Anropare behöver inte null-kontrollera argumentet om metoden returnerarfalse
. Om du vill göra en metod som denna nullbar medveten gör du parametertypen till en nullbar referenstyp och lägger till attributet NotNullWhen:#nullable enable bool IsNullOrEmpty([NotNullWhen(false)] string? value) { ... }
slutexempel
22.6 Attribut för interoperation
För samarbete med andra språk kan en indexerare implementeras med hjälp av indexerade egenskaper. Om det inte finns något IndexerName
attribut för en indexerare används namnet Item
som standard. Med IndexerName
attributet kan en utvecklare åsidosätta det här standardvärdet och ange ett annat namn.
Exempel: Som standard är
Item
indexerarens namn . Detta kan åsidosättas enligt följande:[System.Runtime.CompilerServices.IndexerName("TheItem")] public int this[int index] { get { ... } set { ... } }
Indexerarens namn är
TheItem
nu .slutexempel
ECMA C# draft specification