Ich habe darüber nachgedacht, benutzerdefinierte Typen für Bezeichner wie diesen zu erstellen:
public enum CustomerId : int { /* intentionally empty */ }
public enum OrderId : int { }
public enum ProductId : int { }
Meine Hauptmotivation dafür ist es, den Fehler zu vermeiden, bei dem Sie eine orderItemId versehentlich an eine Funktion übergeben, die eine orderItemDetailId erwartet hat.
Anscheinend funktionieren Aufzählungen nahtlos mit allem, was ich in einer typischen .NET-Webanwendung verwenden möchte:
- MVC-Routing funktioniert einwandfrei
- Die JSON-Serialisierung funktioniert einwandfrei
- Jeder ORM, den ich mir vorstellen kann, funktioniert einwandfrei
Jetzt frage ich mich also: "Warum sollte ich das nicht tun?" Dies sind die einzigen Nachteile, die mir einfallen:
- Dies kann andere Entwickler verwirren
- Es führt zu Inkonsistenzen in Ihrem System, wenn Sie nicht-integrale Bezeichner haben.
- Es kann zusätzliches Casting erfordern, wie z
(CustomerId)42
. Ich denke aber nicht, dass dies ein Problem sein wird, da das ORM- und MVC-Routing Ihnen normalerweise Werte des Aufzählungstyps direkt übergibt.
Meine Frage ist also, was fehle ich? Das ist wahrscheinlich eine schlechte Idee, aber warum?
Antworten:
Es ist eine großartige Idee, einen Typ für jede Art von Kennung zu erstellen , hauptsächlich aus den von Ihnen angegebenen Gründen. Ich würde das nicht tun, indem ich den Typ als enum implementiere, weil es Ihnen mehr Optionen gibt, wenn Sie ihn zu einer richtigen Struktur oder Klasse machen:
Informationen zur Implementierung finden Sie im Artikel Functional C #: Primitive Obsession .
quelle
Es ist ein kluger Hack, aber dennoch ein Hack, der ein Feature für etwas missbraucht, für das es nicht der beabsichtigte Zweck ist. Hacks haben Kosten in Bezug auf die Wartbarkeit, daher ist es nicht genug, dass Sie keine anderen Nachteile finden. Sie müssen auch erhebliche Vorteile im Vergleich zu der idiomatischeren Methode zur Lösung desselben Problems haben.
Der idiomatische Weg wäre, einen Wrapper-Typ oder eine Wrapper-Struktur mit einem einzigen Feld zu erstellen. Dies gibt Ihnen die gleiche Art von Sicherheit in einer expliziten und idiomatischen Art und Weise. Obwohl Ihr Vorschlag zugegebenermaßen clever ist, sehe ich keine wesentlichen Vorteile bei der Verwendung von Wrapper-Typen.
quelle
Aus meiner Sicht ist die Idee gut. Die Absicht, nicht verwandten Klassen von Bezeichnern unterschiedliche Typen zuzuweisen und ansonsten die Semantik über Typen auszudrücken und vom Compiler überprüfen zu lassen, ist gut.
Ich weiß nicht, wie es in Bezug auf die Leistung in .NET funktioniert, z. B. sind die Enum-Werte unbedingt in Kästchen angegeben. OTOH-Code, der mit einer Datenbank funktioniert, ist wahrscheinlich ohnehin E / A-gebunden, und Boxed Identifiers stellen keinen Engpass dar.
In einer perfekten Welt wären Identifikatoren unveränderlich und nur für die Gleichheit vergleichbar. tatsächliche Bytes wären ein Implementierungsdetail. Nicht verwandte Bezeichner hätten inkompatible Typen, sodass Sie nicht versehentlich einen für einen anderen verwenden könnten. Ihre Lösung passt praktisch zur Rechnung.
quelle
Sie können dies nicht tun, Aufzählungen müssen vordefiniert sein. Wenn Sie also nicht bereit sind, das gesamte Spektrum der möglichen Werte von Kunden-IDs vorab zu definieren, ist Ihr Typ unbrauchbar.
Wenn Sie eine Besetzung vornehmen, widersprechen Sie Ihrem primären Ziel, die versehentliche Zuweisung einer Typ-A-ID zu einer Typ-B-ID zu verhindern. Daher sind Aufzählungen für Ihren Zweck nicht hilfreich.
Dies wirft jedoch die Frage auf, ob es hilfreich wäre, Ihre Typen für Kunden-ID, Bestell-ID und Produkt-ID zu erstellen, indem Sie von Int32 (oder Guid) abstammen. Ich kann mir Gründe vorstellen, warum dies falsch wäre.
Der Zweck einer ID ist die Identifizierung. Integrale Typen sind dafür bereits perfekt, sie werden durch ihre Abstammung nicht mehr identifizierbar. Es ist keine Spezialisierung erforderlich oder erwünscht. Ihre Welt wird nicht besser dargestellt, wenn Sie unterschiedliche Typen für Bezeichner verwenden. Aus OO-Sicht wäre es also schlecht.
Mit Ihrem Vorschlag möchten Sie mehr als Typensicherheit, Sie möchten Wertesicherheit und das ist jenseits der Modellierungsdomäne ein operatives Problem.
quelle
public ActionResult GetCustomer(CustomerId custId)
kein Casting benötigt wird - das MVC-Framework liefert den Wert.