Чи слід використовувати uint в C # для значень, які не можуть бути від'ємними?


80

Я щойно спробував реалізувати клас, де uintзамість цього є численні властивості length / count тощо int. Однак, роблячи це, я помітив, що насправді боляче це робити, як ніби насправді ніхто цього не хоче робити.

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

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

PS: Я бачив це запитання, і це принаймні пояснює, чому сам фреймворк завжди використовує, intале навіть у власному коді насправді доволі uintважко дотримуватися, що змушує мене думати, що це, мабуть, насправді не потрібне.


Відповіді:


42

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

У цьому випадку я не думаю, що зменшення читабельності, пов’язане з необхідністю робити касти, того варте.


2
Важко сказати, я не думаю, що касти значно зменшують легальність лише для індексів, і ви можете створити обгортку. А ті, що стають негативними, - це болючі та дорогі помилки.
user1496062

6
Якщо вам потрібно створити обгортку для int, тоді у вас є більше, ніж проблеми з читабельністю :)
S ..

61

До інших відповідей я також додаю, що використання uint як типу загальнодоступного поля, властивості, методу, параметра тощо є порушенням правил специфікації загальної мови, і цього слід уникати, коли це можливо.


3
І я згорів від Microsoft, строго дотримуючись цього. Виявляється, IntPtr мав мати конструктор uint.
Джошуа

3
Я б також додав, якщо вони підтримували натуральні числа, наприклад, цей тип має значення від 0 до 6, вони могли б усунути всі перевірки меж масиву, значний приріст продуктивності на 5-10%.
user1496062

Чи погоджуєтесь ви з наведеним вище твердженням, містере Ліпперт?
AgentFire

@AgentFire: У заяві йдеться про те, що якщо хтось щось зробив, це призведе до певного покращення продуктивності. Я не маю можливості оцінювати протилежні претензії; якщо ви хочете дізнатися, чи відповідає ця претензія, запитайте користувача1496062, що обґрунтовує їхню претензію, а не мене; Я не той, хто заявляє непідтримувані претензії.
Ерік Ліпперт,

1
@EricLippert - це одне з ваших найкоротших найдовших "я не знаю", чи не так: p
AgentFire

4

Негативне значення часто використовується для сигналізації про стан помилки, і розмір операції часто повертається викликом функції; Отже, негативне значення може сигналізувати про помилку, не вдаючись до механізму виключення.

Також зауважте, що .NET часто базується на прямих бібліотеках C, тому доцільно продовжувати цю конвенцію. Якщо вам потрібен більший простір індексу, ви можете порушити домовленість щодо іншого механізму сигналізації про помилки.


24
.NET досить узгоджується із створенням винятків, а не з поверненням кодів помилок. Повернення -1 не є звичайним явищем у керованому коді.
ChrisV

погодились, але ми говоримо про конвенцію про дизайн, яка поширена у всіх технологіях Windows (на додаток до винятків, де вони підтримуються) - ті частини бібліотеки .NET, які взаємодіють з кодом низького рівня, повинні перекладати між кодами помилок ЕПТ та винятками - це якось зрозуміло, чому вони висувають конвенцію int, а не uint.
Hassan Syed

@HassanSyed все-таки, якщо якийсь winapi повертає негативне значення, фреймворк все одно викине власний виняток, обертаючи все в акуратну оболонку, щоб цей аргумент тут не застосовувався.
AgentFire

3

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


3

Використання int також корисно для виявлення цілочисельного переповнення в операціях.


Не на 100% точний, якщо він настільки переповнився, що досяг нуля. Знову ж таки, є захист від переповнення, увімкнений за замовчуванням.
AgentFire

3

IMO, недоліком використання 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);

1
Див. Також ArgumentOutOfRangeException.
Фред
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.