1 points par GN⁺ 2024-08-09 | 1 commentaires | Partager sur WhatsApp

Résumé

Il s’agit d’un document proposant les unions de types (ou unions discriminées) en C#.

Motivation

  • Lors du développement logiciel, les valeurs stockées dans une variable ne sont pas toujours du même genre.
  • Par exemple, lorsque les définitions d’un client et d’un fournisseur ne partagent qu’une partie de leurs propriétés, il peut être nécessaire d’effectuer des opérations similaires sur ces deux types.
  • L’héritage peut résoudre le problème, mais ne convient pas à toutes les situations.
  • Il est nécessaire, en C#, de disposer d’un moyen de stocker un nombre limité de types différents dans une même variable.
  • D’autres langages offrent déjà cette fonctionnalité.

Solution

  • La manière la plus appropriée d’implémenter les unions de types en C# peut être envisagée comme une hiérarchie reposant sur une classe de base abstraite.
  • Cependant, cette approche présente des problèmes, comme les contraintes de hiérarchie ou l’impossibilité de représenter une union de types sans lien entre eux.
  • Plusieurs sortes d’unions peuvent être nécessaires, et cette proposition les classe en quatre catégories.

Standard - classe union

Déclaration

  • Une classe union se déclare de manière similaire à un enum.
  • Chaque membre peut avoir des variables d’état.

Création

  • Elle est créée en assignant une instance d’un type membre.

Déconstruction

  • Elle se décompose via des tests de type et le pattern matching.

Exhaustivité

  • Si tous les types membres sont pris en compte dans une expression ou une instruction switch, un cas par défaut n’est pas nécessaire.

Nullabilité

  • Il est possible d’inclure null en utilisant la notation standard de nullabilité.

Implémentation

  • Une classe union est implémentée comme une classe record abstraite.

Spécialisation - struct union

Déclaration

  • Elle se déclare de manière similaire à une classe union, mais avec le mot-clé struct en plus.

Création

  • Elle est créée en assignant une instance d’un type membre.

Déconstruction

  • Elle se décompose via des tests de type et le pattern matching.

Exhaustivité

  • Si tous les types membres sont pris en compte dans une expression ou une instruction switch, un cas par défaut n’est pas nécessaire.

Nullabilité

  • Il est possible d’inclure null en utilisant la notation standard de nullabilité.

Valeur par défaut

  • Une struct union peut être dans un état non défini lorsqu’elle n’est pas assignée ou qu’elle reçoit sa valeur par défaut.

Implémentation

  • Une struct union est implémentée comme une structure, et les types membres sont implémentés comme des record structs imbriqués.

Temporaire - union ad hoc

Syntaxe

  • Une union ad hoc est référencée à l’aide de la syntaxe de pattern or.

Nommage

  • Il est possible de donner un nom commun à une union ad hoc à l’aide d’un alias using de fichier ou global.

Création

  • Elle est créée en assignant une instance d’un type membre.

Déconstruction

  • Elle se décompose via des tests de type et le pattern matching.

Exhaustivité

  • Si tous les types membres sont pris en compte dans une expression ou une instruction switch, un cas par défaut n’est pas nécessaire.

Nullabilité

  • Il est possible d’inclure null en utilisant la notation standard de nullabilité.

Interchangeabilité

  • Les unions ad hoc ayant les mêmes types membres sont interchangeables.

Unions définies par l’utilisateur

  • Il est possible de déclarer des types union qui ne peuvent pas être exprimés comme classe union ou struct union.
  • L’attribut Closed permet de fermer une hiérarchie.
  • L’attribut Union permet une implémentation sous forme de wrapper struct.

Unions courantes

Option

  • Il s’agit d’une union sous forme de structure qui représente une valeur pouvant être présente ou absente.

Result

  • Il s’agit d’une union sous forme de structure permettant à une fonction de renvoyer soit un résultat réussi, soit une erreur.

Propositions associées

Hiérarchie fermée

  • L’attribut Closed permet de déclarer, pour un type de base abstrait, un ensemble fermé de sous-types.

Valeurs singleton

  • Les types dotés d’un attribut singleton peuvent être utilisés comme valeurs dans un contexte non typé.

Raccourci pour les membres imbriqués

  • Il est possible d’utiliser des noms non liés pour les lier aux membres statiques ou aux types imbriqués du type cible.

Le récapitulatif de GN⁺

  • Ce document propose les unions de types en C# et fournit un moyen de stocker plusieurs types dans une variable selon différents contextes.
  • Il s’agit d’une tentative d’introduire en C# une fonctionnalité déjà présente dans d’autres langages.
  • Cela peut aider les développeurs à améliorer la lisibilité et la maintenabilité du code.
  • Parmi les autres langages offrant une fonctionnalité similaire, on peut citer F#.

1 commentaires

 
GN⁺ 2024-08-09
Avis Hacker News
  • J’utilise des unions discriminées en F# depuis longtemps, et je pensais que C# en avait aussi

    • J’utilise Java et je trouve difficile de revenir à un langage sans ADT
    • Ravi de ne plus avoir à m’excuser de l’absence de cette fonctionnalité majeure en C#
  • Le terme « union de types » m’est peu familier

    • Cela ressemble à une union taguée des langages de la famille ML
    • Je me demande si les développeurs C# ont tendance à créer des noms différents de la terminologie existante
  • En tant que développeur C# de longue date, je trouve que le cas d’usage de cette proposition n’est pas clair

    • On dirait qu’on peut l’implémenter en déclarant une interface vide et des classes record
    • Je me demande ce que je rate
  • TypeScript a des unions de types

    • Cela semble proche des unions discriminées de F# ou Haskell
    • Les unions discriminées ont des constructeurs de cas nommés
  • Programmer sans unions compatibles avec le pattern matching devient difficile

    • Je n’ai jamais complètement compris la signification du problème de l’expression
    • Les points d’extension fournis par le polymorphisme existant peuvent convenir à de futurs clients
    • Pour du code possédé par l’équipe, des unions compatibles avec le pattern matching sont plus adaptées
  • J’ai déjà utilisé des unions C# avec des offsets de champ

    • L’aliasing entre valeurs pointeur/référence et valeurs peut provoquer un comportement indéfini
    • Une union de struct entre un u64 et un objet peut nécessiter un champ séparé, ce qui peut gaspiller 8 octets
  • Avec des constructeurs privés et un package nuget, on peut faire en sorte qu’un type switch n’exige pas de cas _

    • C’est similaire à la version désucrée de la « classe union » proposée
    • C’est bien de supprimer le besoin du package nuget et d’ajouter du sucre syntaxique
  • La proposition ne mentionne pas comment une struct union gère le tearing lors de modifications concurrentes

    • Le tearing peut entraîner des problèmes de sûreté mémoire
    • Il peut exister des variantes ayant, au même offset, un champ entier et un champ référence