Různé atributy interpretované kompilátorem jazyka C#
Existuje několik atributů, které lze použít u prvků v kódu, které přidávají sémantický význam těchto prvků:
-
Conditional
: Provedení metody závislé na identifikátoru preprocesoru. -
Obsolete
: Označte typ nebo člen pro (potenciální) budoucí odebrání. -
AttributeUsage
: Deklarujte prvky jazyka, kde lze použít atribut. -
AsyncMethodBuilder
: Deklarujte typ asynchronního tvůrce metod. -
InterpolatedStringHandler
: Definujte interpolovaný tvůrce řetězců pro známý scénář. -
ModuleInitializer
: Deklarujte metodu, která inicializuje modul. -
SkipLocalsInit
: Elide kód, který inicializuje místní proměnné úložiště na 0. -
UnscopedRef
: Deklarujte, že proměnnáref
obvykle interpretovaná jakoscoped
by měla být považována za neskopovanou. -
OverloadResolutionPriority
: Přidejte atribut tiebreaker, který má vliv na rozlišení přetížení pro pravděpodobně nejednoznačné přetížení. -
Experimental
: Označte typ nebo člen jako experimentální.
Kompilátor používá tyto sémantické významy ke změně jeho výstupu a hlášení možných chyb vývojáři pomocí vašeho kódu.
Atribut Conditional
Atribut Conditional
vytvoří spuštění metody závislé na identifikátoru předběžného zpracování. Atribut Conditional
je alias pro ConditionalAttributea lze jej použít pro metodu nebo třídu atributu.
V následujícím příkladu se použije u metody, Conditional
která povolí nebo zakáže zobrazení diagnostických informací specifických pro program:
#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.");
}
}
TRACE_ON
Pokud identifikátor není definovaný, výstup trasování se nezobrazí. Prozkoumejte sami sebe v interaktivním okně.
Atribut Conditional
se často používá s identifikátorem DEBUG
k povolení funkcí trasování a protokolování pro sestavení ladění, ale ne v buildech vydaných verzí, jak je znázorněno v následujícím příkladu:
[Conditional("DEBUG")]
static void DebugMethod()
{
}
Pokud je volána metoda označená podmíněným voláním, přítomnost nebo absence zadaného symbolu předběžného zpracování určuje, zda kompilátor obsahuje nebo vynechá volání metody. Pokud je definován symbol, volání je zahrnuto; jinak je volání vynecháno. Podmíněná metoda musí být metoda v deklaraci třídy nebo struktury a musí mít návratový void
typ. Použití Conditional
je čistější, elegantnější a méně náchylné k chybám než ohraničující metody uvnitř #if…#endif
bloků.
Pokud má metoda více Conditional
atributů, kompilátor zahrnuje volání metody, pokud jsou definovány jeden nebo více podmíněných symbolů (symboly jsou logicky propojeny pomocí operátoru OR). V následujícím příkladu je přítomnost volání metody nebo A
B
výsledkem volání metody:
[Conditional("A"), Conditional("B")]
static void DoIfAorB()
{
// ...
}
Použití Conditional
s třídami atributů
Atribut Conditional
lze také použít pro definici třídy atributu. V následujícím příkladu vlastní atribut Documentation
přidá informace do metadat, pokud DEBUG
je definován.
[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());
}
}
Atribut Obsolete
Atribut Obsolete
označí prvek kódu, který se už nedoporučuje používat. Použití entity označené jako zastaralé generuje upozornění nebo chybu. Atribut Obsolete
je atribut s jedním použitím a lze jej použít u jakékoli entity, která umožňuje atributy.
Obsolete
je alias pro ObsoleteAttribute.
V následujícím příkladu Obsolete
se atribut použije na třídu A
a metodu B.OldMethod
. Vzhledem k tomu, že druhý argument konstruktoru atributu použitý na B.OldMethod
je nastaven na true
, tato metoda způsobí chybu kompilátoru, zatímco použití třídy A
vytvoří upozornění. Volání však nevyvolá B.NewMethod
žádné upozornění nebo chybu. Když ho například použijete s předchozími definicemi, následující kód vygeneruje dvě upozornění a jednu chybu:
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();
}
}
}
Řetězec zadaný jako první argument konstruktoru atributu se zobrazí jako součást upozornění nebo chyby. Vygenerují se dvě upozornění pro třídu: jedno pro deklaraci odkazu na třídu A
a jedno pro konstruktor třídy. Atribut Obsolete
lze použít bez argumentů, ale včetně vysvětlení, co se má místo toho použít. K zajištění shody názvů můžete použít interpolaci konstantních řetězců a operátor nameof
:
public class B
{
[Obsolete($"use {nameof(NewMethod)} instead", true)]
public void OldMethod() { }
public void NewMethod() { }
}
Atribut Experimental
Počínaje jazykem C# 12 je možné označit typy, metody a sestavení, System.Diagnostics.CodeAnalysis.ExperimentalAttribute které označují experimentální funkci. Kompilátor vydá upozornění, pokud přistupujete k metodě nebo zadáte poznámky s příponou ExperimentalAttribute. Všechny typy deklarované v sestavení nebo modulu označeném atributem Experimental
jsou experimentální. Kompilátor vydá upozornění, pokud k některému z nich přistupujete. Tato upozornění můžete zakázat pro pilotní nasazení experimentální funkce.
Upozorňující
Experimentální funkce se můžou měnit. Rozhraní API se můžou změnit nebo se můžou v budoucích aktualizacích odebrat. Zahrnutí experimentálních funkcí je způsob, jak autorům knihoven získat zpětnou vazbu k nápadům a konceptům pro budoucí vývoj. Při použití jakékoli funkce označené jako experimentální používejte extrémní opatrnost.
Další podrobnosti o atributu Experimental
najdete ve specifikaci funkce.
Atribut SetsRequiredMembers
Atribut SetsRequiredMembers
informuje kompilátor, že konstruktor nastaví všechny required
členy v této třídě nebo struktuře. Kompilátor předpokládá jakýkoli konstruktor s atributem System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute inicializuje všechny required
členy. Jakýkoli kód, který takový konstruktor vyvolá, nepotřebuje inicializátory objektů k nastavení požadovaných členů. Přidání atributu SetsRequiredMembers
je primárně užitečné pro poziční záznamy a primární konstruktory.
Atribut AttributeUsage
Atribut AttributeUsage
určuje způsob použití vlastní třídy atributů.
AttributeUsageAttribute je atribut, který použijete u definic vlastních atributů. Atribut AttributeUsage
umožňuje řídit:
- Na které prvky programu lze atribut použít. Pokud jeho použití neomezíte, lze atribut použít na některý z následujících prvků programu:
- Sestavení
- Modul
- Pole
- Událost
- metoda
- Parametr
- Vlastnost
- Zpět
- Typ
- Určuje, zda lze atribut použít na jeden prvek programu vícekrát.
- Zda odvozené třídy dědí atributy.
Výchozí nastavení při použití explicitně vypadají jako v následujícím příkladu:
[AttributeUsage(AttributeTargets.All,
AllowMultiple = false,
Inherited = true)]
class NewAttribute : Attribute { }
V tomto příkladu NewAttribute
lze třídu použít na libovolný podporovaný prvek programu. Dá se ale použít pouze jednou pro každou entitu. Odvozené třídy dědí atribut použitý na základní třídu.
Argumenty AllowMultiple a Inherited argumenty jsou volitelné, takže následující kód má stejný účinek:
[AttributeUsage(AttributeTargets.All)]
class NewAttribute : Attribute { }
První AttributeUsageAttribute argument musí být jeden nebo více prvků výčtu AttributeTargets . S operátorem OR je možné propojit více cílových typů, například v následujícím příkladu:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
class NewPropertyOrFieldAttribute : Attribute { }
Atributy lze použít buď na vlastnost, nebo backing pole pro automaticky implementovanou vlastnost. Atribut se vztahuje na vlastnost, pokud nezadáte field
specifikátor atributu. Obojí je znázorněno v následujícím příkladu:
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 Pokud argument je true
, výsledný atribut lze použít více než jednou na jednu entitu, jak je znázorněno v následujícím příkladu:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
class MultiUse : Attribute { }
[MultiUse]
[MultiUse]
class Class1 { }
[MultiUse, MultiUse]
class Class2 { }
V tomto případě lze opakovaně použít, MultiUseAttribute
protože AllowMultiple
je nastavena na true
. Oba formáty zobrazené pro použití více atributů jsou platné.
Pokud Inherited je , false
odvozené třídy nedědí atribut z atributu základní třídy. Příklad:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
class NonInheritedAttribute : Attribute { }
[NonInherited]
class BClass { }
class DClass : BClass { }
V tomto případě NonInheritedAttribute
se nepoužije na DClass
dědičnost.
Pomocí těchto klíčových slov můžete také určit, kde se má atribut použít. Pomocí specifikátoru field:
můžete například přidat atribut do backingového pole automaticky implementované vlastnosti. Nebo můžete použít field:
specifikátor nebo property:
param:
použít atribut na libovolný z prvků vygenerovaných z pozičního záznamu. Příklad najdete v tématu Poziční syntaxe pro definici vlastnosti.
Atribut AsyncMethodBuilder
Atribut přidáte System.Runtime.CompilerServices.AsyncMethodBuilderAttribute do typu, který může být asynchronním návratovým typem. Atribut určuje typ, který sestaví implementaci asynchronní metody při vrácení zadaného typu z asynchronní metody. Atribut AsyncMethodBuilder
lze použít u typu, který:
- Má přístupnou
GetAwaiter
metodu. - Objekt vrácený metodou
GetAwaiter
implementuje System.Runtime.CompilerServices.ICriticalNotifyCompletion rozhraní.
Konstruktor atributu AsyncMethodBuilder
určuje typ přidruženého tvůrce. Tvůrce musí implementovat následující přístupné členy:
Statická
Create()
metoda, která vrací typ tvůrce.Čitelná
Task
vlastnost, která vrací asynchronní návratový typ.Metoda
void SetException(Exception)
, která nastaví výjimku při selhání úlohy.A
void SetResult()
nebovoid SetResult(T result)
metoda, která označí úkol jako dokončený a volitelně nastaví výsledek úkolu.Metoda
Start
s následujícím podpisem rozhraní API:void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
Metoda
AwaitOnCompleted
s následujícím podpisem:public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
Metoda
AwaitUnsafeOnCompleted
s následujícím podpisem:public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
O tvůrcích asynchronních metod se dozvíte v článku o následujících tvůrcích poskytovaných rozhraním .NET:
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder
- System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<TResult>
Atribut AsyncMethodBuilder
lze použít u asynchronní metody k přepsání tvůrce pro tento typ.
InterpolatedStringHandler
a InterpolatedStringHandlerArguments
atributy
Pomocí těchto atributů určíte, že typ je interpolovaná obslužná rutina řetězce. Knihovna .NET 6 již obsahuje System.Runtime.CompilerServices.DefaultInterpolatedStringHandler scénáře, ve kterých jako argument parametru string
použijete interpolovaný řetězec. Můžete mít další instance, ve kterých chcete řídit způsob zpracování interpolovaných řetězců. Použijete typ System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute , který implementuje obslužnou rutinu. Použijete parametry System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute konstruktoru daného typu.
Můžete se dozvědět více o vytvoření obslužné rutiny pro interpolované řetězce ve specifikaci funkce pro vylepšení interpolovaných řetězců .
Atribut ModuleInitializer
Atribut ModuleInitializer
označuje metodu, kterou modul runtime volá při načtení sestavení.
ModuleInitializer
je alias pro ModuleInitializerAttribute.
Atribut ModuleInitializer
lze použít pouze u metody, která:
- Je statický.
- Je bez parametrů.
- Vrací objekt
void
. - Je přístupný z modulu obsahujícího, tj
internal
public
. - Nejedná se o obecnou metodu.
- Není obsažen v obecné třídě.
- Není místní funkce.
Atribut ModuleInitializer
lze použít pro více metod. V takovém případě je pořadí, ve kterém je modul runtime volá, deterministický, ale není určen.
Následující příklad ukazuje použití více metod inicializátoru modulu. Metody a Init1
Init2
metody se spustí před Main
a každý přidá řetězec do Text
vlastnosti.
Main
Při spuštění tedy Text
vlastnost již obsahuje řetězce z obou metod inicializátoru.
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! ";
}
}
Generátory zdrojového kódu někdy potřebují vygenerovat inicializační kód. Inicializátory modulů poskytují standardní místo pro tento kód. Ve většině ostatních případů byste měli místo inicializátoru modulu napsat statický konstruktor .
Atribut SkipLocalsInit
Atribut SkipLocalsInit
brání kompilátoru v nastavení příznaku .locals init
při generování do metadat. Atribut SkipLocalsInit
je atribut s jedním použitím a lze jej použít na metodu, vlastnost, třídu, strukturu, rozhraní nebo modul, ale ne na sestavení.
SkipLocalsInit
je alias pro SkipLocalsInitAttribute.
Příznak .locals init
způsobí, že CLR inicializuje všechny místní proměnné deklarované v metodě na jejich výchozí hodnoty. Vzhledem k tomu, že kompilátor také zajistí, že před přiřazením určité hodnoty nikdy nepoužijete proměnnou, .locals init
obvykle není nutné. V některých scénářích ale může mít extra inicializace nula měřitelný dopad na výkon, například když k přidělení pole v zásobníku použijete stackalloc . V takových případech můžete přidat SkipLocalsInit
atribut. Pokud se použije přímo na metodu, atribut ovlivní danou metodu a všechny jeho vnořené funkce, včetně lambda a místních funkcí. Pokud se použije u typu nebo modulu, ovlivní to všechny metody vnořené uvnitř. Tento atribut nemá vliv na abstraktní metody, ale má vliv na kód vygenerovaný pro implementaci.
Tento atribut vyžaduje možnost kompilátoru AllowUnsafeBlocks . Tento požadavek signalizuje, že kód v některých případech může zobrazit nepřiřazenou paměť (například čtení z neinicializované paměti přidělené zásobníkem).
Následující příklad znázorňuje účinek atributu SkipLocalsInit
na metodu, která používá stackalloc
. Metoda zobrazí, co bylo v paměti při přidělení pole celých čísel.
[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.
Pokud chcete tento kód vyzkoušet sami, nastavte v souboru .csprojAllowUnsafeBlocks
kompilátoru:
<PropertyGroup>
...
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Atribut UnscopedRef
Atribut UnscopedRef
označí deklaraci proměnné jako neskopovanou, což znamená, že odkaz může uvozovat.
Přidáte tento atribut, kde kompilátor považuje ref
za implicitní scoped
:
- Parametr
this
prostruct
metody instance. -
ref
parametry, které odkazují naref struct
typy. -
out
parametry.
System.Diagnostics.CodeAnalysis.UnscopedRefAttribute Použití značek prvku jako neskopovaného
Atribut OverloadResolutionPriority
Umožňuje OverloadResolutionPriorityAttribute autorům knihoven preferovat jedno přetížení před druhým, pokud mohou být dvě přetížení nejednoznačné. Hlavním případem použití je, že autoři knihoven píší přetížení s lepším výkonem a přitom podporují stávající kód bez přerušení.
Můžete například přidat nové přetížení, které používá ReadOnlySpan<T> ke snížení přidělení paměti:
[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");
Řešení přetížení považuje obě metody za stejně dobré pro některé typy argumentů. Pro argument int[]
dává přednost prvnímu přetížení. Pokud chcete kompilátoru dát přednost ReadOnlySpan
verzi, můžete zvýšit prioritu tohoto přetížení. Následující příklad ukazuje účinek přidání atributu:
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"
Všechna přetížení s nižší prioritou, než je nejvyšší priorita přetížení, se odeberou ze sady použitelných metod. Metody bez tohoto atributu mají prioritu přetížení nastavenou na výchozí hodnotu nula. Autoři knihoven by měli tento atribut použít jako poslední možnost při přidávání nového a lepšího přetížení metody. Autoři knihoven by měli mít hluboké znalosti o tom, jak má řešení přetížení vliv na volbu lepší metody. Jinak může dojít k neočekávaným chybám.