Почему значение по умолчанию для типа string null вместо пустой строки?

Довольно трудно проверить все мои строки на null, прежде чем я могу безопасно применять такие методы, как ToUpper(), StartWith() и т.д.

Если значение по умолчанию string было пустой строкой, мне не пришлось бы тестировать, и я бы чувствовал, что он более согласуется с другими типами значений, например, int или double. Дополнительно Nullable<String> имеет смысл.

Итак, почему дизайнеры С# решили использовать null как значение по умолчанию для строк?

Примечание. Это относится к этому вопросу, но больше сосредоточено на том, почему вместо того, что с ним делать.

Ответ 1

Почему значение по умолчанию для типа string null вместо пустого строка?

Поскольку string является ссылочным типом , а значением по умолчанию для всех типов ссылок является null.

Это довольно раздражает, чтобы проверить все мои строки на null, прежде чем я смогу безопасно применять методы, такие как ToUpper(), StartWith() и т.д.

Это согласуется с поведением ссылочных типов. Перед вызовом своих членов экземпляра следует поместить чек на нулевую ссылку.

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

Назначение значения по умолчанию для определенного ссылочного типа, отличного от null, сделает его несогласованным.

Дополнительно Nullable<String> имеет смысл.

Nullable<T> работает со значениями типов. Следует отметить, что Nullable не был введен на исходной платформе . NET, поэтому было бы много сломанного кода, если бы они изменились это правило. (Courtesy @jcolebrand)

Ответ 2

Habib прав - потому что string является ссылочным типом.

Но что более важно, вам не нужно проверять null каждый раз, когда вы его используете. Вероятно, вы должны выбросить ArgumentNullException, если кто-то передает вашу функцию в ссылку null.

Здесь вещь - фреймворк выкинул бы NullReferenceException для вас в любом случае, если бы вы попытались вызвать .ToUpper() в строке. Помните, что этот случай все равно может произойти, даже если вы проверите свои аргументы для null, поскольку любое свойство или метод объектов, переданных вашей функции, поскольку параметры могут оцениваться как null.

Говоря, что проверка пустых строк или нулей - это обычная вещь, поэтому они обеспечивают String.IsNullOrEmpty() и String.IsNullOrWhiteSpace() для этой цели.

Ответ 3

Вы можете написать метод расширения (для чего это стоит):

public static string EmptyNull(this string str)
{
    return str ?? "";
}

Теперь это безопасно работает:

string str = null;
string upper = str.EmptyNull().ToUpper();

Ответ 4

Вы также можете использовать следующее, начиная с С# 6.0

string myString = null;
string result = myString?.ToUpper();

Строка результата будет нулевой.

Ответ 5

Пустые строки и нули принципиально разные. Нуль - это отсутствие значения, а пустая строка - это пустое значение.

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

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

Другой случай, когда это будет проблемой, - это когда строка является возвращаемым значением из некоторой функции. Поскольку строка является ссылочным типом и может технически иметь значение как null и пустое, поэтому функция также может технически возвращать нулевой или пустой (нет ничего, чтобы остановить его от этого). Теперь, поскольку есть два понятия "отсутствия значения", то есть пустая строка и нуль, весь код, который потребляет эту функцию, должен будет выполнить 2 проверки. Один для пустого, а другой для null.

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

https://softwareengineering.stackexchange.com/questions/32578/sql-empty-string-vs-null-value

NULL vs Empty при работе с пользовательским вводом

Ответ 6

Основная причина/проблема заключается в том, что разработчики спецификации CLS (которая определяет, как языки взаимодействуют с .net) не определяют средства, с помощью которых члены класса могли указывать, что они должны вызываться напрямую, а не через callvirt, без звонка, выполняющего проверку нулевой ссылки; и это не создавало много определяющих структур, которые не подлежали бы "нормальному" боксу.

Если бы спецификация CLS определяла такое средство, тогда было бы возможно, чтобы .net последовательно следил за ходом, установленным Общей моделью объектов (COM), под которым ссылка на нулевую строку считалась семантически эквивалентной пустой строке, и для других определяемых пользователем неизменяемых типов классов, которые должны иметь семантику значений, чтобы также определять значения по умолчанию. По существу, что произойдет для каждого члена String, например. Length для записи как нечто вроде [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }. Такой подход обеспечил бы очень приятную семантику для вещей, которые должны вести себя как ценности, но из-за проблем с реализацией необходимо хранить в куче. Самая большая трудность в этом подходе заключается в том, что семантика преобразования между такими типами и Object может быть немного мутной.

Альтернативный подход состоял бы в том, чтобы разрешить определение типов специальных структур, которые не наследовали от Object, а вместо этого выполняли пользовательские операции по боксу и распаковке (которые могли бы конвертироваться в/из какого-либо другого типа класса). При таком подходе будет существовать тип класса NullableString, который ведет себя как строка, и тип struct String с произвольным ядром, который будет содержать одно частное поле Value типа String. Попытка конвертировать a String в NullableString или Object возвращает Value, если не null, или String.Empty, если null. Пытаясь применить к String, ненулевая ссылка на экземпляр NullableString сохранит ссылку в Value (возможно, сохранит нуль, если длина равна нулю); литье любой другой ссылки приведет к исключению.

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

Ответ 7

Поскольку строковая переменная является ссылкой, а не экземпляром.

Инициализация его пустым по умолчанию была бы возможна, но это привело бы к множеству несоответствий по всем направлениям.

Ответ 8

Почему разработчики С# решили использовать null в качестве значения по умолчанию строки?

Поскольку строки являются ссылочными типами, ссылочные типы по умолчанию - null. Переменные ссылочных типов хранят ссылки на фактические данные.

Используйте default ключевое слово для этого случая;

string str = default(string); 

str - это string, поэтому он является ссылочным типом, поэтому значение по умолчанию null.

int str = (default)(int);

str - это int, поэтому это тип значения, поэтому значение по умолчанию zero.

Ответ 9

Если значение по умолчанию string было пустой строкой, мне не пришлось бы тестировать

Неправильно! Изменение значения по умолчанию не меняет того факта, что это ссылочный тип, и кто-то еще может явно указывать ссылку null.

Дополнительно Nullable<String> будет иметь смысл.

Верная точка. Было бы разумнее не допускать null для любых ссылочных типов, вместо этого требуя Nullable<TheRefType> для этой функции.

Итак, почему дизайнеры С# решили использовать null как значение по умолчанию для строк?

Согласованность с другими ссылочными типами. Теперь зачем вообще разрешать null в ссылочных типах? Вероятно, таким образом, что он чувствует, как C, несмотря на то, что это сомнительное дизайнерское решение на языке, который также предоставляет Nullable.

Ответ 10

Возможно, если вы используете оператор ?? при назначении вашей строковой переменной, это может вам помочь.

string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.

Ответ 11

A String - неизменный объект, который означает, что при заданном значении старое значение не может быть стерто из памяти, но остается в старом местоположении, а новое значение помещается в новое место. Поэтому, если значение по умолчанию String a было String.Empty, оно потеряло бы блок String.Empty в памяти, когда ему было присвоено его первое значение.

Хотя это кажется незначительным, это может стать проблемой при инициализации большого массива строк со значениями по умолчанию String.Empty. Конечно, вы всегда можете использовать изменяемый класс StringBuilder, если это будет проблемой.

Ответ 12

Так как строка является ссылочным типом, а значение по умолчанию для ссылочного типа равно null.

Ответ 13

Возможно, ключевое слово string запутало вас, поскольку оно выглядит точно так же, как любое другое объявление типа значения, но на самом деле это псевдоним System.String, как описано в этот вопрос.
Также темно-синий цвет в Visual Studio и строчной первой букве может ввести в заблуждение, считая, что это struct.

Ответ 14

Неверные типы не появлялись до 2.0.

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

Многие люди говорят о типах ref-type или not ref, но строка является обычным классом, и было бы установлено, что решения будут доступны.

Ответ 15

Я делаю следующее:

class myPOCOClass
{
   private string _item1="";
   public string item1
  {
       get
       {
         return (_item1==null?"":_item1);
        }
       set{
           _item1=value;
       }
  }
}