Почему коллекция <enum> не может быть использована для <int?>?

Почему коллекция enum недоступна для int?

enum Test { A = 1, B = 2 };

int? x = (int?)Test.A; // Valid

var collection1 = new[] { Test.A }.Cast<int>().ToList();

// InvalidCastException has thrown (Specified cast is not valid.)    
var collection2 = new[] { Test.A }.Cast<int?>().ToList(); 

Ответ 1

Метод Cast может выполнять только преобразования бокса/распаковки, преобразования ссылок и преобразования между типом перечисления и его базовым типом интеграла. Unboxing должен быть в правильном типе, но - он не может распаковываться с типом NULL (в отличие от преобразования С#).

var collection1 = new[] { Test.A }.Cast<int>()
                                  .Select(x => (int?) x)
                                  .ToList();

Для каждого значения Cast будет освобождено от установленного значения enum до значения int, а затем Select преобразует значение int в значение int?.

В этом случае вы также можете уйти от этого:

var collection1 = new[] { Test.A }.Select(x => (int?) x)
                                  .ToList();

то есть. no Cast. Однако это не работает, если вместо этого у вас есть массив object:

// Fails
var collection1 = new object[] { Test.A }.Select(x => (int?) x)
                                         .ToList();

Вы не можете распаковать коробчатое значение перечисления в значение с нулевым значением int. Однако версия Cast все еще работает в этом случае, поскольку она разделяет два шага (сначала распаковывает на int, а затем переходит от int в int?.)

Ответ 2

Это потому, что внутри Cast() делает что-то вроде:

object o = Test.A;

int i = (int)o; // This is valid.
int? ni = (int?)o; // This is not valid!

Вам нужно будет сначала Cast() в int:

var collection3 = new[] { Test.A }.Cast<int>().ToList().Cast<int?>().ToList();

или вообще не использовать Cast():

var collection4 = new object[] { Test.A, null }.Select(i => i == null ? null : (int?)(int)i).ToList();

Ответ 3

Это не сработает, потому что нам нужно понять, что такое enum и что такое int? (nullable int)

int? на самом деле не int, это общий экземпляр Nullable, - синтаксис T? является сокращением для System.Nullable, где T - тип значения. Эти две формы взаимозаменяемы. Ссылка

Enum Основной тип элементов перечисления по умолчанию - int. Ссылка

Следовательно, существует неявное преобразование между int и enum. Но неявное преобразование между enum (int) и Nullable (который является совершенно другим ссылочным типом) не существует. Вот почему вы видите исключение InvalidCastException.

Где, как в первом выражении int? x = (int?)Test.A;, вы принудительно устанавливаете явное преобразование в любое поле типа значения и получаете целое число или получаете нуль, что работает, потому что бокс перечисления в object, а затем распаковывает его как int? возвращает int.

Ответ 4

Я не знаю, почему Cast<>() не работает, но вы можете использовать Select() для выполнения того, что вы хотите.

var collection = new[] { Test.A }.Select(item => (int?)item).ToList();