Ich befinde mich in einem klassischen Design-Dilemma. Ich schreibe eine C#-Datenstruktur für einen Wert und Maßeinheit Tupel (z.B. 7,0 Millimeter) und ich frage mich, ob ich einen Referenztyp oder einen Werttyp verwenden sollte.
Die Vorteile einer Struktur sollte es weniger Heap-Aktionen geben, was zu einer besseren Leistung bei Ausdrücken und einer geringeren Belastung des Garbage Collectors führt. Dies wäre normalerweise meine Wahl für einen einfachen Typ wie diesen, aber in diesem konkreten Fall gibt es Nachteile.
Das Tupel ist Teil eines recht allgemeinen Rahmens für Analyseergebnisse, bei dem die Ergebnisse in einer WPF-Anwendung je nach Typ des Ergebniswerts auf unterschiedliche Weise dargestellt werden. Diese Art der schwachen Typisierung wird von WPF mit all seinen Datenvorlagen, Wertkonvertierungen und Vorlagenselektoren außergewöhnlich gut gehandhabt. Die Implikation ist, dass der Wert eine Menge von Boxing / Unboxing durchlaufen wird, wenn mein Tupel als Struktur dargestellt wird. Tatsächlich wird die Verwendung des Tupels in Ausdrücken geringer sein als die Verwendung in Boxing-Szenarien. Um das ganze Boxing zu vermeiden, überlege ich, meinen Typ als Klasse zu deklarieren. Eine weitere Sorge über eine Struktur ist, dass es Fallstricke mit Zwei-Wege-Bindung in WPF sein könnte, da es einfacher wäre, am Ende mit Kopien der Tupel irgendwo im Code statt Referenzkopien.
Ich habe auch einige bequeme Operator-Überladung. Mit überladenen Vergleichsoperatoren kann ich beispielsweise problemlos Millimeter mit Zentimetern vergleichen. Allerdings gefällt mir die Idee nicht, == und != zu überladen, wenn mein Tupel eine Klasse ist, da die Konvention besagt, dass == und != ReferenceEquals für Referenztypen ist (im Gegensatz zu System.String, was eine andere klassische Diskussion ist). Wenn == und != überladen wird, wird jemand schreiben if (myValue == null) und eine unangenehme Laufzeitausnahme erhalten, wenn myValue sich eines Tages als null herausstellt.
Ein weiterer Aspekt ist, dass es in C# (anders als z.B. in C++) keine klare Möglichkeit gibt, Referenz- und Werttypen im Code zu unterscheiden, obwohl die Semantik sehr unterschiedlich ist. Ich befürchte, dass Benutzer meines Tupels (wenn es als struct deklariert ist) davon ausgehen, dass der Typ eine Klasse ist, da die meisten benutzerdefinierten Datenstrukturen dies sind und eine Referenzsemantik annehmen. Das ist ein weiteres Argument, warum man Klassen bevorzugen sollte, einfach weil es das ist, was der Benutzer erwartet und es gibt keine "." / "->", um sie zu unterscheiden. Im Allgemeinen würde ich fast immer eine Klasse verwenden, es sei denn, mein Profiler sagt mir, eine Struktur zu verwenden, einfach weil die Klassensemantik am ehesten von anderen Programmierern erwartet wird und C# nur vage Hinweise gibt, ob es das eine oder das andere ist.
Meine Fragen lauten also:
Welche anderen Überlegungen sollte ich anstellen, wenn ich entscheide, ob ich mich für einen Wert- oder einen Referenzwert entscheiden soll?
Kann == / != Überladung in einer Klasse gerechtfertigt sein in jede Umstände?
Programmierer nehmen Dinge an. Die meisten würden wahrscheinlich davon ausgehen, dass etwas namens "Punkt" ein Wertetyp ist. Was würden Sie annehmen, wenn Sie einen Code mit einem "UnitValue" lesen würden?
Was würden Sie angesichts meiner Nutzungsbeschreibung wählen?