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.)