Eine Property sieht für den Code, der sie verwendet, wie ein Feld aus (order.Total, order.Total = 5), ist aber in Wahrheit ein Methodenpaar: ein get-Accessor, der einen Wert zurückgibt, und ein set-Accessor, der einen entgegennimmt. Diese Indirektion lässt eine Klasse Werte prüfen, berechnen oder das Schreiben verweigern, ohne dass sich ändert, wie Aufrufer sie verwenden.
Felder gegenüber Properties
Ein öffentliches Feld gibt direkten Zugriff auf den Speicher des Objekts. Jeder kann alles hineinschreiben:
public class Account
{
public decimal Balance; // any code can write -500 here
}
Eine Property setzt Code zwischen den Aufrufer und den Speicher. Hier ist die vollständige, handgeschriebene Form: ein privates Hintergrundfeld plus eine Property mit beiden Accessoren. Innerhalb von set ist das Schlüsselwort value der zugewiesene Wert.
Ausgabe:
250
Rejected a negative balance
250
Über die Validierung hinaus zählen Properties, weil der Großteil des .NET-Ökosystems mit ihnen arbeitet und Felder ignoriert: Interfaces können Properties deklarieren, aber keine Instanzfelder, Data Binding in WPF und MAUI bindet an Properties, und System.Text.Json serialisiert standardmäßig öffentliche Properties und überspringt Felder.
Auto-Properties
Wenn die Accessoren das Feld nur lesen und schreiben würden, lass den Compiler sie schreiben. { get; set; } ist eine automatisch implementierte Property: Der Compiler erzeugt das versteckte Hintergrundfeld für dich.
Ausgabe:
Notebook: 9.99, 10 left
Sie sieht aus wie ein Feld, wozu also der Aufwand? Weil es für kompilierten Code, der ein öffentliches Feld verwendet, eine Breaking Change ist, es später in eine Property umzuwandeln (auch für Argumente mit ref und out), während der Umbau einer Auto-Property in eine vollständige Property mit Validierung für Aufrufer unsichtbar bleibt. Mit { get; set; } zu beginnen hält diese Option kostenlos offen.
private set und schreibgeschützte Properties
Der meiste Zustand sollte für alle lesbar und nur vom Objekt selbst änderbar sein. Gib dem Setter einen engeren Zugriffsmodifizierer:
Ausgabe:
Ines: 2 items, total 15.75
Hier gibt es drei Arten von schreibgeschützt:
{ get; private set; }: Die Klasse kann den Wert jederzeit ändern, externer Code nicht.{ get; }(schreibgeschützte Auto-Property, C# 6): kann nur in einem Konstruktor oder Initialisierer zugewiesen werden. Nach der Konstruktion kann niemand sie ändern, nicht einmal die Klasse. So machst du eine Property unveränderlich.=> expression(berechnete Property): überhaupt kein Speicher. Der Ausdruck läuft bei jedem Lesen,Totalist also immer auf dem Stand der Liste.
Eine berechnete Property sollte billig sein und keine Nebeneffekte haben, weil Aufrufer Properties beiläufig lesen, in Schleifen und im Debugger. Wenn das Ermitteln des Werts echte Arbeit kostet (eine Datenbankabfrage, eine große Berechnung), mach daraus eine Methode wie CalculateTotal(), damit die Kosten sichtbar sind.
Accessoren mit Ausdruckskörper
Vollständige Properties mit einzeiligen Accessoren können => pro Accessor verwenden:
private string title;
public string Title
{
get => title;
set => title = value?.Trim() ?? "";
}
public decimal Total => SumPrices(); ist die Kurzform einer Property mit nur einem Getter; get => ... ist dieselbe Idee innerhalb einer Property, die auch einen Setter hat.
Logik in Getter und Setter: Benachrichtigungen und verzögerte Werte
Weil Accessoren Methoden sind, können sie mehr tun, als einen Wert zu speichern. Zwei verbreitete Muster: eine Änderungsbenachrichtigung aus dem Setter auslösen und einen Wert beim ersten Lesen berechnen und zwischenspeichern.
Ausgabe:
(building summary)
Theme: light
Theme: light
Theme changed
(building summary)
Theme: dark
Changed ist ein Event, und das Setter-Muster ist genau das, wonach Implementierungen von INotifyPropertyChanged in WPF und MAUI aussehen. nameof(Theme) hält den String synchron, wenn die Property umbenannt wird.
Der Fehler mit der endlosen Rekursion
Der häufigste Property-Bug ist ein Setter, der der Property statt dem Hintergrundfeld zuweist:
public string Name
{
get { return Name; } // calls get again, forever
set { Name = value; } // calls set again, forever
}
Jeder Accessor ruft sich selbst auf, und das Programm stirbt mit einer StackOverflowException, die sich nicht abfangen lässt. Die Lösung ist, ein separates Feld zu lesen und zu schreiben (name, kleingeschrieben) oder eine Auto-Property zu verwenden. C# 14 fügt genau für diesen Fall das Schlüsselwort field hinzu: Innerhalb eines Accessors verweist field auf das vom Compiler erzeugte Hintergrundfeld, set => field = value.Trim(); funktioniert also, ohne eines zu deklarieren.
init-Accessoren (C# 9)
Eine schreibgeschützte Property zwingt dich, jeden Wert durch einen Konstruktor zu reichen. C# 9 hat init eingeführt, einen Setter, der nur während der Objekterzeugung erlaubt ist: in einem Konstruktor oder in einem Objektinitialisierer.
public class Product
{
public string Sku { get; init; }
public decimal Price { get; init; }
}
var p = new Product { Sku = "MUG-01", Price = 8.50m }; // fine: during creation
p.Price = 4m; // error CS8852: Init-only property or indexer 'Product.Price' can only be assigned
// in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor
Das Ergebnis ist ein unveränderliches Objekt mit Initialisierersyntax, genau das, was Record-Klassen für ihre positionellen Properties verwenden. Um ein solches Objekt zu „ändern“, erzeugst du ein neues; Records machen das mit with zu einem Einzeiler.
In C# 7 ist die nächste Entsprechung eine schreibgeschützte Property plus ein Konstruktorparameter:
public class Product
{
public string Sku { get; }
public decimal Price { get; }
public Product(string sku, decimal price) { Sku = sku; Price = price; }
}
required-Member (C# 11)
Ein Objektinitialisierer kann Aufrufer nicht zwingen, etwas zu setzen: new Product { } kompiliert, selbst wenn Sku nie leer sein darf. C# 11 hat den Modifizierer required hinzugefügt:
public class User
{
public required string Email { get; init; }
public string DisplayName { get; init; } = "";
}
var ok = new User { Email = "ana@example.com" };
var bad = new User { DisplayName = "Ana" }; // error CS9035: Required member 'User.Email'
// must be set in the object initializer or attribute constructor.
required funktioniert mit set und init. Ein Konstruktor, der die Pflicht-Member selbst setzt, kann das mit dem Attribut [SetsRequiredMembers] kennzeichnen, damit Aufrufer, die diesen Konstruktor verwenden, sie nicht erneut setzen müssen.
Häufige Fehler
- Rekursion in einem Accessor.
set { Name = value; }ruft sich selbst auf. Nimm ein Hintergrundfeld oder eine Auto-Property. - Teure Getter oder Getter mit Nebeneffekten. Leser erwarten, dass das Lesen einer Property schnell ist und sich gefahrlos wiederholen lässt. Nimm für Arbeit eine Methode.
- Öffentliche Felder „vorerst“. Ein Feld später in eine Property umzuwandeln bricht die Binärkompatibilität, und Serializer ignorieren Felder standardmäßig. Beginne mit einer Auto-Property.
- Öffentliche Setter überall.
{ get; set; }bei einem Kontostand oder einem Status lädt zu ungültigen Zuständen ein. Nimmprivate set,initoder schreibgeschützt und ändere Zustand über Methoden. - Eine veränderliche Collection aus einer schreibgeschützten Property zurückgeben.
public List<string> Tags { get; }hindert Aufrufer daran, die Liste zu ersetzen, nicht daran, etwas hinzuzufügen. GibIReadOnlyList<string>zurück, wenn sich die Collection von außen nicht ändern soll.
Häufig gestellte Fragen
Was bedeutet { get; set; } in C#?
Es deklariert eine automatisch implementierte Property: Der Compiler erzeugt ein verstecktes privates Feld sowie einen get-Accessor, der es zurückgibt, und einen set-Accessor, der es zuweist. public string Name { get; set; } verhält sich für Aufrufer wie ein öffentliches Feld, ist aber ein Methodenpaar, du kannst also später Validierung ergänzen oder auf private set umstellen, ohne den Code zu ändern, der es verwendet.
Was ist der Unterschied zwischen einem Feld und einer Property in C#?
Ein Feld ist eine im Objekt gespeicherte Variable. Eine Property ist ein Methodenpaar (get und set), das von außen wie ein Feld aussieht. Mit Properties kann eine Klasse Werte validieren, Ergebnisse berechnen oder das Schreiben einschränken, und mit ihnen arbeiten Data Binding, Serializer und Interfaces. Öffentliche Daten sollten als Properties bereitgestellt werden; Felder bleiben meist privat.
Was ist private set in C#?
public int Stock { get; private set; } lässt jeden Code die Property lesen, aber nur Code innerhalb der Klasse sie ändern. So stellt eine Klasse Zustand bereit, der ihr gehört, etwa einen Kontostand oder einen Zähler, und hält alle Änderungen hinter Methoden, die die Regeln durchsetzen.
Was macht init in C#?
init (C# 9) ist ein Setter, der nur während der Erzeugung des Objekts funktioniert: in einem Konstruktor oder einem Objektinitialisierer. public string Sku { get; init; } erlaubt new Product { Sku = "A-1" }, lehnt aber product.Sku = "B-2" danach mit Fehler CS8852 ab. Es gibt unveränderlichen Objekten die Initialisierersyntax.
Was ist eine required-Property in C#?
Eine als required markierte Property (C# 11) muss von jedem Objektinitialisierer gesetzt werden, der den Typ erzeugt; sie wegzulassen ist ein Kompilierfehler (CS9035). Sie lässt sich gut mit init kombinieren: public required string Email { get; init; } muss bei der Erzeugung angegeben werden und kann sich danach nicht mehr ändern.