Erste Schritte mit Klasseneigenschaften und Accessoren

Abgeschlossen

Klasseneigenschaften bieten einen flexiblen Mechanismus zum Lesen, Schreiben oder Berechnen des Werts eines Datenfelds. Sie werden als öffentliche Datenmitglied angezeigt, aber sie werden als spezielle Methoden implementiert, die als Accessoren bezeichnet werden. Mit diesem Feature können Anrufer einfach auf Daten zugreifen und gleichzeitig die Datensicherheit und Flexibilität fördern.

Verwenden von Eigenschaften

Eigenschaften kombinieren Aspekte von Feldern und Methoden. Dem Benutzer eines Objekts scheint eine Eigenschaft ein Feld zu sein. Für den Zugriff auf die Eigenschaft ist dieselbe Syntax wie für den Zugriff auf ein Feld erforderlich. Für den Implementierer einer Klasse ist eine Eigenschaft ein oder zwei Codeblöcke, die einem get-Accessor und/oder einem set- oder init-Accessor entsprechen. Der Codeblock für den get Accessor wird ausgeführt, wenn die Eigenschaft gelesen wird. Der Codeblock für den set- oder init-Accessor wird ausgeführt, wenn der Eigenschaft ein Wert zugewiesen wird. Eine Eigenschaft ohne einen set-Accessor ist schreibgeschützt. Eine Eigenschaft ohne get-Accessor gilt als nur schreibbar. Eine Eigenschaft, die beide Zugriffsmethoden besitzt, ist lesbar und schreibbar. Sie können einen init-Accessor anstelle eines set-Accessors verwenden, damit die Eigenschaft als Teil der Objektinitialisierung festgelegt werden kann, andernfalls aber schreibgeschützt ist.

Im Gegensatz zu Feldern werden Eigenschaften nicht als Variablen klassifiziert. Daher können Sie eine Eigenschaft nicht als ref- oder out-Parameter übergeben.

Eigenschaften werden häufig verwendet, um die folgenden Szenarien zu unterstützen:

  • Sie können Daten überprüfen, bevor sie eine Änderung zulassen.
  • Sie können Daten für eine Klasse transparent verfügbar machen, in der diese Daten aus einer anderen Quelle abgerufen werden, z. B. einer Datenbank.
  • Sie können eine Aktion ausführen, wenn Daten geändert werden, z. B. das Auslösen eines Ereignisses oder das Ändern des Werts anderer Felder.

Eigenschaften werden im Codeblock einer Klassendefinition deklariert. Eigenschaften werden deklariert, indem die Zugriffsebene des Felds angegeben wird, gefolgt vom Typ der Eigenschaft, gefolgt vom Namen der Eigenschaft, gefolgt von einem Codeblock, der einen get Accessor und/oder einen set Accessor deklariert.

Die get-Zugriffsmethode

Der Text des get Accessors ähnelt dem einer Methode. Er muss einen Wert des Eigenschaftstyps zurückgeben. Der C#-Compiler und der JIT-Compiler (Just-in-Time) erkennen allgemeine Muster für die Implementierung des get Accessors und optimieren sie für diese Muster.

Beispielsweise ist ein get Accessor, der ein Feld ohne Berechnung zurückgibt, wahrscheinlich auf einen Speicherlesevorgang dieses Felds optimiert. Automatisch implementierte Eigenschaften, die in der nächsten Einheit dieses Moduls untersucht werden, folgen diesem Muster und profitieren von diesen Optimierungen. Eine virtuelle get Accessormethode kann jedoch nicht inlineiert werden, da der Compiler zur Kompilierungszeit nicht weiß, welche Methode zur Laufzeit tatsächlich aufgerufen werden kann.

Das folgende Beispiel zeigt einen get Accessor, der den Wert eines privaten Felds _namezurückgibt:


public class Employee
{
    private string _name = "unknown";  // the name field
    public string Name
    {
        get { return _name; }  // the Name property
    }
}

In diesem Beispiel enthält die Employee-Klasse ein privates Feld und eine öffentliche Eigenschaft. Hier ist eine Erläuterung der Employee Klassendefinition:

  • Die Employee-Klasse wird mit dem Modifizierer für den öffentlichen Zugriff definiert, was bedeutet, dass sie von jedem anderen Code in derselben Assembly oder einer anderen Assembly zugänglich ist, die darauf verweist.
  • Das private Feld _name wird mit dem typ string deklariert und einem Standardwert "unknown"zugewiesen.
  • Die öffentliche Eigenschaft Name wird mit dem string Typ deklariert. Die Eigenschaft enthält einen get Accessor, der den Wert des felds _name zurückgibt.

Anmerkung

Die Felder, die die Werte für Eigenschaftszugriffsmethoden bereitstellen, werden häufig als Unterstützungsfelder bezeichnet.

Nehmen Sie sich eine Minute Zeit, um den Code zu berücksichtigen, der ein Employee-Objekt instanziiert und den Wert der Name-Eigenschaft liest:

Employee employee = new Employee();  // create an instance of the Employee class

Console.Write(employee.Name);  // use the get accessor to read the value of the Name property

In diesem Codebeispiel wird ein neues Employee-Objekt namens employeeinstanziiert. Wenn Ihr Code auf die employee.Name-Eigenschaft verweist, mit Ausnahme des Ziels einer Zuweisung, wird der get Accessor aufgerufen, um den Wert der Eigenschaft zu lesen. In diesem Fall gibt der get Accessor den Wert des felds _name zurück, dem der Wert "unknown"zugewiesen wird.

Der Codeblock für einen get Accessor kann auch verwendet werden, um einen berechneten Wert zurückzugeben. Dies kann hilfreich sein, wenn Sie sicherstellen möchten, dass eine Eigenschaft immer einen Wert ungleich NULL zurückgibt. Zum Beispiel:


public class Manager
{
    private string? _name;
    public string Name
    {
        get
        {
            return _name != null ? _name : "NA";
        }
    }
}

Die set-Zugriffsmethode

Der set Accessor ähnelt einer Methode, deren Rückgabetyp ungültig ist. Es verwendet einen impliziten Parameter mit dem Namen value, dessen Typ dem Typ der Eigenschaft entspricht. Der Compiler und JIT-Compiler erkennen auch gängige Muster für einen set oder init Accessor. Diese allgemeinen Muster sind optimiert und schreiben den Speicher für das Unterstützungsfeld direkt. Im folgenden Beispiel wird der Eigenschaft Name ein set-Accessor hinzugefügt:


class Student
{
    private string? _name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return _name != null ? _name : "NA";
        }
        set
        {
            _name = value;
        }
    }
}

Nehmen Sie sich eine Minute Zeit, um den Code zu berücksichtigen, der eine Instanz der Student-Klasse erstellt. Wenn Sie der Name-Eigenschaft einen Wert zuweisen, wird der set Accessor mithilfe eines Arguments aufgerufen, das den neuen Wert bereitstellt. Zum Beispiel:


var student = new Student();
student.Name = "StudentName";  // the set accessor is invoked here

Console.Write(student.Name);  // the get accessor is invoked here

Die init-Zugriffsmethode

Der Code zum Erstellen eines init Accessors entspricht dem Code zum Erstellen eines set Accessors, mit der Ausnahme, dass Sie das schlüsselwort init anstelle von setverwenden. Der Unterschied besteht darin, dass der init Accessor nur im Konstruktor oder mithilfe eines objektinitialisiererverwendet werden kann.

Deklarieren und Verwenden von Lese-/Schreibeigenschaften

Eigenschaften stellen die Vorteile von öffentlichen Datenmembern bereit, ohne die mit dem ungeschützten, nicht kontrollierten und nicht geprüften Zugriff auf die Daten eines Objekts verknüpften Risiken aufzuweisen. Eigenschaften deklarieren Zugriffsmethoden: spezielle Methoden, die dem zugrunde liegenden Datenfeld Werte zuweisen und von diesem abrufen. Die set-Zugriffsmethode ermöglicht das Zuweisen von Datenmembern, und die get-Zugriffsmethode ruft Datenmemberwerte ab.

Dieses Beispiel zeigt eine Person Klasse mit zwei Eigenschaften: Name (Zeichenfolge) und Age (int). Beide Eigenschaften stellen get und set Accessoren bereit, sodass sie als Lese-/Schreibeigenschaften gelten.


class Person
{
    private string _name = "N/A";
    private int _age = 0;

    // Declare a Name property of type string:
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }

    // Declare an Age property of type int:
    public int Age
    {
        get
        {
            return _age;
        }
        set
        {
            _age = value;
        }
    }
}

class TestPerson
{
    static void Main()
    {
        // Create a new Person object named person:
        Person person = new Person();

        // Print out the default name and age of the person:
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Set some values on the person object:
        person.Name = "PersonName";
        person.Age = 99;
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Increment the Age property:
        person.Age += 1;
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

/* Output:
Person details - Name = N/A, Age = 0
Person details - Name = PersonName, Age = 99
Person details - Name = PersonName, Age = 100
*/

Accessorsyntax und Codierungstechniken

Zusätzlich zur grundlegenden Syntax zum Deklarieren von Eigenschaften enthält C# Syntax, mit der Sie präziseren und ausdrucksstarkeren Code schreiben können. Zu diesen Features gehören:

  • Ausdruckskörpermember
  • Feldgestützte Eigenschaften
  • Erforderliche Eigenschaften

Ausdruckskörpermember

Ausdruckskörpermember bieten eine präzisere Syntax zum Schreiben von Zugriffsmethoden mit einzeiligen Eigenschaften.

Anmerkung

Ausdruckskörpermember können auch auf Methoden, Indexer und Ereigniszugriffsmethoden angewendet werden.

Häufig bestehen Eigenschaftenaccessoren aus einzeilige Anweisungen, die gerade das Ergebnis eines Ausdrucks zuweisen oder zurückgeben. Die Person Klasse, die Sie weiter oben in dieser Einheit untersucht haben, ist ein gutes Beispiel:


class Student
{
    private string? _name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return _name != null ? _name : "NA";
        }
        set
        {
            _name = value;
        }
    }
}

In diesem Beispiel verwendet die Name-Eigenschaft eine einzeilige Anweisung, um den Wert des felds _name zurückzugeben, wenn es nicht NULL ist, oder die Zeichenfolge "NV", wenn sie NULL ist. Der set Accessor verwendet eine einzeilige Anweisung, um dem feld Name den Wert der _name-Eigenschaft zuzuweisen.

Eine Definition eines Ausdruckskörpers besteht aus dem Token =>, gefolgt von dem Ausdruck, der zum Zuweisen oder Abrufen des Eigenschaftswerts verwendet wird. Da die für die Student-Klasse definierten Zugriffsmethoden eine einzeilige Anweisung implementieren, sind sie ein guter Kandidat für die Aktualisierung mithilfe von Ausdruckstextdefinitionen.

Der folgende Codeausschnitt aktualisiert die Student-Klasse mithilfe von Ausdruckskörperdefinitionen für die Accessoren get und set:


class Student
{
    private string? _name;  // the name field
    public string Name    // the Name property
    {
        get => _name ?? "NA";
        set => _name = value;
    }
}

Feldgestützte Eigenschaften

Ab C# 13 können Sie dem Accessor für eine Eigenschaft mithilfe der field Schlüsselwortvorschaufunktion eine Überprüfung oder eine andere Logik hinzufügen. Das field Schlüsselwort greift auf das vom Compiler generierte zugrunde liegende Feld für eine Eigenschaft zu. Damit können Sie einen Accessor für eine Eigenschaft schreiben, ohne explizit ein separates zugrunde liegendes Feld zu deklarieren.


public class Person
{
    public string? FirstName 
    { 
        get;
        set => field = value.Trim(); 
    }

    // Omitted for brevity.
}

Wichtig

Das Schlüsselwort field ist ein Vorschaufeature in C# 13. Sie müssen .NET 9 verwenden und das <LangVersion> Element so festlegen, dass es in der Projektdatei in der Vorschau angezeigt wird, um das field Kontextschlüsselwort zu verwenden.

Achten Sie darauf, die field Schlüsselwortfunktion in einer Klasse zu verwenden, die ein Feld mit dem Namen fieldhat. Das neue Schlüsselwort field überlagert ein Feld namens field im Bereich eines Eigenschafts-Accessors. Sie können entweder den Namen der field Variablen ändern oder das @-Token verwenden, um auf den Feldbezeichner als @fieldzu verweisen.

Erforderliche Eigenschaften

Im vorherigen Beispiel kann ein Aufrufer eine Person mithilfe des Standardkonstruktors erstellen, ohne die eigenschaft FirstName festzulegen. Der Eigenschaftentyp ist auf eine Zeichenfolge festgelegt, die Nullwerte zulässt. Ab C# 11 können Sie festlegen, dass Aufrufer eine Eigenschaft festlegen:


public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName) => FirstName = firstName;

    public required string FirstName { get; init; }

    // Omitted for brevity.
}

Der vorangehende Code nimmt zwei Änderungen an der Person Klasse vor. Zunächst enthält die FirstName-Eigenschaftsdeklaration den required Modifizierer. Das bedeutet, dass Code, der eine neue Person erstellt, diese Eigenschaft mit einem Objektinitialisierer festlegen muss. Zweitens verfügt der Konstruktor, der einen firstName Parameter verwendet, über das attribut System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute. Dieses Attribut informiert den Compiler darüber, dass dieser Konstruktor alle erforderlichen Member festlegt. Aufrufer, die diesen Konstruktor verwenden, müssen required Eigenschaften nicht mit einer Objektinitialisierung festlegen.

Wichtig

Verwechseln Sie required nicht mit einer Zeichenfolge, die keine Nullwerte zulässt. Es ist gültig, eine required Eigenschaft auf null oder defaultfestzulegen. Wenn der Typ nicht nullfähig ist, z. B. string in diesen Beispielen, gibt der Compiler eine Warnung aus.


var aPerson = new Person("PersonName");
aPerson = new Person{ FirstName = "PersonName"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();