Sollte ich uint in C # für Werte verwenden, die nicht negativ sein dürfen?

79

Ich habe gerade versucht, eine Klasse zu implementieren, in der uintstattdessen zahlreiche Längen- / Zählungseigenschaften usw. vorhanden sind int. Dabei bemerkte ich jedoch, dass es tatsächlich schmerzhaft ist, dies zu tun, als ob niemand das wirklich tun möchte.

Fast alles, was einen integralen Typ ausgibt int, gibt ein zurück und erfordert daher Abgüsse an mehreren Punkten. Ich wollte ein StringBuffermit einer Pufferlänge erstellen, die standardmäßig auf eines der Felder in dieser Klasse eingestellt ist. Benötigt auch eine Besetzung.

Also fragte ich mich, ob ich einfach inthierher zurückkehren sollte. Ich benutze sowieso nicht das gesamte Sortiment. Ich dachte nur, da das, womit ich mich dort beschäftige, einfach nicht negativ sein kann (wenn es so wäre, wäre es ein Fehler), wäre es eine gute Idee, es tatsächlich zu verwenden uint.

PS: Ich habe diese Frage gesehen und dies erklärt zumindest, warum das Framework selbst immer verwendet, intaber selbst im eigenen Code ist es tatsächlich umständlich, sich daran zu halten, uintwas mich denken lässt, dass es anscheinend nicht wirklich erwünscht ist.

Joey
quelle

Antworten:

42

Während Sie strikt uintfür Variablen verwenden sollten, die eine nicht negative Ganzzahl enthalten, sind Sie auf einen der Gründe gestoßen, warum dies nicht immer praktikabel ist.

In diesem Fall denke ich nicht, dass sich die Verringerung der Lesbarkeit, die mit dem Ausführen von Casts einhergeht, lohnt.

ChrisF
quelle
2
Es ist schwer zu sagen, ich glaube nicht, dass die Casts die Lesbarkeit nur für Indexer stark beeinträchtigen und Sie einen Wrapper erstellen können. Und Ints, die negativ werden, sind schmerzhafte und teure Fehler.
user1496062
6
Wenn Sie einen Wrapper für ein int erstellen müssen, dann haben Sie mehr als Lesbarkeitsprobleme :)
S ..
61

Ich werde zu den anderen Antworten auch hinzufügen, dass die Verwendung von uint als Typ eines öffentlichen Feldes, einer Eigenschaft, einer Methode, eines Parameters usw. einen Verstoß gegen die Regeln für die allgemeine Sprachspezifikation darstellt und nach Möglichkeit vermieden werden sollte.

Eric Lippert
quelle
3
Und ich wurde von Microsoft genau danach verbrannt. Es stellt sich heraus, dass IntPtr einen Uint-Konstruktor haben sollte.
Joshua
3
Ich würde auch hinzufügen, wenn sie natürliche Zahlen unterstützen, z. B. wenn dieser Typ den Wert 0 bis 6 hat, könnten sie alle Array-Grenzen überprüfen, was eine massive Leistungssteigerung von 5-10% bedeutet.
user1496062
Stimmen Sie der obigen Aussage zu, Herr Lippert?
AgentFire
@AgentFire: Die Aussage besagt, dass wenn jemand etwas tun würde, dies zu einer spezifischen Leistungsverbesserung führen würde. Ich bin nicht in der Lage, kontrafaktische Behauptungen zu bewerten. Wenn Sie wissen möchten, ob diese Behauptung wahr ist, fragen Sie user1496062, was ihre Behauptung rechtfertigt, nicht mich. Ich bin nicht derjenige, der die nicht unterstützte Behauptung aufstellt.
Eric Lippert
1
@ EricLippert, das ist eines Ihrer kürzesten längsten "Ich weiß nicht", nicht wahr: p
AgentFire
4

Ein negativer Wert wird häufig verwendet, um einen Fehlerzustand zu signalisieren, und die Größe einer Operation wird häufig durch einen Funktionsaufruf zurückgegeben. Ein negativer Wert kann daher einen Fehler signalisieren, ohne auf einen Ausnahmemechanismus zurückzugreifen.

Beachten Sie auch, dass .NET häufig auf reinen C-Bibliotheken aufbaut. Daher ist es sinnvoll, diese Konvention fortzusetzen. Wenn Sie einen größeren Indexbereich benötigen, können Sie die Konvention für verschiedene Fehlersignalisierungsmechanismen brechen.

Hassan Syed
quelle
24
.NET ist ziemlich konsistent mit dem Auslösen von Ausnahmen, anstatt Fehlercodes zurückzugeben. Die -1-Rückgabe ist im verwalteten Code kein alltäglicher Anblick.
ChrisV
vereinbart, aber wir sprechen über eine Designkonvention, die in allen Windows-Technologien vorherrscht (zusätzlich zu Ausnahmen, bei denen sie unterstützt werden) - diejenigen Teile der .NET-Bibliothek, die mit Code auf niedriger Ebene verbunden sind, müssen zwischen CRT-Fehlercodes und Ausnahmen übersetzen - Es ist irgendwie verständlich, warum sie die Konvention von int und nicht von uint vorbringen.
Hassan Syed
@HassanSyed immer noch, wenn ein Winapi einen negativen Wert zurückgibt, löst das Framework immer noch eine eigene Ausnahme aus und verpackt alles in eine ordentliche Shell, sodass dieses Argument hier nicht zutreffend ist.
AgentFire
3

Mein persönliches Gefühl ist, dass Sie sich wahrscheinlich nur an int halten sollten. Es lohnt sich nicht, so ziemlich jedem einzelnen Eigenschaftszugriff eine Umwandlung hinzuzufügen, nur um einen numerischen Bereich zurückzugewinnen, den Sie in .NET wahrscheinlich sowieso nicht verwenden werden.

ChrisV
quelle
3

Die Verwendung eines int ist auch hilfreich, um einen Ganzzahlüberlauf in Operationen zu erkennen.

Ioan
quelle
Nicht 100% genau, wenn es so übergelaufen ist, dass es Null erreicht. Auch hier ist standardmäßig ein Überlaufschutz aktiviert.
AgentFire
3

IMO, der Nachteil der Verwendung uintist, dass es Fehlerbedingungen verdeckt. Die Entsprechungen des folgenden Codes sind nicht so schön:

if (len < 0)
    terminate_program("length attained impossible value.");

Natürlich sollten Ihre Programme zunächst nichts falsch berechnen, aber ich denke, sie sollten auch geschrieben werden, um numerische Fehler ohne Ausbreitung schnell zu erkennen. In dem Fall, in dem ein MaxValue von 2 ^ 31 ausreicht, sage ich Verwendung intzusammen mit der richtigen Verwendung System.Diagnostics.Debug.Assert()und entsprechenden Fehlerprüfungen, wie oben beispielhaft dargestellt.

Wenn Sie es verwenden, uintverwenden Sie es zusammen mit checked, um einen Unterlauf zu verhindern und die gleichen Ergebnisse zu erzielen. Ich habe jedoch festgestellt, dass die Überprüfung auf vorhandenen Code, der für einen bestimmten Zweck Casts verwendet, etwas schwierig ist.

Heath Hunnicutt
quelle
2

Schwimmen Sie nicht flussaufwärts, wenn Sie nicht müssen. Wenn Sie Ihren Code nicht mit Casts lesen, ist Ihr Code besser lesbar. Wenn Ihre möglichen Werte in ein int passen, ist die Verwendung eines int kein Problem.

Wenn Sie befürchten, dass Sie ein int überlaufen könnten, dann auf jeden Fall ... aber nicht vorzeitig optimieren.

Ich würde sagen, dass die verbesserte Lesbarkeit der Minimierung von Casts das leicht erhöhte Risiko eines Fehlers bei der Verwendung von int überwiegt.

Erik Funkenbusch
quelle
2

Wenn Sie überprüfen möchten, ob ein Wert positiv ist, ist es wahrscheinlich besser, nur assert zu verwenden (beachten Sie, dass dies nur eine Debugging-Technik ist - Sie sollten sicherstellen, dass dies im endgültigen Code niemals vorkommt).

using System.Diagnostics;
...
Debug.Assert (i > 0);
ternaryOperator
quelle
1
Siehe auch die ArgumentOutOfRangeException.
Fred