Freigeben über


Definieren und Lesen von benutzerdefinierten Attributen

Attribute bieten eine Möglichkeit zum Zuordnen von Informationen mit Code auf deklarative Weise. Sie können auch ein wiederverwendbares Element bereitstellen, das auf verschiedene Ziele angewendet werden kann. Sehen Sie sich das ObsoleteAttribute an. Sie kann auf Klassen, Strukturen, Methoden, Konstruktoren und vieles mehr angewendet werden. Es deklariert , dass das Element veraltet ist. Dann liegt es am C#-Compiler, nach diesem Attribut zu suchen und daraufhin eine Aktion auszuführen.

In diesem Lernprogramm erfahren Sie, wie Sie Ihrem Code Attribute hinzufügen, eigene Attribute erstellen und verwenden und wie Sie einige in .NET integrierte Attribute verwenden.

Voraussetzungen

Sie müssen Ihren Computer so einrichten, dass .NET ausgeführt wird. Die Installationsanweisungen finden Sie auf der .NET-Downloadseite . Sie können diese Anwendung unter Windows, Ubuntu Linux, macOS oder in einem Docker-Container ausführen. Sie müssen Ihren bevorzugten Code-Editor installieren. In den folgenden Beschreibungen wird Visual Studio Code verwendet, bei dem es sich um einen plattformübergreifenden Open Source-Editor handelt. Sie können jedoch alle Tools verwenden, mit denen Sie vertraut sind.

Erstellen der App

Nachdem Sie nun alle Tools installiert haben, erstellen Sie eine neue .NET-Konsolen-App. Um den Befehlszeilengenerator zu verwenden, führen Sie den folgenden Befehl in Ihrer bevorzugten Shell aus:

dotnet new console

Mit diesem Befehl werden minimalistische .NET-Projektdateien erzeugt. Führen Sie dotnet restore aus, um die Abhängigkeiten wiederherzustellen, die zum Kompilieren dieses Projekts erforderlich sind.

Sie müssen dotnet restore nicht ausführen, da der Befehl implizit von allen Befehlen ausgeführt wird, die eine Wiederherstellung erfordern. Zu diesen zählen z. B. dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish und dotnet pack. Verwenden Sie die Option --no-restore, um die implizite Wiederherstellung zu deaktivieren.

In bestimmten Fällen eignet sich der dotnet restore-Befehl dennoch. Dies ist etwa bei Szenarios der Fall, in denen die explizite Wiederherstellung sinnvoll ist. Beispiele hierfür sind Continuous Integration-Builds in Azure DevOps Services oder Buildsysteme, die den Zeitpunkt für die Wiederherstellung explizit steuern müssen.

Informationen zum Verwalten von NuGet-Feeds finden Sie in der dotnet restoreDokumentation.

Verwenden Sie dotnet runzum Ausführen des Programms . Es sollte „Hello, World“ auf der Konsole ausgegeben werden.

Hinzufügen von Attributen zu Code

In C# sind Attribute Klassen, die von der Attribute Basisklasse erben. Jede Klasse, die von Attribute erbt, kann als eine Art "Tag" für andere Codeabschnitte verwendet werden. Es gibt zum Beispiel ein Attribut namens ObsoleteAttribute. Dieses Attribut signalisiert, dass Code veraltet ist und nicht mehr verwendet werden sollte. Sie platzieren dieses Attribut z. B. mithilfe von eckigen Klammern auf einer Klasse.

[Obsolete]
public class MyClass
{
}

Auch wenn die Klasse als ObsoleteAttribute benannt ist, ist es im Code nur erforderlich, [Obsolete] zu verwenden. Der größte C#-Code folgt dieser Konvention. Sie können den vollständigen Namen [ObsoleteAttribute] verwenden, wenn Sie dies auswählen.

Wenn Sie eine Klasse als veraltet markieren, ist es ratsam, einige Informationen darüber bereitzustellen, warum sie veraltet sind und/oder was stattdessen verwendet werden soll. Sie fügen einen Zeichenfolgenparameter in das veraltete Attribut ein, um diese Erklärung bereitzustellen.

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

Die Zeichenfolge wird als Argument an einen ObsoleteAttribute Konstruktor übergeben, als ob Sie schreiben var attr = new ObsoleteAttribute("some string").

Parameter für einen Attributkonstruktor sind auf einfache Typen/Literale beschränkt: bool, int, double, string, Type, enums, etc und Arrays dieser Typen. Sie können keinen Ausdruck oder eine Variable verwenden. Sie können positions- oder benannte Parameter verwenden.

Ein eigenes Attribut erstellen

Sie erstellen ein Attribut, indem Sie eine neue Klasse definieren, die von der Attribute Basisklasse erbt.

public class MySpecialAttribute : Attribute
{
}

Mit dem vorherigen Code können Sie [MySpecial] (oder [MySpecialAttribute]) als Attribut an anderer Stelle in der Codebasis verwenden.

[MySpecial]
public class SomeOtherClass
{
}

Attribute in der .NET-Basisklassenbibliothek wie ObsoleteAttribute lösen bestimmte Verhaltensweisen innerhalb des Compilers aus. Jedes von Ihnen erstellte Attribut fungiert jedoch nur als Metadaten und führt nicht zu code innerhalb der auszuführenden Attributklasse. Es liegt an Ihnen, auf diese Metadaten an anderer Stelle in Ihrem Code zu reagieren.

Es gibt hierbei ein Problem, das beachtet werden muss. Wie bereits erwähnt, können nur bestimmte Typen bei Verwendung von Attributen als Argumente übergeben werden. Beim Erstellen eines Attributtyps verhindert der C#-Compiler jedoch nicht, dass Sie diese Parameter erstellen. Im folgenden Beispiel haben Sie ein Attribut mit einem Konstruktor erstellt, der ordnungsgemäß kompiliert wird.

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

Sie können diesen Konstruktor jedoch nicht mit Attributsyntax verwenden.

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

Der vorangehende Code verursacht einen Compilerfehler wie Attribute constructor parameter 'myClass' has type 'Foo', which is not a valid attribute parameter type

Wie man die Verwendung von Attributen einschränkt

Attribute können für die folgenden "Ziele" verwendet werden. In den obigen Beispielen wurde die Verwendung für Klassen gezeigt, aber Attribute können auch für folgende Elemente verwendet werden:

  • Versammlung
  • Klasse
  • Konstruktor
  • Delegieren
  • Enumeration
  • Ereignis
  • Feld
  • GenerischerParameter
  • Schnittstelle
  • Methode
  • Modul
  • Parameter
  • Eigentum
  • ReturnValue
  • Struktur

Wenn Sie eine Attributklasse erstellen, erlaubt C# Ihnen standardmäßig, dieses Attribut für alle möglichen Ziele zu verwenden. Wenn Sie Ihr Attribut auf bestimmte Ziele beschränken möchten, können Sie dies mithilfe der AttributeUsageAttribute Attributklasse tun. Ganz richtig, ein Attribut für ein Attribut!

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

Wenn Sie versuchen, das obige Attribut auf etwas zu setzen, das keine Klasse oder Struktur ist, erhalten Sie einen Compilerfehler wie 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()
    { }
}

Verwenden von Attributen, die an ein Codeelement angefügt sind

Attribute fungieren als Metadaten. Ohne irgendeine äußere Kraft tun sie eigentlich nichts.

Um Attribute zu finden und zu bearbeiten, ist eine Reflexion erforderlich. Mit Reflection können Sie Code in C# schreiben, der anderen Code untersucht. Sie können z. B. Reflection verwenden, um Informationen zu einer Klasse abzurufen(fügen Sie am Anfang Ihres Codes hinzu using System.Reflection; ):

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

Damit wird etwa Folgendes ausgegeben: The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Nachdem Sie ein Objekt (oder ein TypeInfoMemberInfo, FieldInfooder ein anderes Objekt) haben, können Sie die GetCustomAttributes Methode verwenden. Diese Methode gibt eine Auflistung von Attribute Objekten zurück. Sie können auch GetCustomAttribute verwenden und einen Attributtyp angeben.

Hier ist ein Beispiel für die Verwendung von GetCustomAttributes in einer MemberInfo-Instanz für MyClass (bei der wir zuvor gesehen haben, dass darauf ein [Obsolete]-Attribut vorhanden ist).

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

Dies wird in der Konsole ausgegeben: Attribute on MyClass: ObsoleteAttribute. Versuchen Sie, weitere Attribute zu MyClass hinzuzufügen.

Es ist wichtig, darauf hinzuweisen, dass diese Attribute-Objekte verzögert instanziiert werden. Das heißt, sie werden nicht instanziiert, bis Sie GetCustomAttribute oder GetCustomAttributes verwenden. Sie werden außerdem jedes Mal instanziiert. Wenn GetCustomAttributes zweimal nacheinander aufgerufen wird, gibt es zwei verschiedene Instanzen von ObsoleteAttribute zurück.

Allgemeine Attribute in der Laufzeit

Attribute werden von vielen Tools und Frameworks verwendet. NUnit verwendet Attribute wie [Test] und [TestFixture] die von der NUnit-Testläuferin verwendet werden. ASP.NET MVC verwendet Attribute wie [Authorize] und stellt ein Aktionsfilter-Framework bereit, um querschnittliche Anliegen bei MVC-Aktionen auszuführen. PostSharp verwendet die Attributsyntax, um die seitenorientierte Programmierung in C# zu ermöglichen.

Hier sind einige wichtige Attribute, die in die .NET Core-Basisklassenbibliotheken integriert sind:

  • [Obsolete]. Diese wurde in den obigen Beispielen verwendet und lebt im System Namespace. Es ist hilfreich, deklarative Dokumentationen zu einer sich ändernden Codebasis bereitzustellen. Eine Nachricht kann in Form einer Zeichenfolge bereitgestellt werden, und ein anderer boolescher Parameter kann verwendet werden, um von einer Compilerwarnung zu einem Compilerfehler zu eskalieren.
  • [Conditional]. Dieses Attribut befindet sich im System.Diagnostics Namespace. Dieses Attribut kann auf Methoden (oder Attributklassen) angewendet werden. Sie müssen eine Zeichenfolge an den Konstruktor übergeben. Wenn diese Zeichenfolge keiner Direktive entspricht #define , entfernt der C#-Compiler alle Aufrufe dieser Methode (aber nicht die Methode selbst). In der Regel verwenden Sie diese Technik für Debuggingzwecke (Diagnose).
  • [CallerMemberName]. Dieses Attribut kann für Parameter verwendet werden und befindet sich im System.Runtime.CompilerServices Namespace. CallerMemberName ist ein Attribut, das zum Einfügen des Namens der Methode verwendet wird, die eine andere Methode aufruft. Es ist eine Möglichkeit, "magische Zeichenfolgen" bei der Implementierung von INotifyPropertyChanged in verschiedenen UI-Frameworks zu beseitigen. Beispiel:
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
            }
        }
    }
}

Im obigen Code müssen Sie nicht über eine Literalzeichenfolge "Name" verfügen. Die Verwendung CallerMemberName verhindert Tippfehler und sorgt auch für eine reibungslosere Umgestaltung/Umbenennung. Attribute bringen deklarative Macht für C# mit, sie sind jedoch eine Metadatenform von Code und handeln nicht selbst.