Primärkonstruktor
Eintrag zuletzt aktualisiert am: 27.04.2023
Die wesentliche, schon seit
.NET 8.0 Preview 2 implementierte Neuerung in
C# 12.0 sind die Primärkonstruktoren für Klassen. Alte Hasen unter den C#-Entwicklern werden sich erinnern, dass dieses Sprachfeature bereits im Jahr 2014 als
Prototyp für
C# 6.0 verfügbar war, dann aber doch gestrichen wurde.
Nun, sechs C#-Versionen weiter, kommt Microsoft in
C# 12.0 darauf zurück, auch vor dem Hintergrund der Record-Typen, die es seit
C# 9.0 mit Primärkonstruktoren gibt:
public record Person(int ID, string Name, string Website = "");
Ein Primärkonstruktor ist eine Parameterliste direkt hinter dem
Typnamen. In
C# 12.0 ist das auch für Klassendefinitionen möglich:
public class Person(int ID, string Name, string Website = "");
Solch eine Klasse kann ohne Inhaltsbereich (also geschweifte Klammern) existieren, ist aber wertlos. Anders als bei den in
C# 9.0 eingeführten Record-Typen erstellt der Primärkonstruktor -nämlich keine öffentlichen Properties in der Klasse, sondern nur private
Fields. Wenn man diese Klasse mit Primärkonstruktor in einem
Decompiler betrachtet, sieht man zunächst überhaupt keine Verarbeitung der Parameter im Primärkonstruktor:
public class Person
{
public Person(int ID, string Name, string Website = "")
{
}
}
Das liegt daran, dass die Primärkonstruktorparameter gar nicht verwendet werden. Wir müssen die Klasse z.B. um ToString() erweitern, siehe Listing 1. Nun sehen wir im
Decompiler, dass für jeden Primärkonstruktorparameter ein privates
Field angelegt wurde inklusive Zuweisung im Konstruktor (Listing 2).
Listing 1: Klasse mit Primärkonstruktor und
Methode ToString(), in der alle Primärkonstruktor verwendet werden
public class Person(int ID, string Name, string Website = "")
{
public override string ToString()
{
return $"Person #{ID}: {Name} -> {Website}";
}
}
Listing 2: Decompilat von Listing 1 mit ILSpy 8.0.0.7246-preview3
public class Person
{
[CompilerGenerated]
[
DebuggerBrowsable(
DebuggerBrowsableState.Never)]
private int <ID>PC
Backing
Field;
[CompilerGenerated]
[
DebuggerBrowsable(
DebuggerBrowsableState.Never)]
private string <Name>PC
Backing
Field;
[CompilerGenerated]
[
DebuggerBrowsable(
DebuggerBrowsableState.Never)]
private string <Website>PC
Backing
Field;
public Person(int ID, string Name, string Website = "unbekannt")
{
<ID>PC
Backing
Field = ID;
<Name>PC
Backing
Field = Name;
<Website>PC
Backing
Field = Website;
base..ctor();
}
public override string ToString()
{
return $"Person #{<ID>PC
_BackingField}: {<Name>PC__BackingField} -> {<Website>PC_Backing
Field}";
}
}
Um öffentlich auf die im Primärkonstruktor übergebenen Daten zugreifen zu können, muss man die Konstruktorparameter für Zuweisungen verwenden, siehe Name und Website im Listing 3. In Listing 3 gibt es neben der Klasse Person eine zweite, abgeleitete Klasse Autor mit Primärkonstruktor.
Listing 3: Primärkonstruktorbeispiel mit Zuweisung der Primärkonstruktorparameter an öffentliche Properties und
Vererbung
using
System.ComponentModel.Data
Annotations;
using ITVisions;
namespace NET8Konsole.CS12;
/// <summary>
/// Klasse mit Primärkonstruktor
/// </summary>
public class Person(Guid personGuid, string name)
{
public Guid PersonGuid { get; set; } = personGuid;
public string Name { get; set; } = name;
public Person() : this(Guid.Empty, "")
{
// Aufruf ohne Parameter möglich, führt aber zu einem ungültigen
Objekt ;-)
}
public override string ToString()
{
return $"Person {personGuid}: {Name}";
}
}
/// <summary>
/// Abgeleitete Klasse mit Primärkonstruktor
/// </summary>
public class Autor(Guid personGuid, string name, string website) : Person(personGuid, name)
{
public string Website { get; set; } = website;
public override string ToString()
{
return $"Autor {personGuid}: {Name} -> {Website}";
}
}
internal class CS12
PrimaryConstructorsDemo
{
public void Run()
{
var p = new Person();
Console.WriteLine(p.Name);
Console.WriteLine(p.ToString());
var a = new Autor(Guid.NewGuid(), "
Dr. Holger Schwichtenberg", "www.IT-Visions.de");
Console.WriteLine(a.Name);
Console.WriteLine(a.Website);
Console.WriteLine(a.ToString());
}
}