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 Datenmmber angezeigt, aber sie werden als spezielle Methoden implementiert, die als Accessorenbezeichnet 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 die Implementierung einer Klasse ist eine Eigenschaft ein oder zwei Codeblöcke, die einen get Accessor und/oder einen set oder init Accessor darstellen. Der Codeblock für den get Accessor wird ausgeführt, wenn die Eigenschaft gelesen wird. Der Codeblock für die set oder init Accessor wird ausgeführt, wenn der Eigenschaft ein Wert zugewiesen wird. Eine Eigenschaft ohne set Accessor gilt als schreibgeschützt. Eine Eigenschaft ohne get Accessor gilt als schreibgeschützt. Eine Eigenschaft mit beiden Accessoren ist Lese-/Schreibzugriff. Sie können einen init Accessor anstelle eines set Accessors verwenden, um die Einstellung der Eigenschaft als Teil der Objektinitialisierung zu ermöglichen, andernfalls aber schreibgeschützt.

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.

Der get Accessor

Der Textkörper des get Accessors ähnelt der 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 Eigenschaftenaccessoren bereitstellen, werden häufig Sicherungsfelderaufgerufen.

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";
        }
    }
}

Der set Accessor

Der set Accessor ähnelt einer Methode, deren Rückgabetyp ungültig ist. Er verwendet einen impliziten Parameter namens value, dessen Typ der Eigenschaft ist. 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 Sicherungsfeld direkt. Im folgenden Beispiel wird der set-Eigenschaft ein Name 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

Der init Accessor

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-/Schreibzugriffseigenschaften

Eigenschaften bieten den Komfort öffentlicher Datenmember ohne die Risiken, die ungeschützten, unkontrollierten und nicht überprüften Zugriff auf die Daten eines Objekts aufweisen. Eigenschaften deklarieren Accessoren: spezielle Methoden, die Werte aus dem zugrunde liegenden Datenmememm zuweisen und abrufen. Der set Accessor ermöglicht die Zuweisung von Datenmembern, und der get Accessor 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örperliche Mitglieder
  • Feldgestützte Eigenschaften
  • Erforderliche Eigenschaften

Ausdruckskörperliche Mitglieder

Ausdruckskörperelemente bieten eine präzisere Syntax zum Schreiben von Accessoren mit einzeiligen Eigenschaften.

Anmerkung

Ausdruckskörperelemente können auch auf Methoden, Indexer und Ereignisaccessoren angewendet werden.

Eigenschaftenaccessoren bestehen häufig aus einzeiligen Anweisungen, die 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 Ausdruckstextdefinition besteht aus dem => Token gefolgt vom Ausdruck, der zum Zuweisen oder Abrufen des Eigenschaftswerts verwendet wird. Da die für die Student Klasse definierten Accessoren eine einzeilige Anweisung implementieren, ist es ein guter Kandidat für die Aktualisierung mithilfe von Ausdruckstextdefinitionen.

Der folgende Codeausschnitt aktualisiert die Student Klasse mithilfe von Ausdruckstextdefinitionen 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;
    }
}

Feld-gesicherte 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 schlüsselwort field greift auf das compilersynthetisierte Sicherungsfeld für eine Eigenschaft zu. Sie können einen Eigenschaftsaccessor schreiben, ohne explizit ein separates Sicherungsfeld 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 field Schlüsselwort schattiert ein Feld mit dem Namen field im Bereich eines Eigenschaftenaccessors. 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 nullwerte Zeichenfolge festgelegt. 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, sind nicht erforderlich, um required Eigenschaften mit einem Objektinitialisierer festzulegen.

Wichtig

Verwechseln Sie required nicht mit nicht nullwertem Wert. 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();