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

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

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

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

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

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

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

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

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

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

Может ли кто-нибудь описать, почему компилятор не позволяет этого и почему разработчики С# решили сделать это? Я считаю, что это законно на Java... Хотя я этого не подтвердил.

Спасибо.

EDIT: Я прошу понять, почему Java и С# обрабатывают это по-другому, что происходит под сценами на С#, которые делают это недопустимым. Я знаю, как использовать тернар, и я не ищу "лучший способ" для кода примеров. Я понимаю правила тройного в С#, но я хочу знать, ПОЧЕМУ...

РЕДАКТИРОВАТЬ (Jon Skeet): Удален тег "autoboxing", так как в этом вопросе не участвует ни один бокс.

Ответ 1

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

EDIT: Похоже, что это действительно законно на Java. Довольно, как это работает, что делать, когда дело доходит до перегрузки метода, я не уверен... Я только что посмотрел на JLS, и это очень неясно, какой тип условности есть, когда есть две несовместимые ссылки типы. С# способ работы может быть более раздражающим иногда, но он более ясный IMO.

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

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

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

Ответ 2

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

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

Пока string может быть null, он не может быть DBNull.

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

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

[condition] ? true value : false value;

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

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

string item = "item";

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

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

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

Ответ 3

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

object result = (object)foo ?? DBNull.Value;