EigenschaftenProperties

Eigenschaften sind Bürger erster Klasse in C#.Properties are first class citizens in C#. Die Sprache definiert die Syntax, mit der Entwickler Code schreiben können, der genau ihre Entwurfsabsicht ausdrückt.The language defines syntax that enables developers to write code that accurately expresses their design intent.

Eigenschaften verhalten sich wie Felder, wenn darauf zugegriffen wird.Properties behave like fields when they are accessed. Jedoch sind Eigenschaften im Gegensatz zu Feldern mit Accessoren implementiert, die die ausgeführten Anweisungen definieren, wenn auf eine Eigenschaft zugegriffen oder sie zugewiesen wird.However, unlike fields, properties are implemented with accessors that define the statements executed when a property is accessed or assigned.

EigenschaftssyntaxProperty Syntax

Die Syntax für Eigenschaften ist eine natürliche Erweiterung von Feldern.The syntax for properties is a natural extension to fields. Ein Feld definiert einen Speicherort:A field defines a storage location:

public class Person
{
    public string FirstName;
    // remaining implementation removed from listing
}

Eine Eigenschaftendefinition enthält Deklarationen für einen get- und set-Accessor, der den Wert dieser Eigenschaft abruft oder zuweist:A property definition contains declarations for a get and set accessor that retrieves and assigns the value of that property:

public class Person
{
    public string FirstName { get; set; }

    // remaining implementation removed from listing
}

Die oben dargestellte Syntax ist die Auto-Eigenschaft-Syntax.The syntax shown above is the auto property syntax. Der Compiler generiert den Speicherort für das Feld, das die Eigenschaft sichert.The compiler generates the storage location for the field that backs up the property. Der Compiler implementiert außerdem den Text der get- und set-Accessoren.The compiler also implements the body of the get and set accessors.

In einigen Fällen müssen Sie eine Eigenschaft auf einen anderen Wert als den Standardwert für seinen Datentyp initialisieren.Sometimes, you need to initialize a property to a value other than the default for its type. C# ermöglicht dies, indem nach der schließenden Klammer für die Eigenschaft ein Wert festgelegt wird.C# enables that by setting a value after the closing brace for the property. Möglicherweise bevorzugen Sie den Anfangswert für die Eigenschaft FirstName als leere Zeichenfolge und nicht null.You may prefer the initial value for the FirstName property to be the empty string rather than null. Sie würden dies wie unten dargestellt angeben:You would specify that as shown below:

public class Person
{
    public string FirstName { get; set; } = string.Empty;

    // remaining implementation removed from listing
}

Dies eignet sich am besten für schreibgeschützte Eigenschaften, wie Sie später in diesem Artikel sehen werden.This is most useful for read-only properties, as you'll see later in this topic.

Sie können den Speicher auch selbst definieren, wie unten dargestellt:You can also define the storage yourself, as shown below:

public class Person
{
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }
    private string firstName;
    // remaining implementation removed from listing
}

Wenn die Implementierung einer Eigenschaft ein einzelner Ausdruck ist, können Sie Ausdruckskörpermember für die Getter oder Setter verwenden:When a property implementation is a single expression, you can use expression bodied members for the getter or setter:

public class Person
{
    public string FirstName
    {
        get => firstName;
        set => firstName = value;
    }
    private string firstName;
    // remaining implementation removed from listing
}

Diese vereinfachte Syntax wird gegebenenfalls in diesem Thema verwendet werden.This simplified syntax will be used where applicable throughout this topic.

Die oben gezeigte Eigenschaftendefinition ist eine Schreib-Lese-Eigenschaft.The property definition shown above is a read-write property. Beachten Sie das Schlüsselwort value im set-Accessor.Notice the keyword value in the set accessor. Der set-Accessor verfügt immer über einen einzelnen Parameter namens value.The set accessor always has a single parameter named value. Der get-Accessor muss einen Wert zurückgeben, der in den Typ der Eigenschaft konvertiert werden kann (string in diesem Beispiel).The get accessor must return a value that is convertible to the type of the property (string in this example).

Das sind die Grundlagen der Syntax.That's the basics of the syntax. Es gibt viele verschiedene Varianten, die eine Vielzahl von verschiedenen Entwürfen unterstützen.There are many different variations that support a variety of different design idioms. Lassen Sie uns diese erforschen, und lernen Sie die Syntaxoptionen für jede kennen.Let's explore those, and learn the syntax options for each.

SzenarienScenarios

In den Beispielen oben wurde eine der einfachsten Fälle von Eigenschaftendefinition gezeigt: eine Schreib-Lese-Eigenschaft ohne Überprüfung.The examples above showed one of the simplest cases of property definition: a read-write property with no validation. Durch das Schreiben des Codes, den Sie in den get- und set-Accessoren möchten, können Sie viele verschiedene Szenarios erstellen.By writing the code you want in the get and set accessors, you can create many different scenarios.

ValidierungValidation

Sie können Code im set-Accessor schreiben, um sicherzustellen, dass die durch eine Eigenschaft dargestellten Werte immer gültig sind.You can write code in the set accessor to ensure that the values represented by a property are always valid. Angenommen, eine Regel für die Person-Klasse ist z.B., dass der Name nicht leer oder kein Leerzeichen sein kann.For example, suppose one rule for the Person class is that the name cannot be blank, or whitespace. Sie würden das wie folgt schreiben:You would write that as follows:

public class Person
{
    public string FirstName
    {
        get => firstName;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("First name must not be blank");
            firstName = value;
        }
    }
    private string firstName;
    // remaining implementation removed from listing
}

Das obige Beispiel erzwingt die Regel, dass der erste Name nicht leer oder kein Leerzeichen sein darf.The example above enforces the rule that the first name must not be blank, or whitespace. Wenn ein Entwickler schreibtIf a developer writes

hero.FirstName = "";

Die Zuweisung löst eine ArgumentException aus.That assignment throws an ArgumentException. Da der set-Accessor einen void-Rückgabetyp aufweisen muss, melden Sie Fehler im set-Accessor durch Auslösen einer Ausnahme.Because a property set accessor must have a void return type, you report errors in the set accessor by throwing an exception.

Das ist ein einfacher Fall der Überprüfung.That is a simple case of validation. Sie können die gleiche Syntax auf alles andere in Ihrem Szenario erweitern, was benötigt wird.You can extend this same syntax to anything needed in your scenario. Sie können die Beziehungen zwischen unterschiedlichen Eigenschaften oder gegen externe Bedingungen überprüfen.You can check the relationships between different properties, or validate against any external conditions. Gültige C#-Anweisungen sind in einem Eigenschaftenaccessor gültig.Any valid C# statements are valid in a property accessor.

SchreibgeschütztRead-only

Bis zu diesem Zeitpunkt sind alle Eigenschaftsdefinitionen, die Sie gesehen haben Lese-/Schreibeigenschaften mit öffentlichen Accessoren.Up to this point, all the property definitions you have seen are read/write properties with public accessors. Dies sind nicht die einzige gültigen Eingabehilfen für Eigenschaften.That's not the only valid accessibility for properties. Sie können schreibgeschützte Eigenschaften erstellen, oder den set- und get-Accessoren verschiedene Eingabehilfen geben.You can create read-only properties, or give different accessibility to the set and get accessors. Angenommen, Ihre Person-Klasse sollte nur die Änderung des Werts der FirstName-Eigenschaft von anderen Methoden in dieser Klasse ermöglichen.Suppose that your Person class should only enable changing the value of the FirstName property from other methods in that class. Sie konnten dem set-Accessor private-Eingabehilfen anstelle von public geben:You could give the set accessor private accessibility instead of public:

public class Person
{
    public string FirstName { get; private set; }

    // remaining implementation removed from listing
}

Nun, kann auf die FirstName-Eigenschaft von einem beliebigen Code zugegriffen werden, aber es kann nur von einem anderem Code in der Person-Klasse zugewiesen werden.Now, the FirstName property can be accessed from any code, but it can only be assigned from other code in the Person class.

Sie können einen restriktiven Zugriffsmodifizierer zum set- oder get-Accessor hinzufügen.You can add any restrictive access modifier to either the set or get accessors. Jeder Zugriffsmodifizierer, den Sie auf den einzelnen Accessor platzieren, muss eingeschränkter sein als der Zugriffsmodifizierer für die Eigenschaftsdefinition.Any access modifier you place on the individual accessor must be more limited than the access modifier on the property definition. Das Obige ist zulässig, da die FirstName-Eigenschaft public ist, aber der set-Accessor ist private.The above is legal because the FirstName property is public, but the set accessor is private. Sie können keine private-Eigenschaft mit einem public-Accessor deklarieren.You could not declare a private property with a public accessor. Eigenschaftsdeklarationen können ebenfalls deklariert werden protected, internal, protected internal, private protected oder sogar private.Property declarations can also be declared protected, internal, protected internal, private protected or even private.

Es ist auch zulässig, den restriktiveren Modifizierer auf dem get-Accessor zu platzieren.It is also legal to place the more restrictive modifier on the get accessor. Sie verfügen z.B. über eine public-Eigenschaft, schränken jedoch den get-Accessor auf private ein.For example, you could have a public property, but restrict the get accessor to private. Dieses Szenario wird in der Praxis nur selten ausgeführt.That scenario is rarely done in practice.

Sie können auch Änderungen an einer Eigenschaft beschränken, sodass sie nur in einem Konstruktor oder einem Eigenschafteninitialisierer festgelegt werden kann.You can also restrict modifications to a property so that it can only be set in a constructor or a property initializer. Sie können die Person-Klasse daher wie folgt ändern:You can modify the Person class so as follows:

public class Person
{
    public Person(string firstName)
    {
        this.FirstName = firstName;
    }

    public string FirstName { get; }

    // remaining implementation removed from listing
}

Diese Funktion wird am häufigsten für die Initialisierung von Auflistungen verwendet, die als schreibgeschützte Eigenschaften verfügbar gemacht werden:This feature is most commonly used for initializing collections that are exposed as read-only properties:

public class Measurements
{
    public ICollection<DataPoint> points { get; } = new List<DataPoint>();
}

Berechnete EigenschaftenComputed Properties

Eine Eigenschaft muss nicht einfach den Wert eines Memberfelds zurückgeben.A property does not need to simply return the value of a member field. Sie können Eigenschaften erstellen, die einen berechneten Wert zurückgeben.You can create properties that return a computed value. Lassen Sie uns das Person-Objekt so erweitern, dass es den vollständigen Namen zurückgibt, berechnet durch die Verkettung des ersten und letzten Namens:Let's expand the Person object to return the full name, computed by concatenating the first and last names:

public class Person
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string FullName { get { return $"{FirstName} {LastName}"; } }
}

Im Beispiel oben wird die Syntax Zeichenfolgeninterpolation verwendet, um die formatierte Zeichenfolge für den vollständigen Namen zu erstellen.The example above uses the String Interpolation syntax to create the formatted string for the full name.

Sie können auch Ausdruckskörpermember verwenden, das eine kompaktere Möglichkeit zum Erstellen der berechneten FullName-Eigenschaft bietet:You can also use Expression-bodied Members, which provides a more succinct way to create the computed FullName property:

public class Person
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string FullName =>  $"{FirstName} {LastName}";
}

Ausdruckskörpermember verwenden die Syntax Lambda-Ausdruck zum Definieren einer Methode, die einen einzelnen Ausdruck enthält.Expression-bodied Members use the lambda expression syntax to define a method that contain a single expression. Hier gibt dieser Ausdruck den vollständigen Namen für das Person-Objekt zurück.Here, that expression returns the full name for the person object.

Verzögert ausgewertete EigenschaftenLazy Evaluated Properties

Sie können das Konzept einer berechneten Eigenschaft mit dem Speicher mischen und eine verzögert ausgewertete Eigenschaft erstellen.You can mix the concept of a computed property with storage and create a lazy evaluated property. Sie können z.B. die FullName-Eigenschaft so aktualisieren, dass die Zeichenfolgenformatierung nur beim ersten Zugriff umgesetzt wird:For example, you could update the FullName property so that the string formatting only happened the first time it was accessed:

public class Person
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    private string fullName;
    public string FullName
    {
        get
        {
            if (fullName == null)
                fullName = $"{FirstName} {LastName}";
            return fullName;
        }
    }
}

Der obige Code enthält jedoch einen Fehler.The above code contains a bug though. Wenn der Code den Wert der FirstName- oder LastName-Eigenschaft aktualisiert, ist das zuvor ausgewertete fullName-Feld ungültig.If code updates the value of either the FirstName or LastName property, the previously evaluated fullName field is invalid. Sie müssen die set-Accessoren der FirstName- und LastName-Eigenschaft aktualisieren, damit das fullName-Feld erneut berechnet wird:You need to update the set accessors of the FirstName and LastName property so that the fullName field is calculated again:

public class Person
{
    private string firstName;
    public string FirstName
    {
        get => firstName;
        set
        {
            firstName = value;
            fullName = null;
        }
    }

    private string lastName;
    public string LastName
    {
        get => lastName;
        set
        {
            lastName = value;
            fullName = null;
        }
    }

    private string fullName;
    public string FullName
    {
        get
        {
            if (fullName == null)
                fullName = $"{FirstName} {LastName}";
            return fullName;
        }
    }
}

Diese endgültige Version wertet die FullName-Eigenschaft nur bei Bedarf aus.This final version evaluates the FullName property only when needed. Wenn die zuvor berechnete Version gültig ist, wird sie verwendet.If the previously calculated version is valid, it's used. Wenn eine andere Änderung des Zustands die zuvor berechnete Version ungültig macht, wird sie neu berechnet.If another state change invalidates the previously calculated version, it will be recalculated. Entwickler, die diese Klasse verwenden, müssen die Details der Implementierung nicht kennen.Developers that use this class do not need to know the details of the implementation. Keine dieser interne Änderungen hat Einfluss auf die Verwendung des Person-Objekts.None of these internal changes affect the use of the Person object. Das ist der Hauptgrund für die Verwendung von Eigenschaften, um Datenmember eines Objekts verfügbar zu machen.That's the key reason for using Properties to expose data members of an object.

INotifyPropertyChangedINotifyPropertyChanged

Ein abschließendes Szenario, in dem Sie Code in einem Eigenschaftenaccessor schreiben müssen, ist zur Unterstützung der INotifyPropertyChanged-Schnittstelle, die verwendet wird, um Clients mit Datenbindung zu benachrichtigen, dass ein Wert geändert wurde.A final scenario where you need to write code in a property accessor is to support the INotifyPropertyChanged interface used to notify data binding clients that a value has changed. Wenn sich der Wert einer Eigenschaft ändert, löst das Objekt das PropertyChanged-Ereignis aus, um die Änderung anzuzeigen.When the value of a property changes, the object raises the PropertyChanged event to indicate the change. Die Bibliotheken mit Datenbindung wiederum aktualisieren die auf dieser Änderung basierenden Anzeigeelemente.The data binding libraries, in turn, update display elements based on that change. Der folgende Code zeigt, wie Sie INotifyPropertyChanged für die FirstName-Eigenschaft dieser Person-Klasse implementieren würden.The code below shows how you would implement INotifyPropertyChanged for the FirstName property of this person class.

public class Person : INotifyPropertyChanged
{
    public string FirstName
    {
        get => firstName;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("First name must not be blank");
            if (value != firstName)
            {
                PropertyChanged?.Invoke(this, 
                    new PropertyChangedEventArgs(nameof(FirstName)));
            }
            firstName = value;
        }
    }
    private string firstName;

    public event PropertyChangedEventHandler PropertyChanged;
    // remaining implementation removed from listing
}

Der Operator ?. wird bedingter NULL-Operator genannt.The ?. operator is called the null conditional operator. Es sucht vor der Auswertung der rechten Seite des Operators nach einem NULL-Verweis.It checks for a null reference before evaluating the right side of the operator. Das Endergebnis bedeutet, dass, wenn sich keine Abonnenten im PropertyChanged-Ereignis befinden, der Code zum Auslösen des Ereignisses nicht ausgeführt wird.The end result is that if there are no subscribers to the PropertyChanged event, the code to raise the event doesn't execute. Er würde eine NullReferenceException auslösen, ohne diese in diesem Fall zu überprüfen.It would throw a NullReferenceException without this check in that case. Weitere Informationen finden Sie auf der Seite events.See the page on events for more details. In diesem Beispiel wird auch der neue nameof-Operator verwendet, um vom Symbol des Eigenschaftennamen in die Textdarstellung zu konvertieren.This example also uses the new nameof operator to convert from the property name symbol to its text representation. Mithilfe von nameof können Fehler reduziert werden, bei denen Sie den Namen der Eigenschaft falsch eingegeben haben.Using nameof can reduce errors where you have mistyped the name of the property.

Erneut ist dies ein Beispiel für einen Fall, in dem Sie Code in Ihren Accessoren schreiben können, um die benötigten Szenarios zu unterstützen.Again, this is an example of a case where you can write code in your accessors to support the scenarios you need.

SchlussbemerkungSumming up

Eigenschaften sind eine Form intelligenter Felder in einer Klasse oder einem Objekt.Properties are a form of smart fields in a class or object. Außerhalb des Objekts wirken diese wie Felder in dem Objekt.From outside the object, they appear like fields in the object. Eigenschaften können jedoch mithilfe der vollständigen Palette der C#-Funktionalität implementiert werden.However, properties can be implemented using the full palette of C# functionality. Sie können Überprüfung, verschiedene Eingabehilfen, verzögerte Auswertung oder alle Anforderungen, die Ihre Szenarios benötigen, bereitstellen.You can provide validation, different accessibility, lazy evaluation, or any requirements your scenarios need.