Sie sind hier: Weblog

Neu in .NET 9.0 [5]: Halbautomatische Properties in C# 13.0

Foto Dr. Holger Schwichtenberg, Dr. Holger Schwichtenberg
17.01.2025 12:00:00

Eine neue Art von Property-Definitionen war schon für C# 11.0 angekündigt. Jetzt ist in .NET 9.0 mit der dreizehnten Version des C#-Compilers das Feature Semi-Auto Properties enthalten, das aber den Status experimentell hat. Daher ist es nur verfügbar, wenn man in einer Projektdatei entweder <EnablePreviewFeatures>True</EnablePreviewFeatures> oder <LangVersion>preview</LangVersion> setzt.

Der Dotnet-Doktor – Holger Schwichtenberg

Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.

Eine halbautomatische Property (Semi-Auto Property) schließt die Lücke zwischen den vollständigen Properties und den automatischen Properties. Ursprünglich gab es in C# nur die vollständigen Properties, bei denen man für den Standardfall des Lesens und Schreibens von Daten einen Getter und einen Setter sowie ein Field als Speicherplatz implementieren musste. Schon in C# 3.0 kamen dann die automatischen Properties, bei denen man für den Fall, dass man im Getter und Setter nichts anderes ausführt, als Daten aus dem Field zu lesen beziehungsweise in das Field zu speichern, auf die Deklaration von Field und die Implementierung von Getter und Setter verzichten konnte. Seit C# 6.0 darf man automatische Properties auch direkt bei der Deklaration initialisieren.

Sobald aber Getter und/oder Setter erweiterte Funktionen wie Validierung oder Benachrichtigung enthalten, kam eine automatische Property nicht mehr infrage. Hier springt nun die neue Semi-Auto Property ein: Man kann Getter und Setter implementieren, muss dabei aber kein Field explizit deklarieren. Stattdessen verwendet man das neue Schlüsselwort field. Der Compiler erzeugt dafür automatisch ein Field als Speicherplatz.

Bei einer halbautomatischen Property gibt es folgende Möglichkeiten:

  • Getter oder Setter dürfen weggelassen werden; man braucht nur einen von beiden.
  • Anstelle von setdarf auch init verwendet werden. Man hat dann eine halbautomatische Init-Only Property.
  • Eine halbautomatische Property kann am Ende mit einem Wert initialisiert werden.

Die Entwicklungsumgebung zeigt im Tooltip an, dass der Compiler an der Stelle des Schlüsselwortes field ein Field anlegen wird.

(Bild: Screenshot (Holger Schwichtenberg))

Die Einführung eines neuen Schlüsselworts in eine Programmiersprache birgt immer die Gefahr einer Namenskollision mit bestehenden Bezeichnern. Wenn Sie in Ihrem Code ein Field mit Namen field haben, müssen Sie stattdessen @field oder this.field schreiben, um dieses bestehende Field weiterhin zu benutzen.

Folgender Code zeigt eine vollständige Property, eine automatische Property und eine halbautomatische Property im Vergleich:

namespace NET9_Console.CS13;
 
/// <summary>
/// Klasse mit Semi-Auto Properties.
/// The feature 'field keyword' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
/// <EnablePreviewFeatures>True</EnablePreviewFeatures> oder <LangVersion>preview</LangVersion>-->
/// </summary>
class PersonWithID
{
 /// <summary>
 /// Auto Property
 /// </summary>
 public string Name { get; set; } = "unbekannt";
 
 private string company = "Freelancer";
 /// <summary>
 /// Full Properties
 /// </summary>
 public string Company
 {
  get => company; set
  {
   if (value == null)
   {
    throw new ArgumentOutOfRangeException();
   }
   company = value;
  }
 }
 
 /// <summary>
 /// Semi-Auto Property
 /// </summary>
 public int ID
 {
  get;
  set // init auch erlaubt!
  {
   if (value < 0) throw new ArgumentOutOfRangeException();
   if (field > 0) throw new ApplicationException("ID schon gesetzt");
   field = value;
  }
 } = -1;
}
 
/// <summary>
/// Client für Semi-Auto Properties
/// </summary>
class CS13_SemiAutoProperties
{
 public void Run()
 {
  CUI.Demo(nameof(CS13_SemiAutoProperties));
 
  PersonWithID p = new PersonWithID();
  Console.WriteLine($"{p.ID}: {p.Name} arbeitet bei {p.Company}");
  p.ID = 42;
  p.Name = "Dr. Holger Schwichtenberg";
  p.Company = "www.IT-Visions.de";
  Console.WriteLine($"{p.ID}: {p.Name} arbeitet bei {p.Company}");
  try
  {
   p.ID = 43;
  }
  catch (Exception ex)
  {
   CUI.PrintError(ex.Message); // "ID schon gesetzt"
  }
 }
}

(rme)