Diverse attribut som tolkas av C#-kompilatorn
Det finns flera attribut som kan tillämpas på element i koden som lägger till semantisk betydelse för dessa element:
-
Conditional
: Gör körningen av en metod beroende av en förprocessoridentifierare. -
Obsolete
: Markera en typ eller medlem för (potentiell) framtida borttagning. -
AttributeUsage
: Deklarera språkelementen där ett attribut kan tillämpas. -
AsyncMethodBuilder
: Deklarera en asynkron metodbyggaretyp. -
InterpolatedStringHandler
: Definiera en interpolerad strängbyggare för ett känt scenario. -
ModuleInitializer
: Deklarera en metod som initierar en modul. -
SkipLocalsInit
: Elide koden som initierar lokal variabellagring till 0. -
UnscopedRef
: Deklarera att enref
variabel som normalt tolkas somscoped
ska behandlas som oscoped. -
OverloadResolutionPriority
: Lägg till ett tiebreaker-attribut för att påverka överbelastningsupplösningen för eventuellt tvetydiga överlagringar. -
Experimental
: Markera en typ eller medlem som experimentell.
Kompilatorn använder dessa semantiska betydelser för att ändra dess utdata och rapportera möjliga misstag av utvecklare som använder din kod.
Conditional
-attribut
Attributet Conditional
gör körningen av en metod beroende av en förbearbetningsidentifierare. Attributet Conditional
är ett alias för ConditionalAttributeoch kan tillämpas på en metod eller en attributklass.
I följande exempel Conditional
tillämpas på en metod för att aktivera eller inaktivera visning av programspecifik diagnostikinformation:
#define TRACE_ON
using System.Diagnostics;
namespace AttributeExamples;
public class Trace
{
[Conditional("TRACE_ON")]
public static void Msg(string msg)
{
Console.WriteLine(msg);
}
}
public class TraceExample
{
public static void Main()
{
Trace.Msg("Now in Main...");
Console.WriteLine("Done.");
}
}
Om identifieraren TRACE_ON
inte har definierats visas inte spårningsutdata. Utforska själv i det interaktiva fönstret.
Attributet Conditional
används ofta med identifieraren DEBUG
för att aktivera spårnings- och loggningsfunktioner för felsökningsversioner men inte i versionsversioner, som du ser i följande exempel:
[Conditional("DEBUG")]
static void DebugMethod()
{
}
När en metod markerad villkorsstyrd anropas avgör förekomsten eller frånvaron av den angivna förbearbetningssymbolen om kompilatorn inkluderar eller utelämnar anrop till metoden. Om symbolen har definierats inkluderas anropet. annars utelämnas anropet. En villkorsstyrd metod måste vara en metod i en klass- eller structdeklaration och måste ha en void
returtyp. Att använda Conditional
är renare, mer elegant och mindre felbenäget än att omsluta metoder i #if…#endif
block.
Om en metod har flera Conditional
attribut innehåller kompilatorn anrop till metoden om en eller flera villkorssymboler definieras (symbolerna är logiskt länkade med hjälp av OPERATORN OR). I följande exempel resulterar förekomsten av antingen A
eller B
i ett metodanrop:
[Conditional("A"), Conditional("B")]
static void DoIfAorB()
{
// ...
}
Använda Conditional
med attributklasser
Attributet Conditional
kan också tillämpas på en attributklassdefinition. I följande exempel lägger det anpassade attributet Documentation
till information till metadata om DEBUG
det har definierats.
[Conditional("DEBUG")]
public class DocumentationAttribute : System.Attribute
{
string text;
public DocumentationAttribute(string text)
{
this.text = text;
}
}
class SampleClass
{
// This attribute will only be included if DEBUG is defined.
[Documentation("This method displays an integer.")]
static void DoWork(int i)
{
System.Console.WriteLine(i.ToString());
}
}
Obsolete
-attribut
Attributet Obsolete
markerar ett kodelement som inte längre rekommenderas för användning. Användning av en entitet markerad som föråldrad genererar en varning eller ett fel. Attributet Obsolete
är ett engångsattribut och kan tillämpas på alla entiteter som tillåter attribut.
Obsolete
är ett alias för ObsoleteAttribute.
I följande exempel Obsolete
tillämpas attributet på klassen A
och metoden B.OldMethod
. Eftersom det andra argumentet för attributkonstruktorn som tillämpas på B.OldMethod
är inställt på true
, orsakar den här metoden ett kompilatorfel, medan användning av klassen A
ger en varning. Att anropa B.NewMethod
genererar dock ingen varning eller ett fel. När du till exempel använder den med tidigare definitioner genererar följande kod två varningar och ett fel:
namespace AttributeExamples
{
[Obsolete("use class B")]
public class A
{
public void Method() { }
}
public class B
{
[Obsolete("use NewMethod", true)]
public void OldMethod() { }
public void NewMethod() { }
}
public static class ObsoleteProgram
{
public static void Main()
{
// Generates 2 warnings:
A a = new A();
// Generate no errors or warnings:
B b = new B();
b.NewMethod();
// Generates an error, compilation fails.
// b.OldMethod();
}
}
}
Strängen som anges som det första argumentet till attributkonstruktorn visas som en del av varningen eller felet. Två varningar för klassen A
genereras: en för deklarationen av klassreferensen och en för klasskonstruktorn. Attributet Obsolete
kan användas utan argument, men en förklaring av vad du ska använda i stället rekommenderas. Du kan använda konstant stränginterpolation och operatorn nameof
för att se till att namnen matchar:
public class B
{
[Obsolete($"use {nameof(NewMethod)} instead", true)]
public void OldMethod() { }
public void NewMethod() { }
}
Experimental
-attribut
Från och med C# 12 kan typer, metoder och sammansättningar markeras med System.Diagnostics.CodeAnalysis.ExperimentalAttribute för att indikera en experimentell funktion. Kompilatorn utfärdar en varning om du kommer åt en metod eller skriver kommenterad med ExperimentalAttribute. Alla typer som deklareras i en sammansättning eller modul som markerats Experimental
med attributet är experimentella. Kompilatorn utfärdar en varning om du har åtkomst till någon av dem. Du kan inaktivera dessa varningar för att testa en experimentell funktion.
Varning
Experimentella funktioner kan komma att ändras. API:erna kan ändras eller tas bort i framtida uppdateringar. Att inkludera experimentella funktioner är ett sätt för biblioteksförfattare att få feedback om idéer och begrepp för framtida utveckling. Var mycket försiktig när du använder alla funktioner som markerats som experimentella.
Du kan läsa mer information om Experimental
attributet i funktionsspecifikationen.
SetsRequiredMembers
-attribut
Attributet SetsRequiredMembers
informerar kompilatorn om att en konstruktor anger alla required
medlemmar i den klassen eller structen. Kompilatorn förutsätter att alla konstruktorer med System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute attributet initierar alla required
medlemmar. All kod som anropar en sådan konstruktor behöver inte objektinitierare för att ange nödvändiga medlemmar.
SetsRequiredMembers
Att lägga till attributet är främst användbart för positionella poster och primära konstruktorer.
AttributeUsage
-attribut
Attributet AttributeUsage
avgör hur en anpassad attributklass kan användas.
AttributeUsageAttribute är ett attribut som du använder för anpassade attributdefinitioner. Med AttributeUsage
attributet kan du styra:
- Vilka programelement attributet kan tillämpas på. Om du inte begränsar dess användning kan ett attribut tillämpas på något av följande programelement:
- Sammansättning
- Modul
- Fält
- Event
- Metod
- Parameter
- Property
- Returnera
- Typ
- Om ett attribut kan tillämpas på ett enda programelement flera gånger.
- Om härledda klasser ärver attribut.
Standardinställningarna ser ut som i följande exempel när de tillämpas explicit:
[AttributeUsage(AttributeTargets.All,
AllowMultiple = false,
Inherited = true)]
class NewAttribute : Attribute { }
I det här exemplet NewAttribute
kan klassen tillämpas på alla programelement som stöds. Men den kan bara tillämpas en gång på varje entitet. Härledda klasser ärver attributet som tillämpas på en basklass.
Argumenten AllowMultiple och Inherited är valfria, så följande kod har samma effekt:
[AttributeUsage(AttributeTargets.All)]
class NewAttribute : Attribute { }
Det första AttributeUsageAttribute argumentet måste vara ett eller flera element i AttributeTargets uppräkningen. Flera måltyper kan länkas tillsammans med OR-operatorn, som följande exempel visar:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
class NewPropertyOrFieldAttribute : Attribute { }
Attribut kan tillämpas på antingen egenskapen eller bakgrundsfältet för en automatiskt implementerad egenskap. Attributet gäller för egenskapen, såvida du inte anger field
specificeraren för attributet. Båda visas i följande exempel:
class MyClass
{
// Attribute attached to property:
[NewPropertyOrField]
public string Name { get; set; } = string.Empty;
// Attribute attached to backing field:
[field: NewPropertyOrField]
public string Description { get; set; } = string.Empty;
}
AllowMultiple Om argumentet är true
kan det resulterande attributet tillämpas mer än en gång på en enda entitet, som du ser i följande exempel:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
class MultiUse : Attribute { }
[MultiUse]
[MultiUse]
class Class1 { }
[MultiUse, MultiUse]
class Class2 { }
I det här fallet MultiUseAttribute
kan tillämpas flera gånger eftersom AllowMultiple
är inställt på true
. Båda formaten som visas för att tillämpa flera attribut är giltiga.
Om Inherited är false
är , ärver inte härledda klasser attributet från en tillskriven basklass. Till exempel:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
class NonInheritedAttribute : Attribute { }
[NonInherited]
class BClass { }
class DClass : BClass { }
I det här fallet NonInheritedAttribute
tillämpas inte på DClass
via arv.
Du kan också använda dessa nyckelord för att ange var ett attribut ska tillämpas. Du kan till exempel använda field:
specificeraren för att lägga till ett attribut i bakgrundsfältet för en automatiskt implementerad egenskap. Eller så kan du använda field:
, property:
eller param:
-specificeraren för att tillämpa ett attribut på något av de element som genereras från en positionell post. Ett exempel finns i Positionssyntax för egenskapsdefinition.
AsyncMethodBuilder
-attribut
Du lägger till System.Runtime.CompilerServices.AsyncMethodBuilderAttribute attributet till en typ som kan vara en asynkron returtyp. Attributet anger vilken typ som skapar asynkron metodimplementering när den angivna typen returneras från en asynkron metod. Attributet AsyncMethodBuilder
kan tillämpas på en typ som:
- Har en tillgänglig
GetAwaiter
metod. - Objektet som returneras av
GetAwaiter
metoden implementerar System.Runtime.CompilerServices.ICriticalNotifyCompletion gränssnittet.
Konstruktorn till AsyncMethodBuilder
attributet anger typen av associerad byggare. Byggaren måste implementera följande tillgängliga medlemmar:
En statisk
Create()
metod som returnerar typen av byggare.En läsbar
Task
egenskap som returnerar returtypen async.En
void SetException(Exception)
metod som anger undantaget när en aktivitet fel.En
void SetResult()
ellervoid SetResult(T result)
-metod som markerar uppgiften som slutförd och som eventuellt anger aktivitetens resultatEn
Start
metod med följande API-signatur:void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
En
AwaitOnCompleted
metod med följande signatur:public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
En
AwaitUnsafeOnCompleted
metod med följande signatur:public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
Du kan lära dig mer om asynkrona metodbyggare genom att läsa om följande byggare som tillhandahålls av .NET:
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<TResult>
Attributet AsyncMethodBuilder
kan tillämpas på en asynkron metod för att åsidosätta byggverktyget för den typen.
InterpolatedStringHandler
och InterpolatedStringHandlerArguments
attribut
Du använder dessa attribut för att ange att en typ är en interpolerad stränghanterare. .NET 6-biblioteket innehåller System.Runtime.CompilerServices.DefaultInterpolatedStringHandler redan för scenarier där du använder en interpolerad sträng som argument för en string
parameter. Du kan ha andra instanser där du vill styra hur interpolerade strängar bearbetas. Du tillämpar på System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute den typ som implementerar din hanterare. Du använder parametrarna för System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute den typens konstruktor.
Du kan lära dig mer om att skapa en interpolerad stränghanterare i funktionsspecifikationen för interpolerade strängförbättringar.
ModuleInitializer
-attribut
Attributet ModuleInitializer
markerar en metod som körningen anropar när sammansättningen läses in.
ModuleInitializer
är ett alias för ModuleInitializerAttribute.
Attributet ModuleInitializer
kan bara tillämpas på en metod som:
- Är statisk.
- Är parameterlös.
- Returnerar
void
. - Är tillgänglig från den innehållande modulen, d.v.s.
internal
ellerpublic
. - Är inte en allmän metod.
- Finns inte i en allmän klass.
- Är inte en lokal funktion.
Attributet ModuleInitializer
kan tillämpas på flera metoder. I så fall är den ordning i vilken körningen anropar dem deterministisk men inte angiven.
I följande exempel visas användningen av flera modulinitieringsmetoder. Metoderna Init1
och Init2
körs före Main
, och var och en lägger till en sträng i Text
egenskapen. Så när Main
körs har egenskapen Text
redan strängar från båda initieringsmetoderna.
using System;
internal class ModuleInitializerExampleMain
{
public static void Main()
{
Console.WriteLine(ModuleInitializerExampleModule.Text);
//output: Hello from Init1! Hello from Init2!
}
}
using System.Runtime.CompilerServices;
internal class ModuleInitializerExampleModule
{
public static string? Text { get; set; }
[ModuleInitializer]
public static void Init1()
{
Text += "Hello from Init1! ";
}
[ModuleInitializer]
public static void Init2()
{
Text += "Hello from Init2! ";
}
}
Källkodsgeneratorer behöver ibland generera initieringskod. Modulinitierare tillhandahåller en standardplats för koden. I de flesta andra fall bör du skriva en statisk konstruktor i stället för en modulinitierare.
SkipLocalsInit
-attribut
Attributet SkipLocalsInit
hindrar kompilatorn från att ange flaggan när den .locals init
sänder ut till metadata. Attributet SkipLocalsInit
är ett engångsattribut och kan tillämpas på en metod, en egenskap, en klass, en struct, ett gränssnitt eller en modul, men inte för en sammansättning.
SkipLocalsInit
är ett alias för SkipLocalsInitAttribute.
Flaggan .locals init
gör att CLR initierar alla lokala variabler som deklareras i en metod till deras standardvärden. Eftersom kompilatorn också ser till att du aldrig använder en variabel innan du tilldelar något värde till den, .locals init
är det vanligtvis inte nödvändigt. Den extra nollinitieringen kan dock ha mätbar prestandapåverkan i vissa scenarier, till exempel när du använder stackalloc för att allokera en matris på stacken. I sådana fall kan du lägga till attributet SkipLocalsInit
. Om det tillämpas direkt på en metod påverkar attributet den metoden och alla dess kapslade funktioner, inklusive lambdas och lokala funktioner. Om den tillämpas på en typ eller modul påverkar den alla metoder som är kapslade inuti. Det här attributet påverkar inte abstrakta metoder, men det påverkar kod som genereras för implementeringen.
Det här attributet kräver kompilatoralternativet AllowUnsafeBlocks . Det här kravet signalerar att kod i vissa fall kan visa otilldelat minne (till exempel läsning från onitialiserat stackallokerat minne).
I följande exempel visas effekten av SkipLocalsInit
attributet på en metod som använder stackalloc
. Metoden visar vad som fanns i minnet när matrisen med heltal allokerades.
[SkipLocalsInit]
static void ReadUninitializedMemory()
{
Span<int> numbers = stackalloc int[120];
for (int i = 0; i < 120; i++)
{
Console.WriteLine(numbers[i]);
}
}
// output depends on initial contents of memory, for example:
//0
//0
//0
//168
//0
//-1271631451
//32767
//38
//0
//0
//0
//38
// Remaining rows omitted for brevity.
Om du vill prova den här koden själv anger du kompileringsalternativet AllowUnsafeBlocks
i .csproj-filen :
<PropertyGroup>
...
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
UnscopedRef
-attribut
Attributet UnscopedRef
markerar en variabeldeklaration som oscoped, vilket innebär att referensen tillåts fly.
Du lägger till det här attributet ref
där kompilatorn behandlar en scoped
som implicit :
- Parametern
this
förstruct
instansmetoder. -
ref
parametrar som refererar tillref struct
typer. -
out
Parametrar.
Om elementet System.Diagnostics.CodeAnalysis.UnscopedRefAttribute tillämpas som oscoped.
OverloadResolutionPriority
-attribut
Gör OverloadResolutionPriorityAttribute det möjligt för biblioteksförfattare att föredra en överlagring framför en annan när två överlagringar kan vara tvetydiga. Det primära användningsfallet är att biblioteksförfattare skriver bättre presterande överbelastningar samtidigt som de stöder befintlig kod utan pauser.
Du kan till exempel lägga till en ny överbelastning som används ReadOnlySpan<T> för att minska minnesallokeringar:
[OverloadResolutionPriority(1)]
public void M(params ReadOnlySpan<int> s) => Console.WriteLine("Span");
// Default overload resolution priority of 0
public void M(params int[] a) => Console.WriteLine("Array");
Överbelastningsmatchning anser att de två metoderna är lika bra för vissa argumenttyper. För ett argument av int[]
föredrar det den första överlagringen. Om du vill att kompilatorn ska föredra ReadOnlySpan
versionen kan du öka prioriteten för den överbelastningen. I följande exempel visas effekten av att lägga till attributet:
var d = new OverloadExample();
int[] arr = [1, 2, 3];
d.M(1, 2, 3, 4); // Prints "Span"
d.M(arr); // Prints "Span" when PriorityAttribute is applied
d.M([1, 2, 3, 4]); // Prints "Span"
d.M(1, 2, 3, 4); // Prints "Span"
Alla överlagringar med lägre prioritet än den högsta överlagringsprioriteten tas bort från uppsättningen med tillämpliga metoder. Metoder utan det här attributet har överlagringsprioriteten inställd på standardvärdet noll. Biblioteksförfattare bör använda det här attributet som en sista utväg när du lägger till en ny och bättre metodöverlagring. Biblioteksförfattare bör ha en djup förståelse för hur överbelastningsmatchning påverkar valet av den bättre metoden. Annars kan oväntade fel uppstå.