Sdílet prostřednictvím


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á jako scoped 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 AB 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 Experimentalnajdete 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 , falseodvozené 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ý:

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() nebo void 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:

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 internalpublic.
  • 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 Init1Init2 metody se spustí před Maina 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 .csprojAllowUnsafeBlockskompilá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 pro struct metody instance.
  • ref parametry, které odkazují na ref 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.

Viz také