Это действительный код С#
var bob = "abc" + null + null + null + "123"; // abc123
Это недопустимый код С#
var wtf = null.ToString(); // compiler error
Почему допустимо первое утверждение?
Это действительный код С#
var bob = "abc" + null + null + null + "123"; // abc123
Это недопустимый код С#
var wtf = null.ToString(); // compiler error
Почему допустимо первое утверждение?
Причина первой работы:
От MSDN:
В операциях конкатенации строк компилятор С# обрабатывает пустую строку так же, как пустую строку, но не преобразует значение исходной нулевой строки.
Дополнительная информация о + двоичном операторе:
Оператор binary + выполняет конкатенацию строк, когда один или оба операнда имеют строку типа.
Если операнд конкатенации строк равен нулю, будет заменена пустая строка. В противном случае любой нестроковый аргумент преобразуется в его строковое представление, вызывая виртуальный метод
ToString
, унаследованный от объекта типа.Если
ToString
возвращаетnull
, будет заменена пустая строка.
Причина ошибки во втором:
null (ссылка на С#) - Ключевое слово null - это литерал, представляющий нулевую ссылку, которая не относится ни к одному объекту. Значение null является значением по умолчанию для переменных ссылочного типа.
Поскольку оператор +
в С# внутренне переводит на 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
?).
первый образец будет переведен в:
var bob = String.Concat("abc123", null, null, null, "abs123");
Метод Concat
проверяет ввод и перевод null как пустую строку
второй образец будет переведен в:
var wtf = ((object)null).ToString();
Итак, здесь будет генерировано ссылочное исключение null
Первая часть вашего кода просто обрабатывается как в 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()
' - это метод, который может быть вызван экземпляром объекта, но не литералом.
В среде 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
как пустую строку.
'+'
является оператором инфикса. Как и любой оператор, он действительно вызывает метод. Вы можете представить себе версию без инфикса "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()
.;)
Я предполагаю, что это литерал, который не относится ни к одному объекту. ToString()
требуется object
.
Кто-то сказал в этой дискуссии, что вы не можете сделать строку из ничего. (что является хорошей фразой, как я думаю). Но да - вы можете:-), как показано в следующем примере:
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
.
Добавление null
в строку просто игнорируется. null
(во втором примере) не является экземпляром какого-либо объекта, поэтому он даже не имеет метода ToString()
. Это буквально.
Потому что нет никакой разницы между string.Empty
и null
, когда вы объединяете строки.
Вы также можете передать значение null в string.Format
. Но вы пытаетесь вызвать метод на null
, который всегда будет приводить к NullReferenceException
и, следовательно, генерирует ошибку компилятора.
Если по какой-то причине вы действительно хотите это сделать, вы можете написать метод расширения, который проверяет на null
, а затем возвращает string.Empty
. Но такое расширение должно использоваться только тогда, когда оно абсолютно необходимо (по-моему).
Как правило: он может или не может принимать значение null в качестве параметра в зависимости от спецификации, но всегда недействительно вызывать метод с нулевым значением.
Это и другая тема, почему операнды + могут быть пустыми в случае строк. Это своего рода вещь VB (извините, ребята), чтобы облегчить программистам жизнь или предположить, что программист не может иметь дело с нулями. Я полностью не согласен с этой спецификацией. "неизвестно" + "все" должно быть "неизвестно"...