Definiera och läsa anpassade attribut

Attribut ger ett sätt att associera information med kod på ett deklarativt sätt. De kan också tillhandahålla ett återanvändbart element som kan tillämpas på olika mål. ObsoleteAttribute Överväg. Det kan tillämpas på klasser, structs, metoder, konstruktorer med mera. Den deklarerar att elementet är föråldrat. Det är sedan upp till C#-kompilatorn att söka efter det här attributet och utföra en åtgärd som svar.

I den här självstudien får du lära dig hur du lägger till attribut i koden, hur du skapar och använder dina egna attribut och hur du använder vissa attribut som är inbyggda i .NET.

Förutsättningar

Du måste konfigurera datorn för att köra .NET. Du hittar installationsanvisningarna på sidan .NET-nedladdningar . Du kan köra det här programmet på Windows, Ubuntu Linux, macOS eller i en Docker-container. Du måste installera din favoritkodredigerare. Följande beskrivningar använder Visual Studio Code, som är en plattformsoberoende redigerare med öppen källkod. Du kan dock använda de verktyg som du är bekväm med.

Skapa appen

Nu när du har installerat alla verktyg skapar du en ny .NET-konsolapp. Om du vill använda kommandoradsgeneratorn kör du följande kommando i ditt favoritgränssnitt:

dotnet new console

Det här kommandot skapar grundläggande .NET-projektfiler. Du kör dotnet restore för att återställa de beroenden som behövs för att kompilera projektet.

Du behöver inte köra dotnet restore eftersom den körs implicit av alla kommandon som kräver en återställning, till exempel dotnet new, dotnet build, dotnet run, dotnet test, dotnet publishoch dotnet pack. Om du vill inaktivera implicit återställning använder du alternativet --no-restore.

Kommandot dotnet restore är fortfarande användbart i vissa scenarier där det är meningsfullt att uttryckligen återställa, till exempel kontinuerliga integreringsversioner i Azure DevOps Services eller i byggsystem som uttryckligen behöver styra när återställningen sker.

Information om hur du hanterar NuGet-feeds finns i dotnet restore dokumentationen.

Om du vill köra programmet använder du dotnet run. Du bör se "Hello, World" visas på konsolen.

Lägga till attribut i kod

I C# är attribut klasser som ärver från basklassen Attribute . Alla klasser som ärver från Attribute kan användas som en typ av "tagg" på andra koddelar. Det finns till exempel ett attribut som heter ObsoleteAttribute. Det här attributet signalerar att koden är föråldrad och inte bör användas längre. Du placerar det här attributet på en klass, till exempel med hjälp av hakparenteser.

[Obsolete]
public class MyClass
{
}

Klassen kallas ObsoleteAttribute, men det är bara nödvändigt att använda [Obsolete] i koden. De flesta C#-kod följer den här konventionen. Du kan använda det fullständiga namnet [ObsoleteAttribute] om du vill.

När du markerar en klass föråldrad är det en bra idé att ange viss information om varför den är föråldrad och/eller vad som ska användas i stället. Du inkluderar en strängparameter till attributet Föråldrad för att ge den här förklaringen.

[Obsolete("ThisClass is obsolete. Use ThisClass2 instead.")]
public class ThisClass
{
}

Strängen skickas som ett argument till en ObsoleteAttribute konstruktor, som om du skrev var attr = new ObsoleteAttribute("some string").

Parametrar till en attributkonstruktor är begränsade till enkla typer/literaler: bool, int, double, string, Type, enums, etc och matriser av dessa typer. Du kan inte använda ett uttryck eller en variabel. Du kan använda positionella eller namngivna parametrar.

Skapa ett eget attribut

Du skapar ett attribut genom att definiera en ny klass som ärver från basklassen Attribute .

public class MySpecialAttribute : Attribute
{
}

Med föregående kod kan du använda [MySpecial] (eller [MySpecialAttribute]) som ett attribut någon annanstans i kodbasen.

[MySpecial]
public class SomeOtherClass
{
}

Attribut i .NET-basklassbiblioteket som ObsoleteAttribute utlöser vissa beteenden i kompilatorn. Alla attribut som du skapar fungerar dock bara som metadata och resulterar inte i någon kod i attributklassen som körs. Det är upp till dig att agera på dessa metadata någon annanstans i koden.

Det finns en fallgrop här att se upp för. Som tidigare nämnts kan endast vissa typer skickas som argument när du använder attribut. Men när du skapar en attributtyp hindrar C#-kompilatorn dig inte från att skapa dessa parametrar. I följande exempel har du skapat ett attribut med en konstruktor som kompileras korrekt.

public class GotchaAttribute : Attribute
{
    public GotchaAttribute(Foo myClass, string str)
    {
    }
}

Du kan dock inte använda den här konstruktorn med attributsyntax.

[Gotcha(new Foo(), "test")] // does not compile
public class AttributeFail
{
}

Föregående kod orsakar ett kompilatorfel som Attribute constructor parameter 'myClass' has type 'Foo', which is not a valid attribute parameter type

Så här begränsar du attributanvändning

Attributen kan användas på följande "mål". Föregående exempel visar dem på klasser, men de kan också användas på:

  • Sammansättning
  • Klass
  • Konstruktor
  • Delegera
  • Räkna upp
  • Evenemang
  • Fält
  • GeneriskParameter
  • Gränssnitt
  • Metod
  • Modul
  • Parameter
  • Fastighet
  • Returvärde
  • Struktur

När du skapar en attributklass tillåter C# som standard att du använder det attributet på något av de möjliga attributmålen. Om du vill begränsa attributet till vissa mål kan du göra det med hjälp av i attributklassen AttributeUsageAttribute . Det stämmer, ett attribut på ett attribut!

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute
{
}

Om du försöker placera attributet ovan på något som inte är en klass eller en struct får du ett kompilatorfel som Attribute 'MyAttributeForClassAndStructOnly' is not valid on this declaration type. It is only valid on 'class, struct' declarations

public class Foo
{
    // if the below attribute was uncommented, it would cause a compiler error
    // [MyAttributeForClassAndStructOnly]
    public Foo()
    { }
}

Så här använder du attribut som är kopplade till ett kodelement

Attribut fungerar som metadata. Utan någon yttre kraft, gör de faktiskt ingenting.

För att hitta och agera på attribut krävs reflektion. Med reflektion kan du skriva kod i C# som undersöker annan kod. Du kan till exempel använda Reflektion för att hämta information om en klass (lägg till using System.Reflection; i kodens huvud):

TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();
Console.WriteLine("The assembly qualified name of MyClass is " + typeInfo.AssemblyQualifiedName);

Det skriver ut ungefär så här: The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

När du har ett TypeInfo objekt (eller ett MemberInfo, FieldInfoeller ett annat objekt) kan du använda GetCustomAttributes -metoden. Den här metoden returnerar en samling Attribute objekt. Du kan också använda GetCustomAttribute och ange en attributtyp.

Här är ett exempel på hur du använder GetCustomAttributes på en MemberInfo instans ( MyClass som vi såg tidigare har ett [Obsolete] attribut på den).

var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs)
    Console.WriteLine("Attribute on MyClass: " + attr.GetType().Name);

Det skrivs ut på konsolen: Attribute on MyClass: ObsoleteAttribute. Prova att lägga till andra attribut i MyClass.

Det är viktigt att observera att dessa Attribute objekt instansieras fördröjt. De instansieras alltså inte förrän du använder GetCustomAttribute eller GetCustomAttributes. Varje gång instansieras de också. Om du anropar GetCustomAttributes två gånger på en rad returneras två olika instanser av ObsoleteAttribute.

Vanliga attribut i körmiljön

Attribut används av många verktyg och ramverk. NUnit använder attribut som [Test] och [TestFixture] som används av NUnit-testlöparen. ASP.NET MVC använder attribut som [Authorize] och tillhandahåller ett åtgärdsfilterramverk för att utföra tvärgående aspekter på MVC-funktioner. PostSharp använder attributsyntaxen för att tillåta aspektorienterad programmering i C#.

Här följer några viktiga attribut som är inbyggda i .NET Core-basklassbiblioteken:

  • [Obsolete]. Den här användes i exemplen ovan och den finns i System namnområdet. Det är användbart att tillhandahålla deklarativ dokumentation om en ändrad kodbas. Ett meddelande kan anges i form av en sträng och en annan boolesk parameter kan användas för att eskalera från en kompilatorvarning till ett kompilatorfel.
  • [Conditional]. Det här attributet finns i System.Diagnostics namnområdet. Det här attributet kan tillämpas på metoder (eller attributklasser). Du måste skicka en sträng till konstruktorn. Om strängen inte matchar ett #define direktiv tar C#-kompilatorn bort alla anrop till den metoden (men inte själva metoden). Vanligtvis använder du den här tekniken för felsökning (diagnostik).
  • [CallerMemberName]. Det här attributet kan användas för parametrar och finns i System.Runtime.CompilerServices namnområdet. CallerMemberName är ett attribut som används för att mata in namnet på metoden som anropar en annan metod. Det är ett sätt att eliminera "magiska strängar" när du implementerar INotifyPropertyChanged i olika gränssnittsramverk. Som exempel:
public class MyUIClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName] string propertyName = default!)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string? _name;
    public string? Name
    {
        get { return _name;}
        set
        {
            if (value != _name)
            {
                _name = value;
                RaisePropertyChanged();   // notice that "Name" is not needed here explicitly
            }
        }
    }
}

I koden ovan behöver du inte ha någon literalsträng "Name" . Att använda CallerMemberName förhindrar stavfelsrelaterade buggar och ger även smidigare refaktorisering/namnbyte. Attribut ger deklarativ kraft till C#, men de är en metadataform av kod och fungerar inte själva.