Ich habe kürzlich über die Verwendung von Ganzzahlen ohne Vorzeichen in C # nachgedacht (und ich denke, ein ähnliches Argument kann für andere "Hochsprachen" angeführt werden).
Wenn ich eine Ganzzahl benötige, bin ich normalerweise nicht mit dem Dilemma der Größe einer Ganzzahl konfrontiert. Ein Beispiel wäre eine Alterseigenschaft einer Personenklasse (aber die Frage ist nicht auf Eigenschaften beschränkt). In diesem Sinne gibt es meines Erachtens nur einen Vorteil der Verwendung einer Ganzzahl ohne Vorzeichen ("uint") gegenüber einer Ganzzahl mit Vorzeichen ("int") - die Lesbarkeit. Wenn ich die Idee zum Ausdruck bringen möchte, dass ein Alter nur positiv sein kann, kann ich dies erreichen, indem ich den Alterstyp auf uint setze.
Andererseits können Berechnungen mit Ganzzahlen ohne Vorzeichen zu Fehlern aller Art führen, und es ist schwierig, Operationen wie das Subtrahieren von zwei Zeitaltern durchzuführen. (Ich habe gelesen, dass dies einer der Gründe ist, warum Java vorzeichenlose Ganzzahlen weggelassen hat.)
Im Falle von C # kann ich auch denken, dass eine Schutzklausel für den Setter eine Lösung wäre, die das Beste aus zwei Welten bietet, aber dies wäre nicht anwendbar, wenn ich zum Beispiel ein Alter an eine Methode weitergeben würde. Eine Problemumgehung wäre, eine Klasse mit dem Namen Age zu definieren und das Eigenschaftsalter als einziges Element anzugeben. Dieses Muster würde jedoch dazu führen, dass ich viele Klassen erstelle, was Verwirrung stiftet (andere Entwickler würden nicht wissen, wann ein Objekt nur ein Wrapper ist und wenn es etwas mehr sofisticaded ist).
Was sind einige allgemeine Best Practices in Bezug auf dieses Problem? Wie soll ich mit einem solchen Szenario umgehen?
quelle
Antworten:
Die Designer von .NET Framework haben aus mehreren Gründen eine 32-Bit-Ganzzahl mit Vorzeichen als "Allzwecknummer" ausgewählt:
Der Grund für die Verwendung von nicht signierten Ints ist nicht lesbar . Es hat die Fähigkeit, die Mathematik zu erhalten, die nur ein Int ohne Vorzeichen liefert.
Schutzklauseln, Validierung und Vertragsvoraussetzungen sind absolut akzeptable Möglichkeiten, um gültige Zahlenbereiche zu gewährleisten. Selten entspricht ein realer numerischer Bereich genau einer Zahl zwischen 0 und 2 32 -1 (oder was auch immer der native numerische Bereich des von Ihnen gewählten numerischen Typs ist). Die Verwendung von a
uint
, um Ihren Schnittstellenvertrag auf positive Zahlen zu beschränken, ist also eine Art von neben dem Fakt.quelle
for (uint j=some_size-1; j >= 0; --j)
wiederholen die Schleife mit einem vorzeichenlosen int-Zähler, da manche Größe eine ganze Zahl ist: whoops ( Ich bin mir nicht sicher, ob dies ein Problem in C # ist. Ich habe dieses Problem im Code gefunden, in dem versucht wurde, unsigniertes int auf der C-Seite so weit wie möglich zu verwenden - und wir haben es letztendlich geändert, um es später zu bevorzugenint
, und unser Leben war viel einfacher, da weniger Compiler-Warnungen ausgegeben wurden.int
meiste Zeit, da dies die etablierte Konvention ist und die meisten Leute erwarten, dass sie routinemäßig verwendet werden. Verwendenuint
Sie diese Konvention, wenn Sie die besonderen Fähigkeiten von a benötigenuint
." Denken Sie daran, dass die Framework-Designer beschlossen haben, diese Konvention weitgehend einzuhalten, sodass Sie sie nicht einmaluint
in vielen Framework-Kontexten verwenden können (sie ist nicht typkompatibel).Im Allgemeinen sollten Sie für Ihre Daten immer den spezifischsten Datentyp verwenden.
Wenn Sie beispielsweise Entity Framework zum Abrufen von Daten aus einer Datenbank verwenden, verwendet EF automatisch den Datentyp, der dem in der Datenbank verwendeten am nächsten kommt.
Es gibt zwei Probleme damit in C #.
Erstens verwenden die meisten C # -Entwickler nur
int
die Darstellung ganzer Zahlen (es sei denn, es gibt einen Grund für die Verwendunglong
). Dies bedeutet, dass andere Entwickler nicht daran denken, den Datentyp zu überprüfen, und daher die oben genannten Überlauffehler erhalten. Die zweite und kritischere Problem ist / war , dass .NET die ursprünglichen arithmetischen Operatoren nur unterstütztint
,uint
,long
,ulong
,float
, Doppel-, unddecimal
*. Dies ist auch heute noch der Fall (siehe Abschnitt 7.8.4 in der C # 5.0-Sprachspezifikation ). Sie können dies mit dem folgenden Code selbst testen:Das Ergebnis unseres
byte
-byte
ist einint
(System.Int32
).Diese beiden Probleme führten zu der so verbreiteten Praxis, "nur int für ganze Zahlen zu verwenden".
Um Ihre Frage zu beantworten, ist es in C # normalerweise eine gute Idee, sich an
int
Folgendes zu halten:byte
und anint
oder anint
und along
kritisch ist oder die arithmetischen Unterschiede der bereits erwähnten vorzeichenlosen Datentypen).Wenn Sie mit den Daten rechnen müssen, halten Sie sich an die gebräuchlichen Typen.
Denken Sie daran, dass Sie von einem Typ in einen anderen umwandeln können. Dies kann unter CPU-Gesichtspunkten weniger effizient sein, sodass Sie wahrscheinlich mit einem der 7 gängigen Typen besser zurechtkommen. Bei Bedarf ist dies jedoch eine Option.
Enumerations (
enum
) ist eine meiner persönlichen Ausnahmen zu den oben genannten Richtlinien. Wenn ich nur einige Optionen habe, gebe ich die Enumeration als Byte oder Kurzform an. Wenn ich das letzte Bit in einer markierten Aufzählung benötige, gebe ich den Typ an,uint
damit ich hex verwenden kann, um den Wert für die Markierung festzulegen .Wenn Sie eine Eigenschaft mit Code verwenden, der den Wert einschränkt, müssen Sie im Zusammenfassungstag erläutern, welche Einschränkungen bestehen und warum.
* C # -Aliasnamen werden anstelle von .NET-Namen verwendet,
System.Int32
da dies eine C # -Frage ist.Hinweis: Es gab ein Blog oder einen Artikel der .NET-Entwickler (den ich nicht finden kann), in dem auf die begrenzte Anzahl von Rechenfunktionen und einige Gründe hingewiesen wurde, warum sie sich keine Gedanken darüber machten. Wie ich mich erinnere, gaben sie an, dass sie keine Pläne hatten, Unterstützung für die anderen Datentypen hinzuzufügen.
Hinweis: Java unterstützt keine vorzeichenlosen Datentypen und hatte zuvor keine Unterstützung für 8- oder 16-Bit-Ganzzahlen. Da viele C # -Entwickler einen Java-Hintergrund hatten oder in beiden Sprachen arbeiten mussten, wurden die Einschränkungen einer Sprache manchmal der anderen künstlich auferlegt.
quelle
Sie müssen sich hauptsächlich zweier Dinge bewusst sein: der Daten, die Sie darstellen, und etwaiger Zwischenschritte bei Ihren Berechnungen.
Es ist sicherlich sinnvoll, Alter zu haben
unsigned int
, da wir in der Regel kein negatives Alter berücksichtigen. Aber dann erwähnen Sie das Subtrahieren eines Alters von einem anderen. Wenn wir nur blind eine ganze Zahl von einer anderen subtrahieren, ist es definitiv möglich, dass wir eine negative Zahl erhalten, auch wenn wir uns zuvor darauf geeinigt haben, dass negative Alter keinen Sinn ergeben. In diesem Fall möchten Sie also, dass Ihre Berechnung mit einer Ganzzahl mit Vorzeichen durchgeführt wird.In Bezug darauf, ob Werte ohne Vorzeichen schlecht sind oder nicht, würde ich sagen, dass es eine große Verallgemeinerung ist, zu sagen, dass Werte ohne Vorzeichen schlecht sind. Java hat keine vorzeichenlosen Werte, wie Sie erwähnt haben, und es nervt mich ständig. A
byte
kann einen Wert von 0-255 oder 0x00-0xFF haben. Wenn Sie jedoch ein Byte größer als 127 (0x7F) instanziieren möchten, müssen Sie es entweder als negative Zahl schreiben oder eine Ganzzahl in ein Byte umwandeln. Am Ende haben Sie Code, der so aussieht:Das oben Genannte ärgert mich ohne Ende. Ich darf nicht, dass ein Byte einen Wert von 197 hat, obwohl das für die meisten vernünftigen Leute, die mit Bytes zu tun haben, ein vollkommen gültiger Wert ist. Ich kann die ganze Zahl umwandeln oder den negativen Wert finden (197 == -59 in diesem Fall). Beachten Sie auch Folgendes:
Wie Sie sehen, ändert sich das Vorzeichen, wenn Sie zwei Bytes mit gültigen Werten hinzufügen und am Ende ein Byte mit einem gültigen Wert erhalten. Nicht nur das, aber es ist nicht sofort offensichtlich, dass 70 + 80 == -106. Technisch ist dies ein Überlauf, aber in meinen Augen (als Mensch) sollte ein Byte für Werte unter 0xFF nicht überlaufen. Wenn ich auf Papier ein bisschen rechne, halte ich das achte Bit nicht für ein Vorzeichen.
Ich arbeite mit vielen ganzen Zahlen auf der Bit-Ebene, und wenn alles signiert ist, ist normalerweise alles weniger intuitiv und schwieriger zu handhaben, da Sie sich daran erinnern müssen, dass Sie durch Verschieben einer negativen Zahl nach rechts neue
1
s in Ihrer Zahl erhalten. Während die Rechtsverschiebung einer vorzeichenlosen ganzen Zahl dies niemals tut. Beispielsweise:Es werden nur zusätzliche Schritte hinzugefügt, die meines Erachtens nicht notwendig sein sollten.
Während ich
byte
oben verwendet habe, gilt das gleiche für 32-Bit- und 64-Bit-Ganzzahlen. Nicht habenunsigned
ist lähmend und es schockiert mich, dass es Hochsprachen wie Java gibt, die sie überhaupt nicht zulassen. Für die meisten Menschen ist dies jedoch kein Problem, da sich viele Programmierer nicht mit Bit-Level-Arithmetik befassen.Am Ende ist es nützlich, vorzeichenlose Ganzzahlen zu verwenden, wenn Sie sie als Bits betrachten, und es ist nützlich, vorzeichenbehaftete Ganzzahlen zu verwenden, wenn Sie sie als Zahlen betrachten.
quelle