Почему этот код недействителен в C#?

Следующий код не будет компилироваться:

string foo = "bar";
Object o = foo == null ? DBNull.Value : foo;

Я получаю: Ошибка 1 Тип условного выражения не может быть определен, потому что не существует неявного преобразования между "System.DBNull" и "строка"

Чтобы это исправить, я должен сделать что-то вроде этого:

string foo = "bar";
Object o = foo == null ? DBNull.Value : (Object)foo;

Этот бросок кажется бессмысленным, поскольку это, безусловно, законно:

string foo = "bar";
Object o = foo == null ? "gork" : foo;

Мне кажется, что когда троичные ветви имеют разные типы, компилятор не будет автоматически помещать значения в объект типа... но когда они имеют один и тот же тип, то автоматическая блокировка выполняется автоматически.

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

Can anyone describe why the compiler does not allow this and why the designers of C# chose to do this? I believe this is legal in Java...Though I have not verified this.

Благодарю.

EDIT: I am asking for an understanding of why Java and C# handle this differently, what is going on underneath the scenes in C# that make this invalid. I know how to use ternary, and am not looking for a "better way" to code the examples. I understand the rules of ternary in C#, but I want to know WHY...

EDIT (Jon Skeet): Removed "autoboxing" tag as no boxing is involved in this question.

3 ответа

Компилятор требует, чтобы либо типы второго и третьего операндов были одинаковыми, либо чтобы один неявно преобразовывался в другой. В вашем случае это DBNull и string, ни один из которых неявно конвертируется в другой. Приведение любого из них к объекту решает это.

РЕДАКТИРОВАТЬ: Похоже, это действительно законно в Java. Как именно получается, что делать, когда дело доходит до перегрузки методов, я не уверен... Я только что посмотрел на JLS, и совершенно неясно, что это за тип условия, когда есть две несовместимые ссылки типы участвуют. Способ работы в C# может иногда вызывать больше раздражения, но он более понятен.

Соответствующий раздел спецификации C# 3.0 - 7.13, условный оператор:

Второй и третий операнды оператора?: Управляют типом условного выражения. Пусть X и Y будут типами второго и третьего операндов. Затем,

  • Если X и Y одного типа, то это тип условного
  • В противном случае, если неявное преобразование (§6.1) существует из X в Y, но не из Y в X, тогда Y является типом условного выражения.
  • В противном случае, если неявное преобразование (§6.1) существует из Y в X, но не из X в Y, тогда X является типом условного выражения.
  • В противном случае тип выражения не может быть определен, и возникает ошибка времени компиляции.

DBNull.Value возвращает тип DBNull,

Вы хотите, чтобы тип был string,

В то время как string может быть null это не может быть DBNull,

В вашем коде оператор справа от равенства выполняется перед присваиванием объекту.

В основном, если вы используете:

[condition] ? true value : false value;

В.Net опции true и false должны быть неявно преобразованы в один и тот же тип, прежде чем вы их назначите.

Это результат того, как C# работает с безопасностью типов. Например, действует следующее:

string item = "item";

var test = item != null ? item : "BLANK";

C#3 не поддерживает динамические типы, так что же такое тест? В C# каждое присваивание также является оператором с возвращаемым значением, поэтому, хотя var Конструкция является новой в C#3, оператор справа от равенства всегда должен разрешаться в один тип.

В C#4 и выше вы можете явно поддерживать динамические типы, но я не думаю, что это поможет здесь.

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

object result = (object)foo ?? DBNull.Value;
Другие вопросы по тегам