Упакованный базовый тип, допускающий значение NULL, может быть приведен к перечислению, но тип перечисления в штучной упаковке не может быть преобразован в тип, допускающий значение NULL

  • Базовый тип, допускающий значение NULL, в штучной упаковке можно преобразовать в перечисление, но тип перечисления в штучной упаковке нельзя преобразовать в тип, допускающий значение NULL.

И аналогично,

  • Упакованное перечисление, допускающее значение NULL, может быть преобразовано к базовому типу, но упакованное базовый тип не может быть преобразовано в перечисление, допускающее значение NULL.

Хорошо, я знаю, что "упакованный в коробку тип, допускающий значение NULL" - не лучший способ описать это, но это просто вопрос. Я знаю, что это основной тип значения, который попадает в рамку.

Я покажу это на примерах. Предположим, у меня есть enum с int в качестве базового типа.

enum Sex { Male, Female }

Случай I:

int? i = 1;
object o = i;
Sex e = (Sex)o; //success

//but

Sex e = Sex.Male;
object o = e;
int? i = (int?)o; //invalid cast

Случай II:

Sex? e = Sex.Male;
object o = e;
int i = (int)o; //success

//but

int i = 1;
object o = i;
Sex? e = (Sex?)o; //invalid cast

В двух словах:

(enum)int? -> succeeds
(int?)enum -> the reverse fails

(int)enum? -> succeeds
(enum?)int -> the reverse fails

Или, говоря еще проще,

преобразование в значение, не допускающее значения NULL -> успешно
преобразование в значение, допускающее значение NULL -> не выполняется

Теперь я точно знаю, что после того, как вы поместите в коробку тип значения, его можно будет вернуть только к исходному типу. Но поскольку по правилам C # упакованный int может быть преобразован в enum, а упакованный enum может быть преобразован в int, а упакованный int в int? и упакованный int? в int, я искал последовательного понимания других также сценарии, то есть перечисленные выше. Но я не понимаю логики. Во-первых, я считаю, что если все они потерпели неудачу или все преуспели, это имело больше смысла для разработчиков. Во-вторых, даже успешные забросы выглядят немного странно. Я имею в виду, поскольку тип значения может быть неявно приведен к его эквиваленту, допускающему значение NULL (а не наоборот), приведение к типу, допускающему значение NULL, в любом случае должно быть успешным, но с текущей реализацией тип, допускающий значение NULL, успешно преобразуется в не допускающий значение NULL, что может даже завершиться ошибкой, если первое имело нулевое значение. Если бы все было наоборот, это было бы легче понять. Пример вроде:

Sex? e = null;
object o = e;
int i = (int)o; //succeeds, but sure to explode on cast

//but

int i = 1;
object o = i;
Sex? e = (Sex?)o; //invalid cast, even though its always a safe cast

Вопросы:

  1. Итак, какое правило C # позволяет этому случиться?

  2. Есть ли простой способ запомнить это?


person nawfal    schedule 18.12.2013    source источник
comment
Хороший вопрос. Я думаю, что это разница на уровне CLR, а не на уровне C #. Существуют различные вещи, которые допустимы на уровне CLR, но не разрешены на уровне C # - например, приведение типов между int[] и uint[].   -  person Jon Skeet    schedule 18.12.2013


Ответы (1)


Думаю, это тонкость инструкций unbox и unbox.any IL.

Из ECMA 335, раздел III.4.32 (unbox операция - unbox.any аналогична)

Исключения:
System.InvalidCastException выдается, если obj не является типом значения в рамке, valuetype - это Nullable<T> и obj не является упакованным в рамку T, или если тип значения, содержащегося в obj, не является verifier-assignable-to (III.1.8. 2.3) valuetype.

Так, например, в этом случае:

Sex e = Sex.Male;
object o = e;
int? i = (int?)o;

он не работает совершенно правильно - потому что valuetype равен Nullable<int>, а значение obj - не int в штучной упаковке. Часть «присваиваемый для проверяющего» не применяется к случаю Nullable<T>.

Я сомневаюсь, что какое-либо из этих действий описано в спецификации C #, к сожалению - я не думаю, что описывается поведение распаковки от "boxed int" к "enum с базовым типом int", насколько я могу видеть, что является своего рода предпосылкой для включения в смесь допустимости значений NULL.

person Jon Skeet    schedule 18.12.2013
comment
Спец-ниндзя поражает! :) Извините за опоздание с ответом. Действительно хорошая находка. Хотя я не совсем уверен, почему он был разработан таким образом, что сбивает с толку. - person nawfal; 18.12.2013