Ответ 1
Я сказал ранее, что (1) это ошибка компилятора, и (2) она новая. Первое утверждение было точным; во-вторых, я смутился в своей поспешности, чтобы добраться до автобуса вовремя. (Ошибка, о которой я думал, это для меня новичок, это гораздо более сложная ошибка, связанная с отмененными преобразованиями и отмененными операторами приращения.)
Это известная ошибка компилятора. Джон Скит впервые привлек мое внимание некоторое время назад, и я считаю, что там есть вопрос StackOverflow об этом; Я не помню, где небрежно. Возможно, Джон делает.
Итак, ошибка. Пусть определим "поднятый" оператор. Если оператор преобразует из значения типа N с нулевыми значениями в тип N с непустым значением T, то существует также "поднятый" оператор, который преобразует из S? к T?, так что нулевой S? преобразуется в нулевой T? и непустой S? преобразуется в T? путем разворачивания S? на S, преобразование S в T и перенос T в T?.
В спецификации указано, что (1) единственная ситуация, в которой есть поднятый оператор, - это когда S и T являются нечетными типами значений, и (2) что поднятые и не поднятые операторы преобразования считаются для того, являются ли они применимыми кандидатами для преобразования, и, если они применимы, то исходный и целевой типы применимых преобразований, снятые или отмененные, используются для определения наилучшего типа источника, наилучшего целевого типа и, в конечном счете, наилучшего преобразования всех применимые преобразования.
К сожалению, реализация полностью нарушает все эти правила и делает это так, что мы не можем изменить, не нарушая многие существующие программы.
Во-первых, мы нарушаем правило о существовании поднятых операторов. Поднятый оператор рассматривается реализацией, если S и T являются нечетными типами значений, или если S - тип значения, который не является нулевым, и T - любой тип, которому может быть присвоен нуль: тип ссылки, значение NULL тип или тип указателя. Во всех этих случаях мы производим поднятый оператор.
В вашем конкретном случае мы поднимаем до нулевого значения, говоря, что мы конвертируем тип с нулевым значением в ссылочный тип Cat, проверяя значение null. Если источник не равен нулю, мы обычно конвертируем; если это так, то мы создаем нулевую кошку.
Во-вторых, мы тщательно нарушаем правило о том, как определить наилучшие исходные и целевые типы применимых кандидатов, когда один из этих кандидатов является поднятым оператором, а также нарушаем правила определения того, какой из них является лучшим оператором.
Короче говоря, это большой беспорядок, который не может быть исправлен без нарушения реальных клиентов, и поэтому мы, скорее всего, закрепим поведение в Roslyn. В какой-то момент я буду рассматривать документацию о точном поведении компилятора в своем блоге, но я не задерживал бы дыхание, ожидая этого дня, если бы был вами.
И, конечно, многие извинения за ошибки.