1 Punkte von GN⁺ 2024-08-09 | 1 Kommentare | Auf WhatsApp teilen

Zusammenfassung

Dies ist ein Dokument, das Type Unions (oder diskriminierte Unions) für C# vorschlägt.

Motivation

  • Bei der Softwareentwicklung müssen in einer Variablen gespeicherte Werte nicht immer vom gleichen Typ sein.
  • Wenn zum Beispiel Kunden- und Lieferantendefinitionen nur einige Eigenschaften gemeinsam haben, kann es nötig sein, ähnliche Operationen auf beide Typen anzuwenden.
  • Das lässt sich zwar über Vererbung lösen, ist aber nicht für jede Situation geeignet.
  • In C# wird eine Möglichkeit benötigt, eine begrenzte Anzahl unterschiedlicher Typen in derselben Variablen zu speichern.
  • Andere Sprachen bieten diese Funktion bereits an.

Lösung

  • Die naheliegendste Art, Union-Typen in C# umzusetzen, ist eine Hierarchie mit abstrakten Basisklassen.
  • Das bringt jedoch Probleme mit sich, etwa Einschränkungen der Hierarchie und die Unmöglichkeit, Unions aus nicht miteinander verwandten Typen darzustellen.
  • Es kann verschiedene Arten von Unions geben, und dieser Vorschlag teilt sie in vier Kategorien ein.

Standard - Union-Klassen

Deklaration

  • Union-Klassen werden ähnlich wie enum deklariert.
  • Jedes Mitglied kann Zustandsvariablen haben.

Erzeugung

  • Sie werden erzeugt, indem eine Instanz eines Mitgliedstyps zugewiesen wird.

Zerlegung

  • Die Zerlegung erfolgt über Typprüfungen und Pattern Matching.

Vollständigkeit

  • Wenn in einem switch-Ausdruck oder einer switch-Anweisung alle Mitgliedstypen berücksichtigt werden, ist kein Standardfall erforderlich.

Nullability

  • null kann mit der üblichen Nullability-Notation eingeschlossen werden.

Implementierung

  • Union-Klassen werden als abstrakte Record-Klassen implementiert.

Spezialisierung - Union-Structs

Deklaration

  • Sie werden ähnlich wie Union-Klassen deklariert, jedoch mit dem zusätzlichen Schlüsselwort struct.

Erzeugung

  • Sie werden erzeugt, indem eine Instanz eines Mitgliedstyps zugewiesen wird.

Zerlegung

  • Die Zerlegung erfolgt über Typprüfungen und Pattern Matching.

Vollständigkeit

  • Wenn in einem switch-Ausdruck oder einer switch-Anweisung alle Mitgliedstypen berücksichtigt werden, ist kein Standardfall erforderlich.

Nullability

  • null kann mit der üblichen Nullability-Notation eingeschlossen werden.

Standardwert

  • Union-Structs können sich in einem undefinierten Zustand befinden, wenn ihnen nichts zugewiesen wurde oder wenn ihnen der Standardwert zugewiesen wurde.

Implementierung

  • Union-Structs werden als Structs implementiert, und die Mitgliedstypen werden als geschachtelte Record-Structs implementiert.

Ad hoc - Ad-hoc-Unions

Syntax

  • Auf Ad-hoc-Unions wird mit der or-Pattern-Syntax verwiesen.

Benennung

  • Mit Datei- oder globalen using-Aliasen kann Ad-hoc-Unions ein gemeinsamer Name gegeben werden.

Erzeugung

  • Sie werden erzeugt, indem eine Instanz eines Mitgliedstyps zugewiesen wird.

Zerlegung

  • Die Zerlegung erfolgt über Typprüfungen und Pattern Matching.

Vollständigkeit

  • Wenn in einem switch-Ausdruck oder einer switch-Anweisung alle Mitgliedstypen berücksichtigt werden, ist kein Standardfall erforderlich.

Nullability

  • null kann mit der üblichen Nullability-Notation eingeschlossen werden.

Austauschbarkeit

  • Ad-hoc-Unions mit denselben Mitgliedstypen sind untereinander austauschbar.

Benutzerdefinierte Unions

  • Es können Union-Typen deklariert werden, die sich nicht als Union-Klasse oder Union-Struct ausdrücken lassen.
  • Mit dem Attribut Closed kann eine Hierarchie geschlossen werden.
  • Mit dem Attribut Union kann eine Implementierung als Struct-Wrapper erfolgen.

Häufige Unions

Option

  • Eine Struct-Union, die einen Wert darstellt, der vorhanden sein kann oder auch nicht.

Result

  • Eine Struct-Union, die aus einer Funktion ein erfolgreiches Ergebnis oder einen Fehler zurückgibt.

Verwandte Vorschläge

Geschlossene Hierarchien

  • Mit dem Attribut Closed wird für einen abstrakten Basistyp eine geschlossene Menge von Untertypen deklariert.

Singleton-Werte

  • Typen mit einem Singleton-Attribut können in Nicht-Typ-Kontexten als Werte verwendet werden.

Kurzschreibweise für geschachtelte Member

  • Mit ungebundenen Namen kann auf statische Member oder geschachtelte Typen eines Zieltyps gebunden werden.

GN⁺-Zusammenfassung

  • Dieses Dokument schlägt Type Unions für C# vor und bietet eine Möglichkeit, in verschiedenen Situationen mehrere Typen in einer Variablen zu speichern.
  • Es ist ein Versuch, eine Funktion in C# einzuführen, die andere Sprachen bereits bieten.
  • Entwicklerinnen und Entwickler könnten damit die Lesbarkeit und Wartbarkeit von Code verbessern.
  • Ein Beispiel für eine andere Sprache mit ähnlicher Funktionalität ist F#.

1 Kommentare

 
GN⁺ 2024-08-09
Hacker-News-Kommentare
  • Ich habe in F# bereits diskriminierte Unions verwendet und dachte, C# hätte sie ebenfalls

    • Ich nutze Java und finde es schwierig, wieder in einer Sprache ohne ADTs zu arbeiten
    • Ich freue mich, dass ich mich nicht mehr dafür entschuldigen muss, dass C# eine wichtige Funktion fehlt
  • Der Begriff "Type Union" ist mir ungewohnt

    • Es scheint ähnlich wie Tagged Unions aus Sprachen der ML-Familie zu sein
    • Ich frage mich, ob C#-Entwickler dazu neigen, für bestehende Begriffe andere Namen zu erfinden
  • Als langjähriger C#-Entwickler finde ich den Anwendungsfall dieses Vorschlags nicht klar

    • Es wirkt so, als könnte man das mit einem leeren Interface und Record-Klassen umsetzen
    • Ich frage mich, ob ich etwas übersehe
  • TypeScript hat Type Unions

    • Sie scheinen den diskriminierten Unions in F# oder Haskell ähnlich zu sein
    • Diskriminierte Unions haben benannte Case-Konstruktoren
  • Ohne Unions mit Pattern Matching wird Programmierung schwieriger

    • Ich habe die Bedeutung des Expression Problems nie vollständig verstanden
    • Vorhandene Polymorphie als Erweiterungspunkt könnte für zukünftige Clients geeignet sein
    • Für Code, den ein Team besitzt, sind Unions mit Pattern Matching besser geeignet
  • Ich habe Erfahrung mit Feld-Offsets in C#-Unions

    • Aliasbildung zwischen Pointer-/Referenzwerten und Werten kann zu undefiniertem Verhalten führen
    • Eine Struct-Union aus u64 und Objekt kann ein separates Feld erfordern und dadurch 8 Byte verschwenden
  • Mit privaten Konstruktoren und einem NuGet-Paket kann man dafür sorgen, dass ein Switch-Typ keinen _-Case benötigt

    • Das ähnelt einer desugarten Version der vorgeschlagenen "Union-Klasse"
    • Es ist gut, dass das NuGet-Paket überflüssig wird und syntaktischer Zucker hinzukommt
  • Es wird nicht erwähnt, wie Union-Structs Tearing bei gleichzeitigen Änderungen behandeln

    • Tearing kann Probleme mit der Speichersicherheit verursachen
    • Es könnte Varianten mit einem Integer-Feld und einem Referenzfeld am selben Offset geben