Почему этот код недействителен в С#?
Следующий код не будет компилироваться:
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;