Следует ли использовать uint в C # для значений, которые не могут быть отрицательными?

80

Я только что попытался реализовать класс, в котором uintвместо него используются многочисленные свойства длины / количества и т int. Д. Однако при этом я заметил, что это на самом деле болезненно, как будто никто на самом деле этого не хочет.

Почти все, что выдает целочисленный тип, возвращает int, поэтому требуется приведение типов в нескольких точках. Я хотел построить StringBufferс длиной буфера по умолчанию для одного из полей этого класса. Также требуется гипс.

Поэтому я подумал, стоит ли мне просто вернуться intсюда. Я все равно не использую весь диапазон. Я просто подумал, поскольку то, с чем я имею дело, просто не может быть отрицательным (если бы это было так, это было бы ошибкой), было бы неплохо использовать его uint.

PS: Я видел этот вопрос, и это, по крайней мере, объясняет, почему сам фреймворк всегда использует, intно даже в собственном коде его на самом деле громоздко придерживаться, uintчто заставляет меня думать, что он явно не нужен.

Джоуи
источник
Также посмотрите, почему-net-use-int-вместо-of-uint-in-
specific

Ответы:

42

Хотя строго вы должны использовать uintдля переменных, которые содержат неотрицательное целое число, вы столкнулись с одной из причин, по которой это не всегда возможно.

В этом случае я не думаю, что снижение читабельности, связанное с необходимостью выполнения приведений, того стоит.

ChrisF
источник
2
Трудно сказать, я не думаю, что приведение текста сильно снижает удобочитаемость только для индексаторов, и вы можете создать оболочку. А int, которые становятся отрицательными, - это болезненные и дорогие ошибки.
user1496062 04
6
Если вам нужно создать оболочку для int, тогда у вас будет больше, чем проблем с читабельностью :)
S ..
61

Я также добавлю к другим ответам, что использование uint в качестве типа общедоступного поля, свойства, метода, параметра и так далее является нарушением правил спецификации общего языка и его следует избегать, когда это возможно.

Эрик Липперт
источник
3
И я был сожжен Microsoft строго после этого. Оказывается, у IntPtr должен был быть конструктор uint.
Джошуа
3
Я бы также добавил, если бы они поддерживали натуральные числа, например, этот тип имеет значение от 0 до 6, они могли бы исключить все проверки границ массива, что значительно повысило бы производительность на 5-10%.
user1496062 04
Согласны ли вы с приведенным выше утверждением, мистер Липперт?
AgentFire
@AgentFire: В заявлении утверждается, что если кто-то что-то сделает, это приведет к определенному повышению производительности. У меня нет возможности оценивать контрфактические утверждения; если вы хотите знать, верно ли это утверждение, спросите пользователя 1496062, а не меня, что оправдывает его утверждение; Не я делаю необоснованное утверждение.
Эрик Липперт
1
@EricLippert, это одно из ваших самых коротких «я не знаю», не так ли: p
AgentFire
4

Отрицательное значение часто используется для обозначения состояния ошибки, а размер операции часто возвращается при вызове функции; поэтому отрицательное значение может сигнализировать об ошибке, не прибегая к механизму исключения.

Также обратите внимание, что .NET часто строится на простых библиотеках C, поэтому разумно продолжить это соглашение. Если вам требуется большее пространство индекса, вы можете нарушить соглашение о другом механизме сигнализации об ошибках.

Хасан Сайед
источник
24
.NET довольно совместим с выдачей исключений, а не с возвратом кодов ошибок. Возврат -1 не является обычным явлением в управляемом коде.
ChrisV 06
согласовано, но мы говорим о соглашении о дизайне, которое преобладает во всех технологиях Windows (в дополнение к исключениям, где они поддерживаются) - те части библиотеки .NET, которые взаимодействуют с кодом низкого уровня, должны преобразовывать коды ошибок CRT и исключения - вполне понятно, почему они выдвигают соглашение о int, а не uint.
Хасан Сайед
@HassanSyed все же, если какой-то winapi возвращает отрицательное значение, фреймворк по-прежнему будет генерировать собственное исключение, упаковывая все в аккуратную оболочку, так что этот аргумент здесь не применим.
AgentFire
3

Я лично считаю, что вам, вероятно, следует просто придерживаться int. Не стоит добавлять приведение почти к каждому доступу к отдельному свойству только для того, чтобы вернуть числовой диапазон, который .NET вряд ли позволит вам использовать в любом случае.

ChrisV
источник
3

Использование int также полезно для обнаружения целочисленного переполнения в операциях.

Иоан
источник
Не на 100% точный, если он настолько переполнен, что достигает нуля. Опять же, по умолчанию включена защита от переполнения.
AgentFire
3

ИМО, недостатком использования uintявляется то, что он скрывает условия ошибки. Эквиваленты следующего кода не так хороши:

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

Конечно, ваши программы не должны ничего просчитывать с самого начала, но я считаю, что они также должны быть написаны для быстрого обнаружения числовых ошибок без распространения. В случае, когда MaxValue 2 ^ 31 достаточно, я говорю использовать intвместе с правильным использованием System.Diagnostics.Debug.Assert()и соответствующими проверками ошибок, как показано выше.

Если вы все же используете, uintиспользуйте его вместе с, checkedчтобы предотвратить переполнение и получить те же результаты. Однако я обнаружил, что проверку немного сложно применить к существующему коду, который для некоторых целей использует приведение типов.

Хит Ханникатт
источник
2

Не плывите против течения, если в этом нет необходимости. Если не загромождать код приведениями, он становится более читабельным. Кроме того, если ваши возможные значения соответствуют int, то использование int не является проблемой.

Если вы боитесь, что вы можете переполнить int, то во что бы то ни стало ... но не оптимизируйте преждевременно.

Я бы сказал, что улучшенная читаемость минимизации приведений перевешивает немного повышенный риск ошибки при использовании int.

Эрик Функенбуш
источник
2

Если вы хотите проверить, что значение положительное, лучше, вероятно, просто использовать assert (обратите внимание, что это всего лишь метод отладки - вы должны убедиться, что этого никогда не произойдет в окончательном коде).

using System.Diagnostics;
...
Debug.Assert (i > 0);
ternaryOperator
источник
1
См. Также ArgumentOutOfRangeException.
Фред