Почему это допустимо для конкатенации нулевых строк, но не для вызова "null.ToString()"?

Это действительный код С#

var bob = "abc" + null + null + null + "123";  // abc123

Это недопустимый код С#

var wtf = null.ToString(); // compiler error

Почему допустимо первое утверждение?

Ответ 1

Причина первой работы:

От MSDN:

В операциях конкатенации строк компилятор С# обрабатывает пустую строку так же, как пустую строку, но не преобразует значение исходной нулевой строки.

Дополнительная информация о + двоичном операторе:

Оператор binary + выполняет конкатенацию строк, когда один или оба операнда имеют строку типа.

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

Если ToString возвращает null, будет заменена пустая строка.

Причина ошибки во втором:

null (ссылка на С#) - Ключевое слово null - это литерал, представляющий нулевую ссылку, которая не относится ни к одному объекту. Значение null является значением по умолчанию для переменных ссылочного типа.

Ответ 2

Поскольку оператор + в С# внутренне переводит на String.Concat, что является статическим методом. И этот метод используется для обработки null как пустой строки. Если вы посмотрите на источник String.Concat в Reflector, вы увидите следующее:

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDN также упоминает об этом: http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx)

С другой стороны, ToString() - это метод экземпляра, который вы не можете вызвать null (какой тип следует использовать для null?).

Ответ 3

первый образец будет переведен в:

var bob = String.Concat("abc123", null, null, null, "abs123");

Метод Concat проверяет ввод и перевод null как пустую строку

второй образец будет переведен в:

var wtf = ((object)null).ToString();

Итак, здесь будет генерировано ссылочное исключение null

Ответ 4

Первая часть вашего кода просто обрабатывается как в String.Concat,

что вызывает компилятор С# при добавлении строк. "abc" + null переводится на String.Concat("abc", null),

и внутренне, этот метод заменяет null на String.Empty. Итак, почему ваша первая часть кода не вызывает никаких исключений. это похоже на

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

И во второй части вашего кода генерируется исключение, потому что "null" не является объектом, ключевое слово null - это литерал, представляющий нулевую ссылку, которая не относится ни к одному объекту. Значение null является значением по умолчанию для переменных ссылочного типа.

И 'ToString()' - это метод, который может быть вызван экземпляром объекта, но не литералом.

Ответ 5

В среде COM, которая предшествовала .net, она была необходима для любой процедуры, которая получила строку, чтобы освободить ее, когда она была выполнена с ней. Поскольку очень часто пустые строки передавались и выходили из подпрограмм, а потому, что попытка "освободить" нулевой указатель была определена как законная операция do-nothing, Microsoft решила, что пустой указатель строки представляет собой пустую строку.

Чтобы обеспечить некоторую совместимость с COM, многие процедуры в .net интерпретируют нулевой объект как юридическое представление как пустую строку. С несколькими небольшими изменениями .net и его языками (в первую очередь, чтобы члены экземпляра указывали "не вызывать как виртуальные" ), Microsoft могла бы сделать объекты null объявленного типа String более похожими на пустые строки. Если бы Microsoft сделала это, ему также пришлось бы сделать работу Nullable<T> несколько иначе (чтобы позволить Nullable<String> - то, что они должны были ИМХО сделать в любом случае) и/или определить тип NullableString, который будет в основном взаимозаменяемый с String, но который не будет рассматривать null как допустимую пустую строку.

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

Ответ 6

'+' является оператором инфикса. Как и любой оператор, он действительно вызывает метод. Вы можете представить себе версию без инфикса "wow".Plus(null) == "wow"

Разработчик решил что-то вроде этого...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

Итак, ваш пример станет

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

что совпадает с

var bob = "abc".Plus("123");  // abc123

Ни в коем случае ничто не становится строкой. Итак, null.ToString() не отличается от null.VoteMyAnswer().;)

Ответ 7

Я предполагаю, что это литерал, который не относится ни к одному объекту. ToString() требуется object.

Ответ 8

Кто-то сказал в этой дискуссии, что вы не можете сделать строку из ничего. (что является хорошей фразой, как я думаю). Но да - вы можете:-), как показано в следующем примере:

var x = null + (string)null;     
var wtf = x.ToString();

работает отлично и вообще не генерирует исключение. Единственное различие заключается в том, что вам нужно передать одно из нулей в строку - если вы удалите (string), тогда пример все еще компилируется, но генерирует исключение во время выполнения: "Operator" + 'является неоднозначным в операндах типа' <null> ' и '<null> ' ".

NB В приведенном выше примере кода значение x не является нулевым, как вы могли ожидать, это фактически пустая строка после того, как вы ввели один из операндов в строку.


Еще один интересный факт: в С#/.NET метод null обрабатывается не всегда один и тот же, если вы рассматриваете разные типы данных. Например:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

Обратитесь к 1-й строке фрагмента кода: Если x является переменной с нулевым значением (т.е. int?), содержащей значение 1, вы получаете результат <null> назад. Если это строка (как показано в комментарии) со значением "1", вы получаете "1" назад, а не <null>.

N.B. Также интересно: если вы используете var x = 1; для первой строки, вы получаете ошибку времени выполнения. Зачем? Поскольку присваивание превратит переменную x в тип данных int, который не является нулевым. Компилятор здесь не принимает int? и, следовательно, не работает во второй строке, где добавляется null.

Ответ 9

Добавление null в строку просто игнорируется. null (во втором примере) не является экземпляром какого-либо объекта, поэтому он даже не имеет метода ToString(). Это буквально.

Ответ 10

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

Ответ 11

Как правило: он может или не может принимать значение null в качестве параметра в зависимости от спецификации, но всегда недействительно вызывать метод с нулевым значением.


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