Так как true
не является строковым типом, как null + true
строка?
string s = true; //Cannot implicitly convert type 'bool' to 'string'
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'
В чем причина этого?
Так как true
не является строковым типом, как null + true
строка?
string s = true; //Cannot implicitly convert type 'bool' to 'string'
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'
В чем причина этого?
Необычно, как это может показаться, просто следуя правилам спецификации языка С#.
Из раздела 7.3.4:
Операция вида x op y, где op - перегружаемый двоичный оператор, x - выражение типа X, а y - выражение типа Y, обрабатывается следующим образом:
- Определяется набор пользовательских определяемых пользователем операторов, предоставляемых X и Y для операционного оператора op (x, y). Набор состоит из объединения операторов-кандидатов, предоставленных X, и операторов-кандидатов, предоставленных Y, каждый из которых определяется с использованием правил §7.3.5. Если X и Y являются одним и тем же типом, или если X и Y получены из общего базового типа, то совлокальные операторы-кандидаты встречаются только в объединенном наборе один раз.
- Если набор пользовательских определяемых пользователем операторов не пуст, это становится набором операторов-кандидатов для операции. В противном случае предопределенные бинарные операционные операционные операции, включая их снятые формы, становятся набором операторов-кандидатов для операции. Предопределенные реализации данного оператора указаны в описании оператора (§7.8 - §7.12).
- Правила разрешения перегрузки в §7.5.3 применяются к набору операторов-кандидатов для выбора наилучшего оператора относительно списка аргументов (x, y), и этот оператор становится результатом процесса разрешения перегрузки. Если разрешение перегрузки не позволяет выбрать один лучший оператор, возникает ошибка времени привязки.
Итак, пройдите через это по очереди.
X - это нулевой тип здесь - или вообще не тип, если вы хотите об этом думать. Он не предоставляет никаких кандидатов. Y - bool
, который не предоставляет никаких пользовательских операторов +
. Таким образом, на первом этапе не найдены пользовательские операторы.
Затем компилятор переходит во вторую маркерную точку, просматривая предопределенные бинарные операторы + реализации и их отмененные формы. Они перечислены в разделе 7.8.4 спецификации.
Если вы просматриваете эти предопределенные операторы, единственное, что применимо, это string operator +(string x, object y)
. Таким образом, набор кандидатов имеет одну запись. Это делает последнюю точку пролета очень простой... разрешение перегрузки выбирает этот оператор, давая общий тип выражения string
.
Интересным моментом является то, что это произойдет, даже если существуют другие пользовательские операторы, доступные для неперечисленных типов. Например:
// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;
Это хорошо, но он не используется для нулевого литерала, потому что компилятор не знает, что посмотреть в Foo
. Он знает только string
, потому что это предопределенный оператор, явно указанный в спецификации. (На самом деле это не оператор, определяемый строковым типом... 1). Это означает, что это не скомпилируется:
// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;
Другие типы второго операнда будут использовать некоторые другие операторы, конечно:
var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>
1 Вам может быть интересно, почему нет оператора string+. Это разумный вопрос, и я только догадываюсь об ответе, но рассмотрю это выражение:
string x = a + b + c + d;
Если string
не имеет специальной оболочки в компиляторе С#, это будет эффективно:
string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;
Таким образом, создаются две ненужные промежуточные строки. Однако, поскольку в компиляторе есть специальная поддержка, он действительно может скомпилировать вышеуказанное как:
string x = string.Concat(a, b, c, d);
который может создать только одну строку точно соответствующей длины, скопировав все данные ровно один раз. Ницца.
Причина в том, что, когда вы вводите +
, тогда действуют правила привязки операторов С#. Он рассмотрит набор доступных операторов +
и выберите наилучшую перегрузку. Одним из этих операторов является следующий
string operator +(string x, object y)
Эта перегрузка совместима с типами аргументов в выражении null + true
. Следовательно, он выбирается как оператор и оценивается как существенно ((string)null) + true
, который оценивает значение "True"
.
В разделе 7.7.4 спецификации языка С# содержатся сведения об этом разрешении.
Компилятор отправляется на охоту за оператором +(), который может сначала принять нулевой аргумент. Ни один из стандартных типов значений не подходит, значение null для них не является допустимым. Единственное совпадение - System.String.operator +(), там нет двусмысленности.
Второй аргумент этого оператора также является строкой. Это идет kapooey, не может неявно преобразовать bool в строку.
Интересно, что используя Reflector для проверки того, что генерируется, следующий код:
string b = null + true;
Console.WriteLine(b);
преобразуется в это компилятором:
Console.WriteLine(true);
Обоснование этой "оптимизации" немного странно, я должен сказать, и не рифмуется с выбором оператора, которого я ожидал бы.
Кроме того, следующий код:
var b = null + true;
var sb = new StringBuilder(b);
преобразуется в
string b = true;
StringBuilder sb = new StringBuilder(b);
где string b = true;
на самом деле не принимается компилятором.
null
будет сбрасываться в пустую строку, и есть неявный конвертер из bool в строку, поэтому true
будет передан в строку, а затем будет применен оператор +
: он как: string str = "" + true.ToString();
если вы проверите его с помощью Ildasm:
string str = null + true;
это ниже:
.locals init ([0] string str)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: box [mscorlib]System.Boolean
IL_0007: call string [mscorlib]System.String::Concat(object)
IL_000c: stloc.0
Причиной этого является удобство (конкатенация строк является общей задачей).
Как сказал BoltClock, оператор "+" определен по числовым типам, строкам и может быть определен и для наших собственных типов (перегрузка оператора).
Если в типах аргументов нет перегруженного оператора "+", и они не являются числовыми типами, компилятор по умолчанию использует конкатенацию строк.
Компилятор вставляет вызов String.Concat(...)
при объединении с помощью '+', а реализация Concat вызывает ToString для каждого переданного в него объекта.
var b = (null + DateTime.Now); // String
var b = (null + 1); // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type
Сумасшедшие?? Нет, должна быть причина.
Кто-то звонит Eric Lippert
...