J'ai pensé à créer des types personnalisés pour des identifiants comme celui-ci:
public enum CustomerId : int { /* intentionally empty */ }
public enum OrderId : int { }
public enum ProductId : int { }
Ma principale motivation pour cela est d'empêcher le type de bogue où vous passez accidentellement un orderItemId à une fonction qui attendait un orderItemDetailId.
Il semble que les énumérations fonctionnent parfaitement avec tout ce que je voudrais utiliser dans une application Web .NET typique:
- Le routage MVC fonctionne bien
- La sérialisation JSON fonctionne bien
- Chaque ORM auquel je peux penser fonctionne bien
Alors maintenant je me demande, "pourquoi ne devrais-je pas faire ça?" Ce sont les seuls inconvénients auxquels je peux penser:
- Cela peut confondre d'autres développeurs
- Il introduit une incohérence dans votre système si vous avez des identifiants non intégraux.
- Cela peut nécessiter un casting supplémentaire, comme
(CustomerId)42
. Mais je ne pense pas que ce sera un problème, car le routage ORM et MVC vous fournira généralement des valeurs du type enum directement.
Alors ma question est, qu'est-ce que je manque? C'est probablement une mauvaise idée, mais pourquoi?
Réponses:
C'est une excellente idée de créer un type pour chaque type d'identifiant, principalement pour les raisons que vous avez indiquées. Je ne ferais pas cela en implémentant le type enum, car en faire une structure ou une classe appropriée vous donne plus d'options:
Vous pouvez lire sur l'implémentation de cela dans l'article Functional C #: Obsession primitive .
la source
C'est un hack intelligent, mais toujours un hack qui abuse d'une fonctionnalité pour quelque chose qui n'est pas le but recherché. Les hacks ont un coût de maintenabilité, il ne suffit donc pas de trouver d'autres inconvénients, il doit également avoir des avantages significatifs par rapport à la manière la plus idiomatique de résoudre le même problème.
La manière idiomatique serait de créer un type ou une structure de wrapper avec un seul champ. Cela vous donnera le même type de sécurité d'une manière plus explicite et idiomatique. Bien que votre proposition soit certes intelligente, je ne vois aucun avantage significatif à utiliser des types de wrapper.
la source
De mon POV, l'idée est bonne. L'intention de donner différents types à des classes d'identificateurs sans rapport, et d'exprimer autrement la sémantique via des types et de laisser le compilateur le vérifier, est bonne.
Je ne sais pas comment cela fonctionne en termes de performances .NET, par exemple les valeurs enum sont-elles nécessairement encadrées. Le code OTOH qui fonctionne avec une base de données est probablement lié aux E / S de toute façon et les identifiants encadrés ne seront pas un goulot d'étranglement.
Dans un monde parfait, les identifiants seraient immuables et ne seraient comparables que pour l'égalité; les octets réels seraient un détail d'implémentation. Les identifiants indépendants auraient des types incompatibles, vous ne pourriez donc pas les utiliser l'un par l'autre par erreur. Votre solution correspond pratiquement à la facture.
la source
Vous ne pouvez pas faire cela, les énumérations doivent être prédéfinies. Donc, à moins que vous ne vouliez prédéfinir tout le spectre des valeurs possibles des identifiants client, votre type sera inutile.
Si vous lancez, vous défieriez votre objectif principal d'empêcher d'assigner accidentellement un identifiant de type A à un identifiant de type B. Les énumérations ne sont donc pas utiles à votre objectif.
Cela soulève cependant la question de savoir s'il serait utile de créer vos types pour l'ID client, l'ID de commande et l'ID de produit en descendant d'Int32 (ou Guid). Je peux penser à des raisons pour lesquelles ce serait faux.
Le but d'un identifiant est l'identification. Les types intégraux sont déjà parfaits pour cela, ils ne s'identifient plus en descendant d'eux. Aucune spécialisation n'est requise ni souhaitée, votre monde ne sera pas mieux représenté en ayant différents types d'identifiants. Du point de vue OO, ce serait mauvais.
Avec votre suggestion, vous voulez plus que la sécurité du type, vous voulez la sécurité de la valeur et cela dépasse le domaine de la modélisation, c'est un problème opérationnel.
la source
public ActionResult GetCustomer(CustomerId custId)
aucune conversion n'est nécessaire - le framework MVC fournira la valeur.