Страницы

Поиск по вопросам

понедельник, 22 апреля 2019 г.

Можно ли IEnumerator привести к IEnumerable?

Объясните,пожалуйста,почему в ниже приведенном коде возможен Upcast типа IEnumerator к типу IEnumerable,ведь IEnumerator не наследуется от IEnumerable

static IEnumerator GetOddNumbers(params int[] numbers) { foreach (var number in numbers) { if (number % 2 != 0) yield return number; } }
static void Main(string[] args) {
IEnumerable enumerable = (IEnumerable)GetOddNumbers(1, 2, 3); IEnumerator enumerator = GetOddNumbers(1, 2, 3);

//List oddnumbers = new List(GetOddNumbers(1, 2, 3));
//foreach (var number in oddnumbers) //{ // Console.WriteLine(number); //}
//Console.ReadLine(); }


Ответ

Извините, неверно прочитал вопрос вначале, ответ полностью переписан.

Дело в том, что в C# любой объект можно закастить к любому интерфейсу на этапе компиляции:
interface I1 { }
class Program { static void Main(string[] args) { I1 i1 = (I1)(new Program()); } }
В рантайме, если объект реально не имплементирует интерфейс, выбросится исключение InvalidCastExcetion

Почему же разрешён каст к интерфейсу, хотя объект на самом деле не имплементирует этот интерфейс? Рассмотрим код
Program p = new Program(); I1 i1 = (I1)p;
В этой точке статический тип выражения p, на которое навешен каст — Program. Но реальный, динамический тип может быть любой, производный от Program. И этот тип вполне может имплементировать интерфейс! Поэтому компилятор не может доказать, что каст невозможен, и откладывает проверку до момента реального выполнения.
Сравните:
interface I1 { }
sealed class Program // добавили sealed { static void Main(string[] args) { I1 i1 = (I1)(new Program()); // Не компилируется, ошибка CS0030 } }
В этих условиях компилятор таки может доказать, что выражение не может имплементировать интерфейс, ведь класс Program этот интерфейс не имплементирует, а производных классов быть не может. Вот этот код и не компилируется.

Компилятор, конечно, мог бы быть суперумным, и увидеть, что выражение GetOddNumbers(...) есть вызов функции-генератора, и не имплементирует IEnumerable. Но при этом он должен был бы решать это не по декларируемому типу (IEnumerator), а анализируя поток выполнения программы. Это в общем случае неразрешимая задача, так что компилятор C# и не пробует браться за неё. (За исключением нескольких простых случаев, да.)

Комментариев нет:

Отправить комментарий