Please explain why in the code below, Upcast of type IEnumerator <int> is possible to type IEnumerable <int> , because IEnumerator <T> is not inherited from IEnumerable <T>


static IEnumerator<int> GetOddNumbers(params int[] numbers) { foreach (var number in numbers) { if (number % 2 != 0) yield return number; } } static void Main(string[] args) { IEnumerable<int> enumerable = (IEnumerable<int>)GetOddNumbers(1, 2, 3); IEnumerator<int> enumerator = GetOddNumbers(1, 2, 3); //List<int> oddnumbers = new List<int>(GetOddNumbers(1, 2, 3)); //foreach (var number in oddnumbers) //{ // Console.WriteLine(number); //} //Console.ReadLine(); } 

    1 answer 1

    Sorry, misread the question at the beginning, the answer is completely rewritten.


    The fact is that in C # any object can be cast to any interface at the compilation stage:

     interface I1 { } class Program { static void Main(string[] args) { I1 i1 = (I1)(new Program()); } } 

    In runtime, if the object does not actually implement the interface, the InvalidCastExcetion exception will be InvalidCastExcetion .


    Why is it allowed to cast to the interface, although the object does not actually implement this interface? Consider the code

     Program p = new Program(); I1 i1 = (I1)p; 

    At this point, the static type of the p expression on which the caste is hung is Program . But the real, dynamic type can be any, derived from Program . And this type can fully implement the interface! Therefore, the compiler cannot prove that the caste is impossible, and postpones the check until the actual execution.

    Compare:

     interface I1 { } sealed class Program // Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ sealed { static void Main(string[] args) { I1 i1 = (I1)(new Program()); // НС компилируСтся, ошибка CS0030 } } 

    Under these conditions, the compiler can still prove that the expression cannot implement the interface, because the Program class does not implement this interface, and there cannot be any derived classes. This code is not compiled.


    The compiler, of course, could be super-smart, and see that the expression GetOddNumbers(...) is a call to a generator function, and does not implement IEnumerable<int> . But at the same time, he would have to solve it not according to the declared type ( IEnumerator<int> ), but by analyzing the program execution flow. This is a generally unsolvable task, so the C # compiler does not even try to tackle it. (Except for a few simple cases, yes.)

    • But is it possible to cast to the interface a successor of the base class that implements it stored in a variable? - Grundy
    • @Grundy: Couldn't parse your phrase :) - VladD
    • one
      Here :-) - really works :) - Grundy
    • @Grundy: Well, yes, where does it go? - VladD
    • @VladD thanks! Can I have another question? :) Why in the GetOddNumbers method the return value can be IEnumerable <T> and IEnumerator <T>? I checked 2 options and there were no errors in the program operation) - Vladmes