Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation
Modellieren Sie eine diskriminierte Vereinigung in C# 6 mit einem Tag-Enum und kleinen Klassen, erstellen Sie Factory-Methoden und behandeln Sie Varianten über switch mit einem abschließenden default-Fehler.
Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 3. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 3 Lektionen.
DU-Idee in C# 6
Ziel: Eine kleine Discriminated Union in C# 6 erstellen.
- Tag-Enum für den aktiven Fall
- Kleine Fallklassen für Daten
- Factory-Methoden zum Erstellen gültiger Fälle
- Switch + default throw zur Behandlung aller Varianten
Tag + Factorys
Verwenden Sie ein Tag-Enum und Factory-Methoden, um ausschließlich gültige Fälle zu erstellen; die Felder anderer Fälle bleiben ungenutzt.
using System;
public enum PaymentKind { Cash, Card, Wire }
public sealed class Payment
{
public PaymentKind Kind;
// per-case data (only one is meaningful per instance)
public string CardLast4; // for Card
public string Iban; // for Wire
private Payment() { }
// factories keep invariants
public static Payment Cash()
{
Payment p = new Payment();
p.Kind = PaymentKind.Cash;
return p;
}
public static Payment Card(string last4)
{
if (string.IsNullOrEmpty(last4) || last4.Length != 4)
throw new ArgumentException("last4 must be 4 digits", "last4");
Payment p = new Payment();
p.Kind = PaymentKind.Card;
p.CardLast4 = last4;
return p;
}
public static Payment Wire(string iban)
{
if (string.IsNullOrEmpty(iban))
throw new ArgumentException("iban must not be empty", "iban");
Payment p = new Payment();
p.Kind = PaymentKind.Wire;
p.Iban = iban;
return p;
}
}
public class Program
{
public static void Main(string[] args)
{
Payment a = Payment.Cash();
Payment b = Payment.Card("1234");
Payment c = Payment.Wire("TR00BANKIBAN");
Console.WriteLine(a.Kind + ", " + b.Kind + ", " + c.Kind);
}
}
Varianten behandeln
Ein klassischer switch über das Tag ermöglicht eine klare Behandlung. Ein default throw macht nicht behandelte Varianten während der Tests sichtbar.
using System;
public enum PaymentKind { Cash, Card, Wire }
public sealed class Payment
{
public PaymentKind Kind;
public string CardLast4;
public string Iban;
public static Payment Cash()
{
Payment p = new Payment(); p.Kind = PaymentKind.Cash; return p;
}
public static Payment Card(string last4)
{
if (string.IsNullOrEmpty(last4) || last4.Length != 4) throw new ArgumentException("last4");
Payment p = new Payment(); p.Kind = PaymentKind.Card; p.CardLast4 = last4; return p;
}
public static Payment Wire(string iban)
{
if (string.IsNullOrEmpty(iban)) throw new ArgumentException("iban");
Payment p = new Payment(); p.Kind = PaymentKind.Wire; p.Iban = iban; return p;
}
}
public static class Exhaustive
{
public static Exception Unhandled(object x)
{
return new InvalidOperationException("Unhandled case: " + x);
}
}
public class Program
{
static string Describe(Payment p)
{
switch (p.Kind)
{
case PaymentKind.Cash: return "Cash";
case PaymentKind.Card: return "Card ****" + p.CardLast4;
case PaymentKind.Wire: return "Wire " + p.Iban;
default: throw Exhaustive.Unhandled(p.Kind);
}
}
public static void Main(string[] args)
{
Console.WriteLine(Describe(Payment.Card("9876")));
}
}
Match-Helfer (visitorähnlich)
Ein kleiner Match-Helfer bündelt den switch und gibt einen Wert zurück, sodass die Aufrufstellen übersichtlich bleiben.
using System;
public enum PaymentKind { Cash, Card, Wire }
public sealed class Payment
{
public PaymentKind Kind;
public string CardLast4;
public string Iban;
public static Payment Cash() { Payment p = new Payment(); p.Kind = PaymentKind.Cash; return p; }
public static Payment Card(string last4) { Payment p = new Payment(); p.Kind = PaymentKind.Card; p.CardLast4 = last4; return p; }
public static Payment Wire(string iban) { Payment p = new Payment(); p.Kind = PaymentKind.Wire; p.Iban = iban; return p; }
}
public static class PaymentMatch
{
public static T Match<T>(
Payment p,
Func<T> onCash,
Func<string, T> onCard,
Func<string, T> onWire)
{
switch (p.Kind)
{
case PaymentKind.Cash: return onCash();
case PaymentKind.Card: return onCard(p.CardLast4);
case PaymentKind.Wire: return onWire(p.Iban);
default: throw new InvalidOperationException("Unhandled: " + p.Kind);
}
}
}
public class Program
{
public static void Main(string[] args)
{
Payment p = Payment.Card("1234");
string summary = PaymentMatch.Match(
p,
delegate { return "Cash"; },
delegate(string last4) { return "Card ****" + last4; },
delegate(string iban) { return "Wire " + iban; }
);
Console.WriteLine(summary);
}
}
Invarianten über Factorys
Factorys erzwingen Invarianten: Jeder Fall initialisiert nur seine aussagekräftigen Felder; ungültige Eingaben werden früh abgewiesen.
using System;
public enum ResultKind { Ok, Error }
public sealed class Result
{
public ResultKind Kind;
public string Message; // only for Error
public int Value; // only for Ok
private Result() { }
public static Result Ok(int value)
{
Result r = new Result();
r.Kind = ResultKind.Ok;
r.Value = value;
r.Message = null;
return r;
}
public static Result Error(string message)
{
if (string.IsNullOrEmpty(message)) throw new ArgumentException("message");
Result r = new Result();
r.Kind = ResultKind.Error;
r.Message = message;
r.Value = 0;
return r;
}
}
public class Program
{
static string Show(Result r)
{
switch (r.Kind)
{
case ResultKind.Ok: return "OK: " + r.Value;
case ResultKind.Error: return "ERROR: " + r.Message;
default: throw new InvalidOperationException("Unhandled: " + r.Kind);
}
}
public static void Main(string[] args)
{
Console.WriteLine(Show(Result.Ok(5)));
Console.WriteLine(Show(Result.Error("Boom")));
}
}
Tipps und Abwägungen
Tipps:
- Bevorzugen Sie kleine, fokussierte Fälle und Factorys.
- Behandeln Sie Varianten an einer Stelle (Switch-Helfer), damit der Code DRY bleibt.
- Behalten Sie einen default throw bei, damit fehlende Fälle in Tests sichtbar werden.
Abwägungen: Mehr Boilerplate als bei modernen Records, aber übersichtlich und mit C# 6 kompatibel.
Ansatz zur DU-Nachbildung
Zusammenfassung
Zusammenfassung: Modellieren Sie Varianten mit einem Tag-Enum, erstellen Sie gültige Fälle über Factorys und bündeln Sie die Behandlung in einem switch (default löst eine Ausnahme aus), um die Vollständigkeit zur Laufzeit sicherzustellen.
Häufig gestellte Fragen
Ist die Lektion „Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation“ kostenlos?
Ja — der vollständige Text von „Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 3 Lektionen.
Was lerne ich in „Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation“?
Modellieren Sie eine diskriminierte Vereinigung in C# 6 mit einem Tag-Enum und kleinen Klassen, erstellen Sie Factory-Methoden und behandeln Sie Varianten über switch mit einem abschließenden default… Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um C# Academy zu starten?
Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 3.
Wie lange dauert die Lektion „Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?
Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- is-Muster sowie relationale und logische Muster (C#-6-Emulation)
- switch-Ausdrücke und Vollständigkeitsprüfungen mit when (C#-6-Emulation)
- Domänenmodellierung mit diskriminierten Vereinigungen (Records) – C#-6-Emulation