Я новичок в C #, и у меня возникла проблема. При работе с тернарным оператором ( ? :
) существует разница между C # и Java .
Почему в следующем фрагменте кода не работает 4-я строка? Компилятор показывает сообщение об ошибке there is no implicit conversion between 'int' and 'string'
. 5-я строчка тоже не работает. Оба List
объекта, не так ли?
int two = 2;
double six = 6.0;
Write(two > six ? two : six); //param: double
Write(two > six ? two : "6"); //param: not object
Write(two > six ? new List<int>() : new List<string>()); //param: not object
Однако на Java работает тот же код:
int two = 2;
double six = 6.0;
System.out.println(two > six ? two : six); //param: double
System.out.println(two > six ? two : "6"); //param: Object
System.out.println(two > six ? new ArrayList<Integer>()
: new ArrayList<String>()); //param: Object
Какая языковая функция в C # отсутствует? Если есть, то почему не добавляется?
int
это не объект. Это примитивно.ArrayList<String>
иArrayList<Integer>
стало толькоArrayList
в байткод. Это означает, что они выглядят во время выполнения одного и того же типа . Видимо в C # они разных типов.Ответы:
Просматривая раздел 7.14 Спецификации языка C # 5: Условный оператор, мы можем увидеть следующее:
Другими словами: он пытается определить, могут ли x и y быть преобразованы друг в друга, и если нет, возникает ошибка компиляции. В нашем случае ,
int
иstring
не имеют никакого явного или неявного преобразования , поэтому он не будет компилироваться.Сравните это с разделом 15.25 спецификации языка Java 7: Условный оператор :
И, глядя на раздел 15.12.2.7. Вывод аргументов типа На основе фактических аргументов мы видим, что он пытается найти общего предка, который будет служить типом, используемым для вызова, с которым он приземляется
Object
.Object
- приемлемый аргумент, поэтому вызов будет работать.источник
Данные ответы хороши; Я бы добавил к ним, что это правило C # является следствием более общих рекомендаций по дизайну. Когда его просят вывести тип выражения из одного из нескольких вариантов, C # выбирает уникальное лучшее из них . То есть, если вы дадите C # несколько вариантов выбора, например «Жираф, Млекопитающее, Животное», тогда он может выбрать наиболее общий - Животное - или наиболее конкретный - Жираф - в зависимости от обстоятельств. Но он должен выбрать один из фактически предоставленных ему вариантов . C # никогда не говорит: «Я выбираю между Cat и Dog, поэтому я сделаю вывод, что Animal - лучший выбор». Это не было возможностью выбора, поэтому C # не может его выбрать.
В случае тернарного оператора C # пытается выбрать более общий тип int и string, но ни один из них не является более общим типом. Вместо того, чтобы выбирать тип, который изначально не был выбран, например, object, C # решает, что никакой тип не может быть выведен.
Отмечу также, что это соответствует другому принципу проектирования C #: если что-то не так, сообщите об этом разработчику. Язык не говорит: «Я угадаю, что вы имели в виду, и продолжу путаться, если смогу». Язык гласит: «Я думаю, вы написали здесь что-то непонятное, и я собираюсь вам об этом рассказать».
Также отмечу, что C # не ведет от переменной к присвоенному значению , а наоборот. C # не говорит: «вы назначаете объектной переменной, поэтому выражение должно быть конвертируемым в объект, поэтому я позабочусь об этом». Скорее, C # говорит: «это выражение должно иметь тип, и я должен иметь возможность сделать вывод, что этот тип совместим с объектом». Поскольку выражение не имеет типа, возникает ошибка.
источник
int? b = (a != 0 ? a : (int?) null)
. Также есть этот вопрос вместе со всеми связанными вопросами сбоку. Если вы продолжите переходить по ссылкам, их довольно много. Для сравнения, я никогда не слышал, чтобы кто-то сталкивался с реальной проблемой, связанной с тем, как это делает Java.Что касается дженериков:
В C # компилятор пытается преобразовать правые части выражения в некоторый общий тип; поскольку
List<int>
иList<string>
являются двумя разными сконструированными типами, один не может быть преобразован в другой.В Java компилятор пытается найти общий супертип вместо преобразования, поэтому компиляция кода включает неявное использование подстановочных знаков и стирание типа ;
имеет тип компиляции
ArrayList<?>
(на самом деле он может быть такжеArrayList<? extends Serializable>
илиArrayList<? extends Comparable<?>>
, в зависимости от контекста использования, поскольку они оба являются общими универсальными супертипами) и тип времени выполнения rawArrayList
(поскольку это общий необработанный супертип).Например (проверьте сами) ,
источник
Write(two > six ? new List<object>() : new List<string>());
тоже не работает.ArrayList<String>
иArrayList<Integer>
будетArrayList
(исходный тип), но предполагаемый тип для этого тернарного оператора -ArrayList<?>
(тип подстановочного знака). В более общем плане то, что среда выполнения Java реализует универсальные шаблоны посредством стирания типа, не влияет на тип выражения во время компиляции .two > six ? new ArrayList<Integer>() : new ArrayList<String>()
is,ArrayList<? extends Serializable&Comparable<?>>
что означает, что вы можете назначить его как переменной типа,ArrayList<? extends Serializable>
так и переменной типаArrayList<? extends Comparable<?>>
, но, конечно жеArrayList<?>
, что эквивалентноArrayList<? extends Object>
, также работает.И в Java, и в C # (и в большинстве других языков) результат выражения имеет тип. В случае тернарного оператора есть два возможных подвыражения, оцениваемых для результата, и оба должны иметь один и тот же тип. В случае Java
int
переменная может быть преобразована вInteger
автобокс. Теперь, поскольку обаInteger
иString
наследуются отObject
, они могут быть преобразованы в один и тот же тип простым сужающим преобразованием.С другой стороны, в C # an
int
является примитивом, и не существует неявного преобразования вstring
или любое другоеobject
.источник
int
вobject
.Integer/String
вObject
не является сужающим преобразованием, это полная противоположность :-)Integer
аString
также реализовать,Serializable
иComparable
поэтому назначения для любого из них также будут работать, например,Comparable<?> c=condition? 6: "6";
илиList<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();
являются допустимым кодом Java.Это довольно просто. Неявного преобразования между строкой и целым числом нет. тернарному оператору необходимо, чтобы последние два операнда имели один и тот же тип.
Пытаться:
источник