Ostatnio pomyślałem o użyciu liczb całkowitych bez znaku w języku C # (i chyba podobny argument można powiedzieć o innych „językach wysokiego poziomu”)
Gdy w potrzebie liczby całkowitej zwykle nie stoję przed dylematem wielkości liczby całkowitej, przykładem może być właściwość age klasy Person (ale pytanie nie ogranicza się do właściwości). Mając to na uwadze, o ile widzę, istnieje tylko jedna zaleta używania niepodpisanej liczby całkowitej („uint”) nad liczbą całkowitą ze znakiem („int”) - czytelność. Jeśli chcę wyrazić pogląd, że wiek może być tylko dodatni, mogę to osiągnąć, ustawiając typ wieku na uint.
Z drugiej strony obliczenia liczb całkowitych bez znaku mogą prowadzić do różnego rodzaju błędów i utrudniają wykonywanie operacji, takich jak odejmowanie dwóch grup wiekowych. (Przeczytałem, że jest to jeden z powodów, dla których Java pominęła liczby całkowite bez znaku)
W przypadku C # mogę również pomyśleć, że klauzula ochronna na seterze byłaby rozwiązaniem, które daje to, co najlepsze z dwóch światów, ale nie miałoby to zastosowania, gdy na przykład wiek byłby przejściem do jakiejś metody. Obejściem tego problemu byłoby zdefiniowanie klasy o nazwie Wiek, a jedyną rzeczą byłaby wiek właściwości, ale ten wzorzec spowodowałby, że utworzyłem wiele klas i byłby źródłem zamieszania (inni programiści nie wiedzieliby, kiedy obiekt jest tylko opakowaniem a kiedy jest to coś bardziej sofistycznego).
Jakie są najlepsze ogólne praktyki dotyczące tego problemu? Jak mam poradzić sobie z tego typu scenariuszem?
źródło
Odpowiedzi:
Projektanci platformy .NET Framework wybrali 32-bitową liczbę całkowitą ze znakiem jako swój „numer ogólnego przeznaczenia” z kilku powodów:
Powodem używania bez znaku int jest brak czytelności; ma możliwość uzyskania matematyki zapewnianej tylko przez int bez znaku.
Klauzule ochronne, walidacja i warunki wstępne umowy są całkowicie akceptowalnymi sposobami na zapewnienie prawidłowych zakresów liczbowych. Rzadko rzeczywisty zakres liczbowy odpowiada dokładnie liczbie od zera do 2 32 -1 (lub cokolwiek natywny zakres liczbowy jest typem liczbowym, który wybrałeś), więc użycie a
uint
do ograniczenia umowy interfejsu do liczb dodatnich jest rodzajem bez związku.źródło
for (uint j=some_size-1; j >= 0; --j)
- ups ( nie jestem pewien, czy jest to problem w języku C #)! Znalazłem ten problem w kodzie, przed którym próbowałem jak najwięcej używać niepodpisanej int po stronie C - i ostatecznie zmieniliśmy go tak, aby sprzyjałint
później, a nasze życie było znacznie łatwiejsze, z mniejszą liczbą ostrzeżeń kompilatora.int
większość czasu, ponieważ jest to ustalona konwencja i tego większość ludzi spodziewa się, że będzie używana rutynowo. Używaj,uint
gdy potrzebujesz specjalnych możliwości auint
.” Pamiętaj, że projektanci Frameworków zdecydowali się na przestrzeganie tej konwencji, więc nie możesz nawet używać jejuint
w wielu kontekstach (nie jest kompatybilna z typem).Zasadniczo należy zawsze używać najbardziej konkretnego typu danych dla swoich danych.
Jeśli na przykład używasz Entity Framework do pobierania danych z bazy danych, EF automatycznie użyje typu danych najbliższego typowi użytemu w bazie danych.
Istnieją dwa problemy z tym w języku C #.
Po pierwsze, większość programistów C # używa tylko
int
do reprezentowania liczb całkowitych (chyba że istnieje powód do użycialong
). Oznacza to, że inni programiści nie będą myśleć o sprawdzeniu typu danych, więc otrzymają błędy przepełnienia wspomniane powyżej. Druga, bardziej poważny problem, jest / było to, że .NET za oryginalne operatory arytmetyczne obsługiwane tylkoint
,uint
,long
,ulong
,float
, dwu-, idecimal
*. Tak jest do dzisiaj (patrz sekcja 7.8.4 w specyfikacji języka C # 5.0 ). Możesz to przetestować samodzielnie, używając następującego kodu:Wynikiem naszego
byte
-byte
jestint
(System.Int32
).Te dwa problemy doprowadziły do tak częstej praktyki „tylko int do liczb całkowitych”.
Aby odpowiedzieć na twoje pytanie, w języku C # zwykle dobrym pomysłem jest trzymanie się,
int
chyba że:byte
i anint
lub aint
ilong
jest krytyczna, lub różnice arytmetyczne wcześniej wspomnianych niepodpisanych).Jeśli potrzebujesz wykonać matematykę na danych, trzymaj się typowych typów.
Pamiętaj, że możesz rzucać z jednego rodzaju na inny. Może to być mniej wydajne z punktu widzenia procesora, więc prawdopodobnie lepiej jest z jednym z 7 popularnych typów, ale jest to opcja w razie potrzeby.
Enumerations (
enum
) jest jednym z moich osobistych wyjątków od powyższych wytycznych. Jeśli mam tylko kilka opcji, określę wyliczenie jako bajt lub krótki. Jeśli potrzebuję tego ostatniego bitu w oznaczonym wyliczeniu, określę typ, abyuint
móc użyć wartości szesnastkowej do ustawienia wartości flagi.Jeśli używasz właściwości z kodem ograniczającym wartość, wyjaśnij w tagu podsumowania, jakie są ograniczenia i dlaczego.
* Aliasy C # są używane zamiast nazw .NET takich jak,
System.Int32
ponieważ jest to pytanie C #.Uwaga: istniał blog lub artykuł od twórców platformy .NET (którego nie mogę znaleźć), w którym wskazano na ograniczoną liczbę funkcji arytmetycznych i kilka powodów, dla których nie martwili się o to. Jak pamiętam, wskazali, że nie mieli planów dodania obsługi innych typów danych.
Uwaga: Java nie obsługuje niepodpisanych typów danych i wcześniej nie obsługiwała 8 lub 16 bitowych liczb całkowitych. Ponieważ wielu programistów C # pochodziło z języka Java lub musiało pracować w obu językach, ograniczenia jednego języka były czasem sztucznie narzucane w drugim.
źródło
Musisz przede wszystkim pamiętać o dwóch rzeczach: danych, które reprezentujesz, oraz o wszelkich pośrednich krokach w swoich obliczeniach.
Z pewnością warto mieć wiek
unsigned int
, ponieważ zwykle nie bierzemy pod uwagę wieku ujemnego. Ale potem wspominasz o odejmowaniu jednego wieku od drugiego. Jeśli po prostu ślepo odejmiemy jedną liczbę całkowitą od drugiej, to z pewnością możliwe jest uzyskanie liczby ujemnej, nawet jeśli wcześniej ustaliliśmy, że ujemne wieku nie mają sensu. Tak więc w tym przypadku chciałbyś, aby twoje obliczenia zostały wykonane przy użyciu liczby całkowitej ze znakiem.W odniesieniu do tego, czy niepodpisane wartości są złe, czy nie, powiedziałbym, że ogromnym uogólnieniem jest twierdzenie, że niepodpisane wartości są złe. Java, jak wspomniałeś, nie ma niepodpisanych wartości i ciągle mnie denerwuje. A
byte
może mieć wartość od 0-255 lub 0x00-0xFF. Ale jeśli chcesz utworzyć bajt większy niż 127 (0x7F), musisz zapisać go jako liczbę ujemną lub rzucić liczbę całkowitą na bajt. Otrzymujesz kod, który wygląda następująco:Powyższe denerwuje mnie bez końca. Nie wolno mi mieć bajtu o wartości 197, nawet jeśli jest to całkowicie poprawna wartość dla większości rozsądnych ludzi zajmujących się bajtami. Mogę rzucić liczbę całkowitą lub znaleźć wartość ujemną (197 == -59 w tym przypadku). Weź również pod uwagę to:
Jak widać, dodanie dwóch bajtów z prawidłowymi wartościami i zakończenie bajtu z prawidłową wartością powoduje zmianę znaku. Nie tylko to, ale nie jest od razu oczywiste, że 70 + 80 == -106. Technicznie jest to przepełnienie, ale moim zdaniem (jako istota ludzka) bajt nie powinien przepełniać wartości poniżej 0xFF. Kiedy wykonuję arytmetykę na papierze, nie uważam, że ósmy bit jest bitem znaku.
Pracuję z wieloma liczbami całkowitymi na poziomie bitów, a posiadanie wszystkiego, co jest podpisane, zwykle sprawia, że wszystko jest mniej intuicyjne i trudniejsze w obsłudze, ponieważ musisz pamiętać, że przesunięcie w prawo liczby ujemnej daje nowe
1
s liczby. Podczas gdy przesunięcie w prawo niepodpisanej liczby całkowitej nigdy tego nie robi. Na przykład:To tylko dodatkowe kroki, które moim zdaniem nie powinny być konieczne.
Chociaż użyłem
byte
powyżej, to samo dotyczy 32-bitowych i 64-bitowych liczb całkowitych. Brak posiadaniaunsigned
jest paraliżujący i szokuje mnie, że istnieją języki wysokiego poziomu, takie jak Java, które w ogóle na to nie pozwalają. Ale dla większości ludzi nie stanowi to problemu, ponieważ wielu programistów nie zajmuje się arytmetyką na poziomie bitów.Na koniec warto używać liczb całkowitych bez znaku, jeśli myślisz o nich jak o bitach, i warto używać liczb całkowitych ze znakiem, gdy myślisz o nich jako o liczbach.
źródło