Ініціалізація рядків за замовчуванням: NULL або Empty? [зачинено]


130

Я завжди ініціалізував свої рядки до NULL, маючи на увазі, що NULL означає відсутність значення та "" або String.Empty - це дійсне значення. Останнім часом я бачив більше прикладів коду, де String.Empty вважається значенням за замовчуванням або не представляє значення. Це здається мені дивним, коли щойно додані нульові типи в c # здається, ми робимо кроки назад за допомогою рядків, не використовуючи NULL для позначення "Без значення".

Що ви використовуєте як ініціалізатор за замовчуванням і чому?

Редагувати: Грунтуючись на відповідях, я розвиваю подальші думки

  1. Уникнення поводження з помилками Якщо значення не повинно бути нульовим, чому воно встановилося NULLв першу чергу? Можливо, було б краще визначити помилку в місці, де вона виникає, а не приховати її через решту вашої бази коду?

  2. Уникнення нульових перевірок Якщо ви втомилися робити нульові перевірки в коді, чи не було б краще абстрагувати нульові чеки? Можливо, оберніть (або розтягніть!) Рядкові методи, щоб зробити їх NULLбезпечними? Що трапиться, якщо ви постійно користуєтесь, String.Emptyа нуль працює, це входить у вашу систему, ви все одно починаєте додавати NULLчеки?

Не можу не повернутись до думки, що це лінь. Будь-яка DBA ударить вас дев'ятьма способами безглуздо, якби ви використовували '' замість nullйого бази даних. Я думаю, що ті ж принципи застосовуються і в програмуванні, і повинен бути хтось, хто б'є по голові тих, хто використовує, String.Emptyа не NULLпредставляє ніякої цінності.

Пов’язані запитання


"Здоровий"? Має бути не даною, яку я знаю.
vfilby

@Joel, я вражений тим, скільки людей не мають поняття про Zim чи GIR, я також вражений деякими моїми друзями, які вважають це відразливим. Не кажучи, що це чиста доброта, але там є дивовижні самородки гумору.
vfilby

Я знаю, але іноді весело прикидатися інакше.
Dana Sane

1
Я зіткнувся з цією проблемою в колекціях MVC форм або змінних сеансів дуже багато, я виявив, що найбільш корисним було перетворити null в String.Empty з ?? стенограми, а потім застосуйте всі необхідні операції струни. напр. (item ?? String.Empty) .Trim (). ToUpper ()
sonjz

4
це не конструктивно ??
nawfal

Відповіді:


111

+1 для розрізнення "порожній" та NULL. Я погоджуюся, що "порожній" повинен означати "дійсний, але порожній", а "NULL" повинен означати "недійсний".

Тож я відповів би на ваше запитання так:

порожній, коли я хочу дійсне значення за замовчуванням, яке може бути або не може бути змінено, наприклад, прізвище користувача.

NULL, коли це помилка, якщо наступний код не встановлює значення явно.


11
Розрізняти NULL від порожнього чудово, коли між ними є різниця. Однак є багато випадків, коли різниці немає, і тому два способи представлення однієї і тієї ж речі є відповідальністю.
Грег Менший

6
@Greg: Хоча я погоджуюся, що сорт може заплутатись, він також може стати великим надбанням. Проста, послідовна умова написання "" або NULL для розмежування між дійсними та недійсними значеннями полегшить ваше розуміння коду. Ось чому я завжди тестую булеві символи з "if (var)," покажчики з "if (var! = NULL)" і цілі числа з "if (var! = 0)" - вони означають компілятор те саме, але вони містять додаткову інформацію, яка допомагає поганому розробнику, який підтримує мій код.
Адам Лісс

32

За даними MSDN :

Ініціалізуючи рядки зі Emptyзначенням замість null, ви можете зменшити шанси на NullReferenceExceptionвиникнення.

Завжди використовувати IsNullOrEmpty()хорошу практику.


45
Тільки тому, що ви зменшуєте шанси на виняток, не означає, що виняток не повинен відбуватися. Якщо ваш код залежить від того, яке значення є, він повинен кинути виняток!
rmeador

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

Це головним чином те, що я використовую, розрізняю, яке також використовувати.
PositiveGuy

3
Не забувайте IsNullOrWhiteSpace () для .NET Framework 4+
Coops

13

Чому ви хочете, щоб ваша рядок взагалі була ініціалізована? Не потрібно ініціалізувати змінну, коли ви оголошуєте її, і IMO, ви повинні робити це лише тоді, коли значення, яке ви призначаєте, дійсне в контексті блоку коду.

Я бачу це багато:

string name = null; // or String.Empty
if (condition)
{
  name = "foo";
}
else
{
  name = "bar";
}

return name;

Неініціалізація до нуля була б настільки ж ефективною. Крім того, найчастіше потрібно призначити значення. Ініціалізуючи null, ви потенційно можете пропустити кодові шляхи, які не присвоюють значення. Так:

string name = null; // or String.Empty
if (condition)
{
  name = "foo";
}
else if (othercondition)
{
  name = "bar";
}

return name; //returns null when condition and othercondition are false

Коли ви не ініціалізуєтеся на null, компілятор генерує помилку, кажучи, що не всі кодові шляхи призначають значення. Звичайно, це дуже простий приклад ...

Маттійс


У Visual Studio, яку я вважаю, майже кожен програміст C # використовує, ваша друга ситуація (без цього = null) генеруватиме попередження саме з тієї причини, яку ви вказали - не має значення, чи значення нульового рядка за замовчуванням є нульовим. якщо ви не гарантуєте призначення через кожен шлях коду, IDE (та / або я вважаю, що компілятор, що лежить в основі [?]), генерує попередження. Хоча попередження не перешкоджають компіляції, вони все ще є - залишення тих, що легко вирішуються, може допомогти затуманити інших, що може заслуговувати на увагу програміста
Code Jockey

Наскільки мені відомо, перша ситуація буде цілком задоволена без ініціалізації nameдо null(без попереджень), оскільки кожен кодовий шлях присвоює значення name- взагалі не потрібно там ініціалізувати
Code Jockey

8

Для більшості програмного забезпечення, яке насправді не є рядковим програмним забезпеченням, логіка програми не повинна залежати від вмісту струнних змінних. Щоразу, коли я бачу щось подібне в програмі:

if (s == "value")

У мене погані відчуття. Чому в цьому методі є рядковий літерал? Що налаштування s? Чи відомо, що логіка залежить від значення рядка? Чи знає він, що це має бути нижчим регістром для роботи? Чи слід це виправляти, змінюючи його на використання String.Compare? Чи повинен я створювати Enumі аналізувати його?

З цього погляду можна потрапити до філософії коду, яка є досить простою: ви уникаєте вивчення вмісту рядка, де це можливо. Порівняння рядка зString.Empty справді - це лише особливий випадок порівняння його з буквальним: цього потрібно уникати, якщо цього не потрібно.

Знаючи це, я не моргну, коли бачу щось подібне в нашій кодовій базі:

string msg = Validate(item);
if (msg != null)
{
   DisplayErrorMessage(msg);
   return;
}

я це знаю Validate ніколи не повернетьсяString.Empty , тому що ми пишемо кращий код, ніж цей.

Звичайно, решта світу не працює так. Коли ваша програма має справу з введенням користувача, базами даних, файлами тощо, вам доведеться враховувати інші філософії. Там завдання вашого коду - накладати порядок на хаос. Частина цього порядку - це знати, коли має означати порожній рядокString.Empty і коли він повинен означатиnull .

(Просто для того, щоб переконатися, що я не розмовляв із своєю дупою, я просто шукав нашу кодову базу на предмет "String.IsNullOrEmpty". Усі 54 події в ній знаходяться в методах, які обробляють введення користувача, повертають значення з сценаріїв Python, вивчають значення, отримані з зовнішні API та ін.)


6

Це насправді зяюча дірка у мові C #. Немає можливості визначити рядок, який не може бути нульовим. Це спричиняє проблеми настільки ж прості, як та, яку ви описуєте, що змушує програмістів приймати рішення, якого вони не повинні приймати, оскільки в багатьох випадках NULL та String.Empty означають те саме. Це, в свою чергу, може згодом змусити інших програмістів працювати з NULL і String.Empty, що дратує.

Більшою проблемою є те, що бази даних дозволяють визначати поля, які відображають у рядок C #, але поля бази даних можуть бути визначені як NOT NULL. Отже, немає способу точно представити, скажімо, поле varchar (100) NOT NULL у SQL Server, використовуючи тип C #.

Інші мови, наприклад Spec #, дозволяють це зробити.

На мій погляд, нездатність C # визначити рядок, який не дозволяє null, так само погано, як і його попередня нездатність визначити int, яка дозволяє null.

Щоб повністю відповісти на ваше запитання: я завжди використовую порожню рядок для ініціалізації за замовчуванням, оскільки це більше схоже на те, як працюють типи даних бази даних. (Редагувати: Цей вислів був дуже незрозумілим. У ньому слід писати "Я використовую порожню рядок для ініціалізації за замовчуванням, коли NULL є зайвим станом, так само, як я встановив стовпчик бази даних як НЕ НУЛЬНИЙ, якщо NULL був би зайвим станом". , багато моїх стовпців БД налаштовані як НЕ NULL, тому коли я переношу їх у рядок C #, рядок буде порожнім або матиме значення, але ніколи не буде NULL. Іншими словами, я ініціалізую лише рядок у NULL якщо null має значення, яке відрізняється від значення String.Empty, і я вважаю, що цей випадок рідше (але тут люди наводили законні приклади цього випадку). ")


Використання String.Empty є подібним лише до одного із способів визначення рядка бази даних. Використання null для відображення значення не набагато більше відповідає нульовому nvarchar. Я думаю, що будь-яка DBA, яка коштує їх солі, вдарила б тебе дев'ятьма способами дурного, якби ти "" не представляв жодної цінності.
vfilby

Насправді, Грег, ти зрозумів це неправильно. Це ненульові типи значень, найменше "як працюють типи баз даних", оскільки вони ніколи не можуть містити нуль, і, отже, ніколи не можуть зіставляти нульовий стовпець. У контракті будь-яка рядок може відображатись у будь-якому стовпчику варшара.
Tor Haugen

Ви маєте рацію, моє останнє твердження було недостатньо зрозумілим. У більшості випадків мої стовпці бази даних НЕ НУЛЬНІ (бо не було б різниці між значенням порожнього рядка та NULL), тому я намагаюся зберегти свої рядки подібними, ніколи не зберігаючи в них нуль, і саме це я мав на увазі.
Грег Менший

5

Це залежить.

Чи потрібно мати змогу сказати, чи не вистачає значення (чи не можна його визначити)?

Чи порожній рядок є дійсним значенням для використання цього рядка?

Якщо ви відповіли "так" обом, тоді ви хочете використовувати null. Інакше ви не можете вказати різницю між "без значення" та "порожнім рядком".

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



3

Я або встановив його на "" або null - я завжди перевіряю за допомогою String.IsNullOrEmpty, тому або добре.

Але внутрішній вуха в мені говорить, що я повинен встановити його на нуль, перш ніж мати для нього належне значення ...



2

Чи можливо це метод уникнення помилок (бажано чи ні ..)? Оскільки "" все ще є рядок, ви могли б викликати на ньому рядкові функції, що призвело б до винятку, якби це було NULL?


1
Це виправдання, яке я зазвичай чую, просто звучить як лінь. "Мені не хочеться заважати перевіряти це значення, тому я збираюся скористатися ярликом" - так мені здається.
vfilby

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

2

Я завжди ініціалізую їх як NULL.

Я завжди використовую string.IsNullOrEmpty(someString)для перевірки його цінності.

Простий.


1

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

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


1

Порожній рядок - це значення (фрагмент тексту, який, до речі, буває, не містить літер). Null означає не-значення.

Я ініціалізую змінні до нуля, коли хочу вказати, що вони не вказують на або містять фактичні значення - коли у наміру немає значення.


1

Повторюючи відповідь Tomalak, майте на увазі, що при призначенні змінної рядка початковому значенню null, ваша змінна більше не є об'єктом рядка; те саме з будь-яким об'єктом в C #. Отже, якщо ви спробуєте отримати доступ до будь-яких методів або властивостей для вашої змінної, і ви вважаєте, що це об'єкт рядка, ви отримаєте виняток NullReferenceException.


1

Null слід використовувати лише у випадках, коли значення необов’язкове. Якщо значення не є необов’язковим (наприклад, "Ім'я" або "Адреса"), то значення ніколи не повинно бути нульовим. Це стосується баз даних, а також POCO та інтерфейсу користувача. Null означає "це значення необов’язкове і наразі відсутнє".

Якщо ваше поле є необов’язковим, тоді слід ініціалізувати його як порожній рядок. Якщо ініціалізувати його як null, то об'єкт переведе в недійсний стан (недійсний за вашою власною моделлю даних).

Особисто я вважаю за краще, щоб рядки не були нульовими за замовчуванням, а замість цього лише нульовими, якщо ми оголосили "рядок?". Хоча, можливо, це не здійснене чи логічне на більш глибокому рівні; не впевнений.



0

Я думаю, що немає причин не використовувати null для непризначеного (або в цьому місці в потоці програми не зустрічається) значення. Якщо ви хочете розрізнити, є == null. Якщо ви просто хочете перевірити певне значення і не байдуже, чи це нульове чи щось інше, String.Equals ("XXX", MyStringVar) справляється чудово.

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