Diverse kenmerken die worden geïnterpreteerd door de C#-compiler
Er zijn verschillende kenmerken die kunnen worden toegepast op elementen in uw code die semantische betekenis toevoegen aan deze elementen:
Conditional
: Maak de uitvoering van een methode afhankelijk van een preprocessor-id.Obsolete
: Een type of lid markeren voor (mogelijk) toekomstige verwijdering.AttributeUsage
: Declareer de taalelementen waarop een kenmerk kan worden toegepast.AsyncMethodBuilder
: Declareer een type opbouwfunctie voor asynchrone methoden.InterpolatedStringHandler
: Definieer een geïnterpoleerde opbouwfunctie voor tekenreeksen voor een bekend scenario.ModuleInitializer
: Declareer een methode waarmee een module wordt geïnitialiseerd.SkipLocalsInit
: Elide de code waarmee lokale variabele opslag wordt geïnitialiseerd naar 0.UnscopedRef
: declareer dat eenref
variabele die normaal gesproken moet worden geïnterpreteerdscoped
als onbereikbaar worden behandeld.OverloadResolutionPriority
: Voeg een tiebreakerkenmerk toe om de overbelastingsresolutie te beïnvloeden voor mogelijk dubbelzinnige overbelastingen.Experimental
: Markeer een type of lid als experimenteel.
De compiler gebruikt deze semantische betekenissen om de uitvoer te wijzigen en mogelijke fouten te rapporteren door ontwikkelaars die uw code gebruiken.
Conditional
-kenmerk
Het Conditional
kenmerk maakt de uitvoering van een methode afhankelijk van een voorverwerkings-id. Het Conditional
kenmerk is een alias voor ConditionalAttributeen kan worden toegepast op een methode of een kenmerkklasse.
In het volgende voorbeeld Conditional
wordt toegepast op een methode om de weergave van programmaspecifieke diagnostische gegevens in of uit te schakelen:
#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.");
}
}
Als de TRACE_ON
id niet is gedefinieerd, wordt de traceringsuitvoer niet weergegeven. Verken jezelf in het interactieve venster.
Het Conditional
kenmerk wordt vaak gebruikt met de DEBUG
id om tracerings- en logboekregistratiefuncties in te schakelen voor foutopsporingsversies, maar niet in release-builds, zoals wordt weergegeven in het volgende voorbeeld:
[Conditional("DEBUG")]
static void DebugMethod()
{
}
Wanneer een methode die is gemarkeerd als voorwaarde wordt aangeroepen, bepaalt de aanwezigheid of afwezigheid van het opgegeven voorverwerkingssymbool of de compiler aanroepen naar de methode bevat of weglaat. Als het symbool is gedefinieerd, wordt de aanroep opgenomen; anders wordt de aanroep weggelaten. Een voorwaardelijke methode moet een methode zijn in een klasse- of structdeclaratie en moet een void
retourtype hebben. Het gebruik Conditional
is schoner, eleganter en minder foutgevoelig dan het insluiten van methoden in #if…#endif
blokken.
Als een methode meerdere Conditional
kenmerken heeft, bevat compiler aanroepen naar de methode als een of meer voorwaardelijke symbolen zijn gedefinieerd (de symbolen zijn logisch aan elkaar gekoppeld met behulp van de OPERATOR OR). In het volgende voorbeeld wordt de aanwezigheid van A
een van beide of B
resultaten in een methode-aanroep weergegeven:
[Conditional("A"), Conditional("B")]
static void DoIfAorB()
{
// ...
}
Gebruiken Conditional
met kenmerkklassen
Het Conditional
kenmerk kan ook worden toegepast op een kenmerkklassedefinitie. In het volgende voorbeeld voegt het aangepaste kenmerk Documentation
informatie toe aan de metagegevens als DEBUG
deze is gedefinieerd.
[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
-kenmerk
Het Obsolete
kenmerk markeert een code-element, omdat dit niet meer wordt aanbevolen voor gebruik. Het gebruik van een entiteit die is gemarkeerd als verouderd, genereert een waarschuwing of een fout. Het Obsolete
kenmerk is een kenmerk voor eenmalig gebruik en kan worden toegepast op elke entiteit die kenmerken toestaat. Obsolete
is een alias voor ObsoleteAttribute.
In het volgende voorbeeld wordt het Obsolete
kenmerk toegepast op klasse A
en methode B.OldMethod
. Omdat het tweede argument van de kenmerkconstructor waarop is toegepast B.OldMethod
is ingesteld true
, veroorzaakt deze methode een compilerfout, terwijl het gebruik van klasse A
een waarschuwing produceert. Aanroepen B.NewMethod
produceert echter geen waarschuwing of fout. Wanneer u deze bijvoorbeeld gebruikt met de vorige definities, genereert de volgende code twee waarschuwingen en één fout:
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();
}
}
}
De tekenreeks die wordt opgegeven als het eerste argument voor de kenmerkconstructor, wordt weergegeven als onderdeel van de waarschuwing of fout. Er worden twee waarschuwingen voor klasse A
gegenereerd: één voor de declaratie van de klassereferentie en één voor de klasseconstructor. Het Obsolete
kenmerk kan zonder argumenten worden gebruikt, maar ook een uitleg over wat u in plaats daarvan moet gebruiken, wordt aanbevolen.
In C# 10 kunt u constante tekenreeksinterpolatie en de nameof
operator gebruiken om ervoor te zorgen dat de namen overeenkomen:
public class B
{
[Obsolete($"use {nameof(NewMethod)} instead", true)]
public void OldMethod() { }
public void NewMethod() { }
}
Experimental
-kenmerk
Vanaf C# 12 kunnen typen, methoden en assembly's worden gemarkeerd met de System.Diagnostics.CodeAnalysis.ExperimentalAttribute functie om een experimentele functie aan te geven. De compiler geeft een waarschuwing uit als u toegang krijgt tot een methode of een type met aantekeningen bij de ExperimentalAttribute. Alle typen die zijn gedeclareerd in een assembly of module die zijn gemarkeerd met het Experimental
kenmerk, zijn experimenteel. De compiler geeft een waarschuwing uit als u er toegang toe hebt. U kunt deze waarschuwingen uitschakelen om een experimentele functie uit te voeren.
Waarschuwing
Experimentele functies zijn onderhevig aan wijzigingen. De API's kunnen worden gewijzigd of worden verwijderd in toekomstige updates. Het opnemen van experimentele functies is een manier voor bibliotheekauteurs om feedback te krijgen over ideeën en concepten voor toekomstige ontwikkeling. Wees uiterst voorzichtig wanneer u een functie gebruikt die is gemarkeerd als experimenteel.
Meer informatie over het Experimental
kenmerk vindt u in de functiespecificatie.
SetsRequiredMembers
-kenmerk
Het SetsRequiredMembers
kenmerk informeert de compiler dat een constructor alle required
leden in die klasse of struct instelt. De compiler gaat ervan uit dat een constructor met het System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute kenmerk alle required
leden initialiseert. Elke code die een dergelijke constructor aanroept, heeft geen object-initialisatiefuncties nodig om vereiste leden in te stellen. Het toevoegen van het SetsRequiredMembers
kenmerk is voornamelijk handig voor positionele records en primaire constructors.
AttributeUsage
-kenmerk
Het AttributeUsage
kenmerk bepaalt hoe een aangepaste kenmerkklasse kan worden gebruikt. AttributeUsageAttribute is een kenmerk dat u toepast op aangepaste kenmerkdefinities. Met AttributeUsage
het kenmerk kunt u het volgende beheren:
- Op welke programma-elementen het kenmerk kan worden toegepast. Tenzij u het gebruik ervan beperkt, kan een kenmerk worden toegepast op een van de volgende programma-elementen:
- Assembly
- Module
- Veld
- Gebeurtenis
- Wijze
- Parameter
- Eigenschappen
- Retourneren
- Type
- Of een kenmerk meerdere keren kan worden toegepast op één programma-element.
- Of afgeleide klassen kenmerken overnemen.
De standaardinstellingen zien er als volgt uit wanneer deze expliciet worden toegepast:
[AttributeUsage(AttributeTargets.All,
AllowMultiple = false,
Inherited = true)]
class NewAttribute : Attribute { }
In dit voorbeeld kan de NewAttribute
klasse worden toegepast op elk ondersteund programma-element. Maar het kan slechts één keer worden toegepast op elke entiteit. Afgeleide klassen nemen het kenmerk over dat is toegepast op een basisklasse.
De AllowMultiple en Inherited argumenten zijn optioneel, dus de volgende code heeft hetzelfde effect:
[AttributeUsage(AttributeTargets.All)]
class NewAttribute : Attribute { }
Het eerste AttributeUsageAttribute argument moet een of meer elementen van de AttributeTargets opsomming zijn. Meerdere doeltypen kunnen worden gekoppeld aan de OPERATOR OF, zoals in het volgende voorbeeld:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
class NewPropertyOrFieldAttribute : Attribute { }
Kenmerken kunnen worden toegepast op de eigenschap of het backing-veld voor een automatisch geïmplementeerde eigenschap. Het kenmerk is van toepassing op de eigenschap, tenzij u de field
aanduiding voor het kenmerk opgeeft. Beide worden weergegeven in het volgende voorbeeld:
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;
}
Als het AllowMultiple argument is true
, kan het resulterende kenmerk meerdere keren worden toegepast op één entiteit, zoals wordt weergegeven in het volgende voorbeeld:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
class MultiUse : Attribute { }
[MultiUse]
[MultiUse]
class Class1 { }
[MultiUse, MultiUse]
class Class2 { }
In dit geval MultiUseAttribute
kan herhaaldelijk worden toegepast omdat AllowMultiple
deze is ingesteld op true
. Beide indelingen die worden weergegeven voor het toepassen van meerdere kenmerken, zijn geldig.
Als Inherited dat het is false
, nemen afgeleide klassen het kenmerk niet over van een toegeschreven basisklasse. Voorbeeld:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
class NonInheritedAttribute : Attribute { }
[NonInherited]
class BClass { }
class DClass : BClass { }
In dit geval NonInheritedAttribute
wordt dit niet toegepast DClass
via overname.
U kunt deze trefwoorden ook gebruiken om op te geven waar een kenmerk moet worden toegepast. U kunt bijvoorbeeld de field:
aanduiding gebruiken om een kenmerk toe te voegen aan het backingveld van een automatisch geïmplementeerde eigenschap. U kunt ook de field:
of property:
param:
aanduiding gebruiken om een kenmerk toe te passen op een van de elementen die zijn gegenereerd op basis van een positionele record. Zie Positionele syntaxis voor eigenschapsdefinitie voor een voorbeeld.
AsyncMethodBuilder
-kenmerk
U voegt het System.Runtime.CompilerServices.AsyncMethodBuilderAttribute kenmerk toe aan een type dat een asynchroon retourtype kan zijn. Het kenmerk geeft het type op dat de implementatie van de asynchrone methode bouwt wanneer het opgegeven type wordt geretourneerd vanuit een asynchrone methode. Het AsyncMethodBuilder
kenmerk kan worden toegepast op een type dat:
- Heeft een toegankelijke
GetAwaiter
methode. - Het object dat door de
GetAwaiter
methode wordt geretourneerd, implementeert de System.Runtime.CompilerServices.ICriticalNotifyCompletion interface.
De constructor voor het AsyncMethodBuilder
kenmerk geeft het type van de bijbehorende opbouwfunctie aan. De opbouwfunctie moet de volgende toegankelijke leden implementeren:
Een statische
Create()
methode die het type opbouwfunctie retourneert.Een leesbare
Task
eigenschap die het asynchrone retourtype retourneert.Een
void SetException(Exception)
methode waarmee de uitzondering wordt ingesteld wanneer een taak fouten veroorzaakt.Een
void SetResult()
ofvoid SetResult(T result)
methode waarmee de taak wordt gemarkeerd als voltooid en optioneel het resultaat van de taak wordt ingesteldEen
Start
methode met de volgende API-handtekening:void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
Een
AwaitOnCompleted
methode met de volgende handtekening:public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
Een
AwaitUnsafeOnCompleted
methode met de volgende handtekening:public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
U vindt meer informatie over asynchrone opbouwfuncties voor methoden door te lezen over de volgende opbouwfuncties die door .NET worden geleverd:
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<TResult>
In C# 10 en hoger kan het AsyncMethodBuilder
kenmerk worden toegepast op een asynchrone methode om de opbouwfunctie voor dat type te overschrijven.
InterpolatedStringHandler
en InterpolatedStringHandlerArguments
kenmerken
Vanaf C# 10 gebruikt u deze kenmerken om op te geven dat een type een geïnterpoleerde tekenreekshandler is. De .NET 6-bibliotheek bevat System.Runtime.CompilerServices.DefaultInterpolatedStringHandler al voor scenario's waarin u een geïnterpoleerde tekenreeks gebruikt als het argument voor een string
parameter. Mogelijk hebt u andere exemplaren waarin u wilt bepalen hoe geïnterpoleerde tekenreeksen worden verwerkt. U past het System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute toe op het type dat uw handler implementeert. U past de System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute parameters van de constructor van dat type toe.
Meer informatie over het bouwen van een geïnterpoleerde tekenreekshandler vindt u in de C# 10-functiespecificatie voor geïnterpoleerde tekenreeksverbeteringen.
ModuleInitializer
-kenmerk
Het ModuleInitializer
kenmerk markeert een methode die door de runtime wordt aangeroepen wanneer de assembly wordt geladen. ModuleInitializer
is een alias voor ModuleInitializerAttribute.
Het ModuleInitializer
kenmerk kan alleen worden toegepast op een methode die:
- Is statisch.
- Is parameterloos.
- Retourneert
void
. - Is toegankelijk vanuit de betreffende module, dat wil
internal
wel.public
- Is geen algemene methode.
- Is niet opgenomen in een algemene klasse.
- Is geen lokale functie.
Het ModuleInitializer
kenmerk kan worden toegepast op meerdere methoden. In dat geval is de volgorde waarin de runtime deze aanroept deterministisch, maar niet opgegeven.
In het volgende voorbeeld ziet u hoe u meerdere initialisatiemethoden voor modules gebruikt. De Init1
en Init2
methoden worden eerder Main
uitgevoerd en elke methode voegt een tekenreeks toe aan de Text
eigenschap. Dus wanneer Main
de eigenschap wordt uitgevoerd, heeft de Text
eigenschap al tekenreeksen van beide initialisatiemethoden.
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! ";
}
}
Broncodegeneratoren moeten soms initialisatiecode genereren. Module-initializers bieden een standaardplaats voor die code. In de meeste andere gevallen moet u een statische constructor schrijven in plaats van een module-initialisatiefunctie.
SkipLocalsInit
-kenmerk
Het SkipLocalsInit
kenmerk voorkomt dat de compiler de .locals init
vlag instelt bij het verzenden naar metagegevens. Het SkipLocalsInit
kenmerk is een kenmerk voor eenmalig gebruik en kan worden toegepast op een methode, een eigenschap, een klasse, een struct, een interface of een module, maar niet op een assembly. SkipLocalsInit
is een alias voor SkipLocalsInitAttribute.
De .locals init
vlag zorgt ervoor dat de CLR alle lokale variabelen initialiseert die zijn gedeclareerd in een methode naar de standaardwaarden. Omdat de compiler er ook voor zorgt dat u nooit een variabele gebruikt voordat u er een waarde aan toewijst, .locals init
is dit doorgaans niet nodig. De extra nul-initialisatie kan echter meetbare invloed hebben op de prestaties in sommige scenario's, zoals wanneer u stackalloc gebruikt om een matrix toe te wijzen aan de stack. In die gevallen kunt u het SkipLocalsInit
kenmerk toevoegen. Als het kenmerk rechtstreeks op een methode wordt toegepast, is dit van invloed op die methode en alle geneste functies, waaronder lambdas en lokale functies. Als dit wordt toegepast op een type of module, is dit van invloed op alle methoden die binnen zijn genest. Dit kenmerk heeft geen invloed op abstracte methoden, maar heeft wel invloed op de code die is gegenereerd voor de implementatie.
Voor dit kenmerk is de optie AllowUnsafeBlocks-compiler vereist. Deze vereiste geeft aan dat code in sommige gevallen niet-toegewezen geheugen kan weergeven (bijvoorbeeld lezen uit niet-geïnitialiseerd geheugen dat is toegewezen aan stack).
In het volgende voorbeeld ziet u het effect van SkipLocalsInit
het kenmerk op een methode die gebruikmaakt van stackalloc
. De methode geeft wat zich in het geheugen bevond toen de matrix met gehele getallen werd toegewezen.
[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.
Als u deze code zelf wilt proberen, stelt u de AllowUnsafeBlocks
compileroptie in uw .csproj-bestand in:
<PropertyGroup>
...
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
UnscopedRef
-kenmerk
Het UnscopedRef
kenmerk markeert een variabeledeclaratie als onbereikbaar, wat betekent dat de verwijzing mag ontsnappen.
U voegt dit kenmerk toe waarbij de compiler impliciet een ref
als volgt scoped
behandelt:
- De
this
parameter voorstruct
exemplaarmethoden. ref
parameters die verwijzen naarref struct
typen.out
Parameters.
Als u de System.Diagnostics.CodeAnalysis.UnscopedRefAttribute markeringen toepast, wordt het element niet gescoopt.
OverloadResolutionPriority
-kenmerk
De OverloadResolutionPriorityAttribute mogelijkheid biedt bibliotheekauteurs de voorkeur aan een overbelasting boven een andere wanneer twee overbelastingen dubbelzinnig kunnen zijn. De primaire use-case is dat bibliotheekauteurs beter presterende overbelastingen kunnen schrijven terwijl bestaande code nog steeds zonder onderbrekingen wordt ondersteund.
U kunt bijvoorbeeld een nieuwe overbelasting toevoegen die wordt gebruikt ReadOnlySpan<T> om geheugentoewijzingen te verminderen:
[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");
Overbelastingsresolutie beschouwt de twee methoden even goed voor sommige argumenttypen. Voor een argument van int[]
, geeft het de voorkeur aan de eerste overbelasting. Als u wilt dat de compiler de voorkeur geeft aan de ReadOnlySpan
versie, kunt u de prioriteit van die overbelasting verhogen. In het volgende voorbeeld ziet u het effect van het toevoegen van het kenmerk:
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"
Alle overbelastingen met een lagere prioriteit dan de hoogste overbelastingsprioriteit worden verwijderd uit de set toepasselijke methoden. Methoden zonder dit kenmerk hebben de prioriteit voor overbelasting ingesteld op de standaardwaarde van nul. Auteurs van bibliotheken moeten dit kenmerk als laatste redmiddel gebruiken bij het toevoegen van een nieuwe en betere overbelasting van methoden. Auteurs van bibliotheken moeten goed begrijpen hoe overbelastingsresolutie van invloed is op het kiezen van de betere methode. Anders kunnen onverwachte fouten resulteren.