Należy używać uint w C # dla wartości, które nie mogą być ujemne?

80

Właśnie próbowałem zaimplementować klasę, w której uintzamiast wielu właściwości length / count itp. Są int. Jednak robiąc to zauważyłem, że jest to bolesne, jakby nikt nie chciał tego robić.

Prawie wszystko, co podaje typ całkowy, zwraca an int, dlatego wymaga rzutów w kilku punktach. Chciałem skonstruować StringBufferz długością bufora domyślną dla jednego z pól w tej klasie. Wymaga też obsady.

Zastanawiałem się więc, czy po prostu powrócić do inttego miejsca. Z pewnością i tak nie używam całego zakresu. Pomyślałem tylko, że skoro to, z czym mam do czynienia, po prostu nie może być negatywne (gdyby tak było, byłby to błąd), fajnie byłoby to wykorzystać uint.

PS: Widziałem to pytanie i to przynajmniej wyjaśnia, dlaczego sam framework zawsze używa, intale nawet we własnym kodzie jest tak naprawdę niewygodny do trzymania się, uintco sprawia, że ​​myślę, że najwyraźniej nie jest tak naprawdę potrzebny.

Joey
źródło

Odpowiedzi:

42

Chociaż ściśle powinieneś używać uintdla zmiennych, które zawierają nieujemną liczbę całkowitą, natknąłeś się na jeden z powodów, dla których nie zawsze jest to wykonalne.

W tym przypadku nie sądzę, aby zmniejszenie czytelności związane z koniecznością wykonywania rzutów było tego warte.

ChrisF
źródło
2
Trudno powiedzieć, nie sądzę, by rzutowania znacznie zmniejszały czytelność tylko dla indeksatorów i można utworzyć opakowanie. A int, które stają się ujemne, to bolesne i kosztowne błędy.
user1496062,
6
Jeśli musisz stworzyć opakowanie dla int, to masz więcej niż problemy z czytelnością :)
S ..
61

Do innych odpowiedzi dodam również, że używanie uint jako typu pola publicznego, właściwości, metody, parametru itd. Jest naruszeniem zasad Common Language Specification i należy go unikać, gdy jest to możliwe.

Eric Lippert
źródło
3
I zostałem spalony przez Microsoft, ściśle przestrzegając tego. Okazuje się, że IntPtr powinien był mieć konstruktora uint.
Joshua
3
Dodałbym również, że jeśli obsługują liczby naturalne, np. Ten typ ma wartość od 0 do 6, mogą wyeliminować wszystkie sprawdzanie granic tablicy, co oznacza ogromny wzrost wydajności o 5-10%.
user1496062
Czy zgadza się Pan z powyższym stwierdzeniem, panie Lippert?
AgentFire
@AgentFire: W oświadczeniu stwierdza się, że gdyby ktoś coś zrobił, spowodowałoby to określoną poprawę wydajności. Nie mam możliwości oceny roszczeń ze scenariusza alternatywnego; jeśli chcesz wiedzieć, czy to twierdzenie jest prawdziwe, zapytaj użytkownika1496062, co uzasadnia jego roszczenie, a nie mnie; To nie ja składam nieuzasadnione twierdzenie.
Eric Lippert
1
@EricLippert to jedno z najkrótszych twoich najdłuższych „nie wiem”, prawda: p
AgentFire
4

Wartość ujemna jest często używana do sygnalizowania stanu błędu, a wywołanie funkcji często zwraca rozmiar operacji; wartość ujemna może zatem sygnalizować błąd bez uciekania się do mechanizmu wyjątków.

Należy również zauważyć, że .NET często opiera się na zwykłych bibliotekach C, dlatego rozsądne jest kontynuowanie tej konwencji. Jeśli potrzebujesz większej przestrzeni indeksów, możesz złamać konwencję dotyczącą innego mechanizmu sygnalizacji błędów.

Hassan Syed
źródło
24
.NET jest dość spójny z rzucaniem wyjątków zamiast zwracaniem kodów błędów. Zwrot -1 nie jest typowym widokiem w kodzie zarządzanym.
ChrisV,
zgodził się, ale mówimy o konwencji projektowej, która jest powszechna w technologiach Windows (oprócz wyjątków, w których są obsługiwane) - te części biblioteki .NET, które łączą się z kodem niskiego poziomu, muszą tłumaczyć między kodami błędów CRT i wyjątkami - to trochę zrozumiałe, dlaczego wprowadzają konwencję int, a nie uint.
Hassan Syed
@HassanSyed nadal, jeśli jakieś winapi zwróci wartość ujemną, framework nadal będzie rzucał swój własny wyjątek, opakowując wszystko w zgrabną powłokę, więc ten argument nie ma tutaj zastosowania.
AgentFire
3

Osobiście uważam, że prawdopodobnie powinieneś po prostu trzymać się int. Nie warto dodawać rzutowania do prawie każdego dostępu do właściwości tylko po to, aby odzyskać zakres liczbowy, którego .NET i tak prawdopodobnie nie pozwoli Ci użyć.

ChrisV
źródło
3

Używanie int jest również pomocne w wykrywaniu przepełnienia liczb całkowitych w operacjach.

Ioan
źródło
Nie w 100% dokładne, jeśli jest tak przepełnione, że osiąga zero. Ponownie, ochrona przed przepełnieniem jest domyślnie włączona.
AgentFire
3

IMO, wadą używania uintjest to, że przesłania błędy. Odpowiedniki poniższego kodu nie są tak ładne:

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

Oczywiście twoje programy nie powinny na początku niczego przeliczyć, ale uważam, że powinny być napisane tak, aby szybko wykrywać błędy numeryczne bez propagacji. W przypadku, gdy MaxValue równa 2 ^ 31 jest wystarczająca, mówię, że używaj intwraz z właściwym użyciem System.Diagnostics.Debug.Assert()i odpowiednimi kontrolami błędów, jak pokazano powyżej.

Jeśli uintużywasz go razem z, checkedaby zapobiec niedomiarowi i uzyskać takie same wyniki. Jednak odkryłem, że sprawdzanie jest trochę trudne do zastosowania w istniejącym kodzie, który używa rzutów w jakimś celu.

Heath Hunnicutt
źródło
2

Nie pływaj pod prąd, jeśli nie musisz. Brak literowania kodu za pomocą rzutowań sprawia, że ​​kod jest bardziej czytelny. Ponadto, jeśli możliwe wartości mieszczą się w int, to użycie int nie stanowi problemu.

Jeśli obawiasz się, że możesz przepełnić int, to zdecydowanie tam ... ale nie optymalizuj przedwcześnie.

Powiedziałbym, że poprawiona czytelność minimalizowania rzutów przeważa nad nieco podwyższonym ryzykiem błędu przy użyciu int.

Erik Funkenbusch
źródło
2

Jeśli chcesz sprawdzić, czy wartość jest dodatnia, lepszym sposobem jest prawdopodobnie użycie assert (zwróć uwagę, że jest to tylko technika debugowania - powinieneś upewnić się, że nigdy nie wystąpi to w końcowym kodzie).

using System.Diagnostics;
...
Debug.Assert (i > 0);
ternaryOperator
źródło
1
Zobacz także ArgumentOutOfRangeException.
Fred