Почему этот код недействителен в C #? - PullRequest
39 голосов
/ 14 октября 2008

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

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;

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

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

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

Спасибо.

РЕДАКТИРОВАТЬ: Я прошу понять, почему Java и C # обрабатывают это по-разному, что происходит под сценами в C #, которые делают это недопустимым. Я знаю, как использовать троичный, и я не ищу «лучший способ» для кодирования примеров. Я понимаю троичные правила в C #, но я хочу знать, ПОЧЕМУ ...

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

Ответы [ 3 ]

68 голосов
/ 14 октября 2008

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

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

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

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

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

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 # каждое присваивание также является оператором с возвращаемым значением, поэтому, хотя конструкция C # 3 является новой в C # 3, оператор справа от равенства всегда должен разрешаться в один тип.

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

10 голосов
/ 14 октября 2008

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

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