CodeGym /Kurse /C# SELF /required Properties un...

required Properties und field Properties

C# SELF
Level 17 , Lektion 3
Verfügbar

1. Einführung

C# ohne Properties zu programmieren ist möglich, aber das ist ungefähr so, als würdest du mit Rollschuhen durchs Weiße Haus fahren – geht, macht aber keinen Spaß. Wir sind inzwischen an den kurzen Syntax ohne unnötige Getter und Setter gewöhnt, und wenn unsere App mit „ernsthaften“ Modellen arbeitet (zum Beispiel einer User-Klasse, die einen Benutzer im System beschreibt), ist es wichtig, dass alle nötigen Daten wirklich vorhanden sind.

Zum Beispiel, wenn wir eine Klasse haben:

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Man kann leicht vergessen, die Properties zu initialisieren:

User user = new User(); // Name wird null sein, Age = 0

Irgendwo nach zehn Bildschirmen und hundert Logik-Abzweigungen bekommst du dann die berühmte NullReferenceException und suchst ewig nach dem Schuldigen.

Erinnern wir uns an init-only Properties

Klar, wir können Initialisierung nur beim Erstellen erzwingen:

public string Name { get; init; }

Aber selbst mit diesem Syntax zwingt dich niemand, beim Erstellen einen Wert zu übergeben – die meisten Standardkonstruktoren initialisieren Felder mit Default-Werten (null, 0 usw.).

Wie zwingt man also sich selbst oder andere, einen Wert wirklich zu setzen? Genau dafür wurde der Modifier required erfunden, der in C# 11 eingeführt wurde.

2. required Properties: Strikte Initialisierung

Der Modifier required sagt dem Compiler, dass eine bestimmte Property beim Erstellen des Objekts explizit gesetzt werden muss. Kurz gesagt: Wenn so eine Property nicht initialisiert wird, lässt dich der Compiler nicht durch und die IDE zeigt dir eine fette rote Welle.


public class User
{
    public required string Name { get; set; }
    public int Age { get; set; }
}
Verpflichtende Property mit required

Versuchen wir, einen User zu erstellen:

// Compilerfehler: Property Name muss initialisiert werden!
User user1 = new User();

Oder so:


// Compilerfehler: required-Property Name nicht gesetzt
User user2 = new User { Age = 18 };

Und so ist es korrekt:

User user3 = new User { Name = "Hermine", Age = 18 };

Wie funktioniert required?

Der Modifier required sagt dem Compiler: „Nach Abschluss des Konstruktors (egal welchem!) muss diese Property explizit gesetzt sein“.

Das funktioniert sowohl für normale Properties als auch für init-only:

public required string Name { get; init; }

Wenn du einen eigenen Konstruktor schreibst, der das required-Property initialisiert, ist alles gut.

Typisches Prüfschema

Szenario Compiler zufrieden?
Required-Property nicht gesetzt
Im Objekt-Initializer gesetzt ✔️
Im Konstruktor gesetzt ✔️

Beispiel in unserem „Hunde“-Modell

public class Dog
{
    public required string Name { get; set; }
    public int Age { get; set; }
}

Dog dog = new Dog { Name = "Bobik", Age = 5 }; // Alles ok!
Dog badDog = new Dog { Age = 2 }; // Fehler! Name nicht gesetzt

Wo hilft required wirklich?

  • DTO-Übergabe zwischen Schichten: Wenn du ein API hast, müssen immer alle nötigen Felder mitgegeben werden.
  • Komplexe Modelle mit Pflichtattributen: Zum Beispiel Product mit verpflichtendem SKU, Order mit verpflichtender Nummer.
  • Im Vorstellungsgespräch und Code-Review: Wenn du so einen Syntax zeigst, wird dein Code wahrscheinlich mit Respekt (und ein bisschen Neid) gelesen.

3. Wie funktioniert required mit Konstruktoren?

Manchmal schreibst du einen eigenen Konstruktor. Was passiert, wenn du das required-Property nicht im Konstruktor oder Objekt-Initializer initialisierst? Der Compiler gibt einen Fehler aus.


public class Article
{
    public required string Title { get; set; }
    public required string Author { get; set; }

    public Article()
    {
        // Wenn Title und Author nicht initialisiert werden – Compilerfehler!
        // So geht's:
        Title = "Ohne Titel";
        Author = "Unbekannt";
    }
}

Wenn der Konstruktor selbst Werte für required-Properties setzt – alles super. Wenn nicht, musst du die Felder über den Objekt-Initializer (new Article { ... }) initialisieren.

Spezifika der Verwendung

  • required funktioniert nur mit Properties, nicht mit Feldern.
  • required wird nicht vererbt – wenn die Basisklasse required hat, aber die abgeleitete Klasse nicht, meckert der Compiler nicht (aber es ist gute Praxis, required explizit im Nachfolger zu wiederholen).
  • required kann nicht auf automatische Felder oder irgendwas anderes außer Properties angewendet werden.

4. Arrow-Syntax für Properties

Mit den neuen C#-Versionen wollen Entwickler immer öfter kompakten und ausdrucksstarken Code schreiben. Eines der auffälligsten Features für Properties ist die Arrow-Syntax (oder expression-bodied properties).

Manchmal willst du eine Property definieren, die einfach nur einen Wert zurückgibt, ohne extra Logik. Früher musstest du dafür einen kompletten Getter mit geschweiften Klammern schreiben:

public int Age
{
    get { return birthYear > 0 ? DateTime.Now.Year - birthYear : 0; }
}

Jetzt geht das viel kürzer – mit dem Pfeil (=>):


public int Age => birthYear > 0 ? DateTime.Now.Year - birthYear : 0;

Das nennt sich expression-bodied property. Perfekt für einfache Berechnungen und macht den Code kompakter.

Beispiel für get und set

public class Book
{
    private string _title;

    public string Title
    {
        get => _title;
        set => _title = value.Trim();
    }
}

Hier gibt get den Feldwert zurück, und set weist zu, nachdem überflüssige Leerzeichen entfernt wurden.

Beispiel nur mit get (read-only):

Wenn die Property nur gelesen werden soll – kannst du sie komplett ohne geschweifte Klammern einfach mit => schreiben.

public class Person
{
    private string name = "Mark Twain";

    // Read-only: berechnete Property
    public string Name => name.ToUpper();
}

Hier kann die Property Name nur gelesen werden – sie gibt immer name in Großbuchstaben zurück.

5. Das Keyword field für Properties

Vor C# 14 konntest du nicht direkt auf das versteckte Feld einer Auto-Property im Setter oder Getter zugreifen (zum Beispiel, um Rekursion zu vermeiden oder eigene Logik einzubauen). Du musstest das Feld explizit deklarieren.

C# 14 erlaubt den Zugriff auf das versteckte Feld einer Auto-Property mit dem Keyword field:


public class Person
{
    public string Name
    {
        get => field; // field – das versteckte Feld der Property Name
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Name darf nicht leer sein!");
            field = value; // field statt explizitem _name
        }
    }
}

Früher musste man das so machen:

private string _name;
public string Name
{
    get => _name;
    set
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Name darf nicht leer sein!");
        _name = value;
    }
}

Eine Variablendeklaration weniger. Je kompakter der Code, desto besser.

Warum ist es manchmal wichtig, im Property auf das Feld zuzugreifen?

  • Manchmal willst du explizit kontrollieren, wo und wie der Wert gespeichert wird (z.B. wenn du eine Kopie zurückgeben, cachen oder lazy-loading machen willst).
  • Wenn du Attribute oder Reflection nutzt – manchmal brauchst du das Feld beim Namen.
  • In manchen (De-)Serialisierungs- oder Performance-Tuning-Fällen willst du mehr Kontrolle über die Speicherung.

Verwendungsbeispiele:

Datenvalidierung im Setter

public double Grade
{
    get => field;
    set
    {
        if (value < 0 || value > 5)
            throw new ArgumentOutOfRangeException("Note muss zwischen 0 und 5 liegen");
        field = value;
    }
}

Statistik bei Wertänderung

public int StepCount
{
    get => field;
    set
    {
        if (value > field)
        {
            Console.WriteLine($"Yeah! Du hast {value - field} Schritte mehr gemacht!");
        }
        field = value;
    }
}

Lazy Load

public string Data
{
    get
    {
        if (field == null)
            field = LoadDataFromDatabase();
        return field;
    }
    set => field = value;
}

6. Typische Fehler und Besonderheiten

Fehler Nr. 1: Required-Property vergessen zu initialisieren.
Der Compiler lässt dich mit so einem Code nicht durch und gibt sofort beim Build einen Fehler aus – das hilft, Probleme zur Laufzeit zu vermeiden.

Fehler Nr. 2: Required-Properties funktionieren nicht ohne vollständige Initialisierung im Konstruktor.
Wenn der Konstruktor keine Werte für alle Pflicht-Properties annimmt, erinnert dich der Compiler daran, dass du was vergessen hast.

Fehler Nr. 3: Versuch, required mit const oder readonly zu kombinieren.
Diese Modifier sind nicht kompatibel – required geht nur mit normalen Properties. Der Versuch, sie zu kombinieren, führt zu einem Fehler.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION