0Pricing
C# Academy · Lekcja

Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6

Proszę modelować sumę rozłączną w C# 6 za pomocą wyliczenia tagów i małych klas, tworzyć metody fabrykujące oraz obsługiwać warianty przez switch z domyślnym rzucaniem wyjątku.

Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6 to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 3 lekcji w sumie.

Idea DU w C# 6

Cel: Zbudować małą unię rozłączną w C# 6.

  • Enum znaczników określający aktywny przypadek
  • Małe klasy przypadków przechowujące dane
  • Metody fabryczne do tworzenia poprawnych przypadków
  • Switch + default throw do obsługi wszystkich wariantów

Znacznik i metody fabryczne

Należy użyć enuma znaczników i metod fabrycznych, aby tworzyć wyłącznie poprawne przypadki; pola dotyczące innych przypadków pozostają niewykorzystane.

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);
  }
}

Obsługa wariantów

Klasyczny switch po znaczniku zapewnia przejrzystą obsługę. default rzucający wyjątek ujawnia nieobsługiwane warianty podczas testów.

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")));
  }
}

Pomocnik Match (w stylu visitora)

Niewielki pomocnik Match centralizuje switch i zwraca wartość, dzięki czemu miejsca wywołań pozostają przejrzyste.

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);
  }
}

Niezmienniki za pośrednictwem fabryk

Fabryki wymuszają zachowanie niezmienników: każdy przypadek inicjalizuje tylko istotne dla siebie pola, a niepoprawne dane wejściowe są odrzucane na wczesnym etapie.

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")));
  }
}

Wskazówki i kompromisy

Wskazówki:

  • Należy preferować małe, skoncentrowane przypadki i fabryki.
  • Warianty należy obsługiwać w jednym miejscu (pomocniku switch), aby zachować zasadę DRY.
  • Należy zachować default throw, aby ujawniać brakujące przypadki w testach.

Kompromisy: Więcej kodu pomocniczego niż w przypadku nowoczesnych rekordów, ale rozwiązanie jest przejrzyste i zgodne z C# 6.

Podejście do emulacji DU

Szybkie sprawdzenie: Jaki jest prosty sposób na emulację unii rozłącznej w C# 6?

Podsumowanie

Podsumowanie: Należy modelować warianty za pomocą enuma znaczników, tworzyć poprawne przypadki za pomocą fabryk i centralizować obsługę w jednym switch (default rzuca wyjątek), aby uzyskać kompletność sprawdzaną w czasie działania.

Często zadawane pytania

Czy lekcja „Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6” jest bezpłatna?

Tak — pełny tekst „Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 3 lekcji w sumie.

Co nauczysz się w „Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6”?

Proszę modelować sumę rozłączną w C# 6 za pomocą wyliczenia tagów i małych klas, tworzyć metody fabrykujące oraz obsługiwać warianty przez switch z domyślnym rzucaniem wyjątku. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 3.

Ile czasu zajmuje lekcja „Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wzorce is, relacyjne i logiczne (emulacja w C# 6)
  2. Wyrażenia switch i sprawdzanie kompletności za pomocą when (emulacja w C# 6)
  3. Modelowanie domeny za pomocą sum rozłącznych (rekordy) — emulacja w C# 6
← Powrót do C# Academy