it-swarm.com.de

Sollte ich die Verwendung von int ohne Vorzeichen in C # vermeiden?

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" verwendet 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 vorzeichenlosen Ganzzahl ("uint") gegenüber einer vorzeichenbehafteten Ganzzahl ("int") - der 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 für vorzeichenlose Ganzzahlen 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 Java hat vorzeichenlose Ganzzahlen weggelassen)

Im Fall 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 auf eine Methode übergehen würde. Eine Problemumgehung wäre, eine Klasse namens "Alter" zu definieren und das Eigenschaftsalter als einziges Element zu verwenden. Bei diesem Muster würde ich jedoch viele Klassen erstellen und Verwirrung stiften (andere Entwickler würden nicht wissen, wann ein Objekt nur ein Wrapper ist und wenn es etwas weicher ist).

Was sind einige allgemeine Best Practices in Bezug auf dieses Problem? Wie soll ich mit dieser Art von Szenario umgehen?

24
Belgi

Die Entwickler von .NET Framework haben aus mehreren Gründen eine 32-Bit-Ganzzahl mit Vorzeichen als "Allzwecknummer" ausgewählt:

  1. Es kann negative Zahlen verarbeiten, insbesondere -1 (was das Framework verwendet, um eine Fehlerbedingung anzuzeigen; aus diesem Grund wird überall dort, wo eine Indizierung erforderlich ist, ein vorzeichenbehaftetes int verwendet, obwohl negative Zahlen in einem Indexierungskontext nicht aussagekräftig sind).
  2. Es ist groß genug, um die meisten Zwecke zu erfüllen, während es klein genug ist, um fast überall wirtschaftlich eingesetzt zu werden.

Der Grund für die Verwendung von Ints ohne Vorzeichen ist nicht Lesbarkeit; Es hat die Fähigkeit, die Mathematik zu erhalten, die nur ein Int ohne Vorzeichen bietet.

Schutzklauseln, Validierung und Vertragsvoraussetzungen sind durchaus akzeptable Möglichkeiten, um gültige Zahlenbereiche zu gewährleisten. Ein realer numerischer Bereich entspricht selten genau einer Zahl zwischen Null und 232-1 (oder was auch immer der native numerische Bereich von dem von Ihnen gewählten numerischen Typ ist), daher ist die Verwendung eines uint, um Ihren Schnittstellenvertrag auf positive Zahlen zu beschränken, nebensächlich.

24
Robert Harvey

Im Allgemeinen sollten Sie immer den spezifischsten Datentyp für Ihre Daten verwenden.

Wenn Sie beispielsweise Entity Framework verwenden, um Daten aus einer Datenbank abzurufen, verwendet EF automatisch den Datentyp, der dem in der Datenbank verwendeten am nächsten kommt.

In C # gibt es zwei Probleme damit.
Erstens verwenden die meisten C # -Entwickler nur int, um ganze Zahlen darzustellen (es sei denn, es gibt einen Grund, long zu verwenden). Dies bedeutet, dass andere Entwickler nicht daran denken, den Datentyp zu überprüfen, sodass sie die oben genannten Überlauffehler erhalten. Das zweite und kritischere Problem ist/war, dass .NETs rsprüngliche arithmetische Operatoren nur int, uint, long, ulong, float, double und decimal *. Dies ist auch heute noch der Fall (siehe Abschnitt 7.8.4 in C # 5.0-Sprachspezifikation ). Sie können dies selbst mit dem folgenden Code testen:

byte a, b;
a = 1;
b = 2;
var c = a - b;      //In visual studio, hover over "var" and the tip will indicate the data type, or you can get the value from cName below.
string cName = c.GetType().Namespace + '.' + c.GetType().Name;

Das Ergebnis unseres byte-byte ist ein int (System.Int32).

Diese beiden Probleme führten zu der so weit 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 zu halten, es sei denn:

  • Ein automatisierter Codegenerator verwendete einen anderen Wert (wie Entity Framework).
  • Allen anderen Entwicklern des Projekts ist bekannt, dass Sie die weniger gebräuchlichen Datentypen verwenden (fügen Sie einen Kommentar hinzu, der darauf hinweist, dass Sie den Datentyp verwendet haben und warum).
  • Die weniger gebräuchlichen Datentypen werden bereits häufig im Projekt verwendet.
  • Das Programm erfordert die Vorteile des weniger verbreiteten Datentyps (Sie haben 100 Millionen davon, die Sie im RAM behalten müssen, also den Unterschied zwischen einem byte und einem int oder einem int und ein long ist kritisch oder die bereits erwähnten arithmetischen Unterschiede von vorzeichenlosen).

Wenn Sie die Daten berechnen müssen, halten Sie sich an die gängigen Typen.
Denken Sie daran, Sie können von einem Typ zum anderen wechseln. Dies kann unter CPU-Gesichtspunkten weniger effizient sein, sodass Sie mit einem der 7 gängigen Typen wahrscheinlich besser dran sind. Bei Bedarf ist dies jedoch eine Option.

Aufzählungen ( enum ) sind eine meiner persönlichen Ausnahmen von den oben genannten Richtlinien. Wenn ich nur wenige Optionen habe, werde ich geben Sie die Aufzählung an ein Byte oder einen Kurzschluss sein. Wenn ich das letzte Bit in einer markierten Aufzählung benötige, gebe ich den Typ uint an, damit ich den Wert für das Flag mit hex setzen kann.

Wenn Sie eine Eigenschaft mit werteinschränkendem Code verwenden, müssen Sie im Zusammenfassungstag erläutern, welche Einschränkungen vorhanden sind und warum.

* C # -Aliasnamen werden anstelle von .NET-Namen wie System.Int32 Verwendet, da dies eine C # -Frage ist.

Hinweis: Es gab einen Blog oder 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 darüber keine Sorgen machten. Soweit 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 aus einem Java Hintergrund oder Um in beiden Sprachen arbeiten zu können, wurden die Einschränkungen einer Sprache manchmal der anderen künstlich auferlegt.

8
Trisped

Sie müssen sich hauptsächlich zweier Dinge bewusst sein: der Daten, die Sie darstellen, und aller Zwischenschritte in Ihren Berechnungen.

Es ist sicherlich sinnvoll, das Alter unsigned int Zu haben, da wir normalerweise 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, eine negative Zahl zu erhalten, selbst wenn wir uns vorher einig waren, dass negatives Alter keinen Sinn ergibt. In diesem Fall möchten Sie, dass Ihre Berechnung mit einer vorzeichenbehafteten Ganzzahl durchgeführt wird.

In Bezug darauf, ob vorzeichenlose Werte schlecht sind oder nicht, würde ich sagen, dass es eine große Verallgemeinerung ist, zu sagen, dass vorzeichenlose Werte schlecht sind. Java hat keine vorzeichenlosen Werte, wie Sie erwähnt haben, und es nervt mich ständig. Ein byte kann einen Wert von 0-255 oder 0x00-0xFF haben. Aber wenn Sie Wenn Sie ein Byte instanziieren möchten, das größer als 127 (0x7F) ist, müssen Sie es entweder als negative Zahl schreiben oder eine Ganzzahl in ein Byte umwandeln. Am Ende erhalten Sie Code, der folgendermaßen aussieht:

byte a = 0x80; // Won't compile!
byte b = (byte) 0x80;
byte c = -128; // Equal to b

Das obige nervt mich bis zum Ende. Ich darf kein Byte mit einem Wert von 197 haben, obwohl dies ein vollkommen gültiger Wert für die meisten gesunden Menschen ist, die mit Bytes zu tun haben. Ich kann die ganze Zahl umwandeln oder den negativen Wert finden (197 == -59 in diesem Fall). Beachten Sie auch Folgendes:

byte a = 70;
byte b = 80;
byte c = a + b; // c == -106

Wie Sie sehen können, ändert das Hinzufügen von zwei Bytes mit gültigen Werten und das Beenden eines Bytes mit einem gültigen Wert das Vorzeichen. Nicht nur das, aber es ist nicht sofort offensichtlich, dass 70 + 80 == -106. Technisch gesehen ist dies ein Überlauf, aber in meinen Augen (als Mensch) sollte ein Byte für Werte unter 0xFF nicht überlaufen. Wenn ich auf Papier etwas rechne, betrachte ich das 8. Bit nicht als Vorzeichen.

Ich arbeite mit vielen ganzen Zahlen auf Bitebene, und wenn alles signiert ist, ist normalerweise alles weniger intuitiv und schwieriger zu handhaben, da Sie sich daran erinnern müssen, dass das Verschieben einer negativen Zahl nach rechts neue 1 S ergibt in Ihrer Nummer. Während das Verschieben einer Ganzzahl ohne Vorzeichen nach rechts dies niemals tut. Zum Beispiel:

signed byte b = 0b10000000;
b = b >> 1; // b == 0b1100 0000
b = b & 0x7F;// b == 0b0100 0000

unsigned byte b = 0b10000000;
b = b >> 1; // b == 0b0100 0000;

Es werden nur zusätzliche Schritte hinzugefügt, die meiner Meinung nach nicht notwendig sein sollten.

Während ich oben byte verwendet habe, gilt das Gleiche für 32-Bit- und 64-Bit-Ganzzahlen. unsigned nicht zu haben ist lähmend und es schockiert mich, dass es Hochsprachen wie Java, die sie überhaupt nicht zulassen. Aber für die meisten Leute ist dies kein Problem , weil sich viele Programmierer nicht mit Arithmetik auf Bitebene 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.

7
Shaz