Есть ли какие-либо веские причины, по которым троичные числа в C # ограничены?

StackOverflow https://stackoverflow.com/questions/1669492

  •  13-09-2019
  •  | 
  •  

Вопрос

Терпит неудачу:

object o = ((1==2) ? 1 : "test");

Преуспевает:

object o;
if (1 == 2)
{
    o = 1;
}
else
{
    o = "test";
}

Ошибка в первом утверждении заключается в следующем:

Тип условного выражения не может быть определен, поскольку нет неявного преобразования между 'int' и 'string'.

Хотя зачем это нужно, я присваиваю эти значения переменной типа object.

Редактировать: Приведенный выше пример тривиален, да, но есть примеры, когда это было бы весьма полезно:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = ((subscriptionID == null) ? DBNull.Value : subscriptionID),
}
Это было полезно?

Решение

использование:

object o = ((1==2) ? (object)1 : "test");

Проблема заключается в том, что возвращаемый тип условного оператора не может быть определен однозначно.То есть, между int и string нет лучшего выбора.Компилятор всегда будет использовать тип истинного выражения и при необходимости неявно приводить выражение false.

Редактировать: В вашем втором примере:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = subscriptionID.HasValue ? (object)subscriptionID : DBNull.Value,
}

PS:
Это не называется "троичным оператором". IT является троичный оператор, но он называется "условным оператором".

Другие советы

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

Во-первых, желательно, чтобы как можно больше выражений имели однозначный тип, который может быть определен исключительно по содержимому выражения.Это желательно по нескольким причинам.Например:это значительно упрощает создание движка IntelliSense.Вы печатаете x.M(some-expression. и IntelliSense должен уметь анализировать некоторое выражение, определите его тип и создайте выпадающий список ДО того, как IntelliSense узнает, к какому методу относится x.M.IntelliSense не может точно знать, на что ссылается x.M, если M перегружен, пока не увидит все аргументы, но вы еще не ввели даже первый аргумент.

Во-вторых, мы предпочитаем, чтобы информация о типе передавалась "изнутри наружу" именно из-за сценария, о котором я только что упомянул:разрешение перегрузки.Рассмотрим следующее:

void M(object x) {}
void M(int x) {}
void M(string x) {}
...
M(b ? 1 : "hello");

Что это должно делать?Должно ли это вызывать перегрузку объекта?Должен ли он иногда вызывать перегрузку строки, а иногда вызывать перегрузку int?Что, если бы у вас была еще одна перегрузка, скажем M(IComparable x) -- когда ты его выбираешь?

Все становится очень сложно, когда информация о типе "течет в обе стороны".Высказывание "Я присваиваю эту вещь переменной типа object, поэтому компилятор должен знать, что можно выбирать object в качестве типа" не стирает;часто бывает так, что мы не знаем тип переменной, которой вы присваиваете значение, потому что это то, что мы пытаемся выяснить в процессе.Разрешение перегрузки - это именно процесс определения типов параметров, которые являются переменными, которым вы присваиваете аргументы, на основе типов аргументов.Если типы аргументов зависят от типов, которым они присваиваются, то в наших рассуждениях наблюдается цикличность.

Информация о типе "передается в обе стороны" для лямбда-выражений;эффективная реализация этой идеи заняла у меня большую часть года.Я написал длинную серию статей, описывающих некоторые трудности при разработке и реализации компилятора, который может выполнять анализ, при котором информация о типах преобразуется в сложные выражения на основе контекста, в котором возможно используется выражение;часть первая находится здесь:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx

Вы могли бы сказать: "ну, хорошо, я понимаю, почему тот факт, что я присваиваю object , не может быть безопасно использован компилятором, и я понимаю, почему для выражения необходимо иметь однозначный тип, но почему тип выражения не является object, поскольку и int, и string преобразуются в object?" Это подводит меня к моему третьему пункту:

В-третьих, один из тонких, но последовательно применяемых принципов проектирования C # - "не создавайте типы с помощью магии".Когда дается список выражений, из которых мы должны определить тип, тип, который мы определяем, всегда где-то есть в списке.Мы никогда не придумываем что-то новое и не выбираем его за вас;тип, который вы получаете, всегда тот, который вы предоставили нам на выбор.Если вы говорите найти лучший тип в наборе типов, мы находим лучший тип В этом наборе типов.В наборе {int, string} нет наилучшего общего типа, который есть, скажем, в "Animal, Turtle, Mammal, Wallaby".Это проектное решение применимо к условному оператору, к сценариям унификации вывода типов, к выводу неявно типизированных типов массивов и так далее.

Причина такого конструкторского решения заключается в том, что обычным людям легче понять, что компилятор собирается делать в любой заданной ситуации, когда должен быть определен наилучший тип;если вы знаете, что будет выбран типаж, который находится прямо здесь, глядя вам в лицо, тогда намного легче понять, что произойдет.

Это также избавляет нас от необходимости разрабатывать множество сложных правил о том, какой тип является наиболее распространенным из набора типов, когда возникают конфликты.Предположим, у вас есть типы {Foo, Bar}, где оба класса реализуют IBlah, и оба класса наследуются от Baz.Какой наиболее распространенный тип, IBlah, который реализуют оба, или Baz, который оба расширяют?Мы не хотим отвечать на этот вопрос;мы хотим полностью избежать этого.

Наконец, я отмечаю, что компилятор C # на самом деле слегка ошибается в определении типов в некоторых неясных случаях.Моя первая статья об этом находится здесь:

http://blogs.msdn.com/ericlippert/archive/2006/05/24/type-inference-woes-part-one.aspx

Можно утверждать, что на самом деле компилятор делает это правильно, а спецификация неправильная;дизайн реализации, на мой взгляд, лучше, чем стандартный дизайн.

В любом случае, это всего лишь несколько причин для разработки этого конкретного аспекта троичного оператора.Здесь есть и другие тонкости, например, как верификатор CLR определяет, гарантированно ли данный набор ветвящихся путей оставляет правильный тип в стеке во всех возможных путях.Подробное обсуждение этого вопроса завело бы меня довольно далеко.

Почему функция X работает именно так, часто бывает очень сложно ответить на вопрос.Гораздо проще ответить на фактическое поведение.

Мое обоснованное предположение, почему.Условному оператору разрешено лаконично и лаконично использовать логическое выражение для выбора между двумя связанными значениями.Они должны быть связаны, поскольку используются в одном месте.Если вместо этого пользователь выбирает 2 несвязанных значения, возможно, в коде есть тонкая опечатка/ошибка, и компилятору лучше предупредить их об этом, а не неявно приводить к объекту.Чего они, возможно, не ожидали.

«int» — это примитивный тип, а не объект, а «строка» считается скорее «примитивным объектом».Когда вы делаете что-то вроде «object o = 1», вы фактически помещаете «int» в «Int32».Вот ссылка на статью о боксе:

http://msdn.microsoft.com/en-us/magazine/cc301569.aspx

Как правило, следует избегать бокса из-за потери производительности, которую трудно отследить.

Когда вы используете троичное выражение, компилятор вообще не смотрит на переменную присваивания, чтобы определить окончательный тип.Чтобы разбить исходный оператор на то, что делает компилятор:

Заявление:объект о = ((1==2) ?1:"тест");

Компилятор:

  1. Каковы типы «1» и «тест» в '((1==2) ?1:"тест")'?Они совпадают?
  2. Соответствует ли последний тип из № 1 типу оператора присваивания для объекта o?

Поскольку компилятор не вычисляет #2, пока не будет выполнен #1, он терпит неудачу.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top