Why does the C # compiler allow explicit casting between IEnumerable <T> and TAlmostAnything?

The following code gives you a compiler error, as you would expect:

List<Banana> aBunchOfBananas = new List<Banana>(); Banana justOneBanana = (Banana)aBunchOfBananas; 

However, when using IEnumerable<Banana> you just get a runtime error.

 IEnumerable<Banana> aBunchOfBananas = new List<Banana>(); Banana justOneBanana = (Banana)aBunchOfBananas; 

Why does the C # compiler allow this?

+59
generics c # compiler-errors
Feb 08 2018-12-12T00:
source share
4 answers

I would suggest this because IEnumerable<T> is an interface in which some implementation can have an explicit cast to Banana - no matter how stupid it will be.

On the other hand, the compiler knows that List<T> cannot be explicitly passed to Banana .

A good selection of examples, by the way!

Adding an example to illustrate. Maybe we will have an “enumerable” that should always contain at most one Banana :

 public class SingleItemList<T>:Banana, IEnumerable<T> where T:Banana { public static explicit operator T(SingleItemList<T> enumerable) { return enumerable.SingleOrDefault(); } // Others omitted... } 

Then you could do this:

 IEnumerable<Banana> aBunchOfBananas = new SingleItemList<Banana>(); Banana justOneBanana = (Banana)aBunchOfBananas; 

Like what writes the following that the compiler understands perfectly:

 Banana justOneBanana = aBunchOfBananas.SingleOrDefault(); 
+48
Feb 08 2018-12-12T00:
source share
— -

When you say Y y = (Y)x; , this actor tells the compiler: "Believe me, whatever the x , it can be dropped at Y at runtime, so just do it, okay?"

But when you say

 List<Banana> aBunchOfBananas = new List<Banana>(); Banana justOneBanana = (Banana)aBunchOfBananas; 

the compiler can look at the definitions for each of these specific classes ( Banana and List<Banana> ) and see that there are no static explicit operator Banana(List<Banana> bananas) defined (remember, an explicit listing must be defined either in volume or type which is superimposed is from the specification, section 17.9.4). At compile time, it is known that what you say can never be true. So he yells at you to stop lying.

But when you say

 IEnumerable<Banana> aBunchOfBananas = new List<Banana>(); Banana justOneBanana = (Banana)aBunchOfBananas; 

well, now the compiler does not know. It is very good that the case when aBunchOfBananas happens at run time, its concrete type x could be determined by the static explicit operator Banana(X bananas) . Therefore, the compiler trusts you as you requested it.

+29
Feb 08 2018-12-12T00:
source share

Perhaps because the compiler knows that Banana not extending List<T> , but there is a possibility that some object that implements IEnumerable<T> could also extend Banana and make the listing valid.

+16
Feb 08 2018-12-12T00:
source share

According to the language specification (6.2.4) "Explicit reference conversions: From any class S to any type of interface T, if S is not sealed and not provided S does not implement T ... Explicit reference conversions are those conversions between reference types that require run-time checks to make sure they are correct ... "

Therefore, the compiler does not check the implementation of the interface at compile time. This is the runtime CLR. He checks the metadata, trying to find an implementation in the classroom or among his parents. I don’t know why it does that. It probably takes a long time. So this code compiles correctly:

 public interface IInterface {} public class Banana { } class Program { static void Main( string[] args ) { Banana banana = new Banana(); IInterface b = (IInterface)banana; } } 

In the other hand, if we try to apply a banana to the class, the compiler checks its metadata and gives an error:

  FileStream fs = (FileStream)banana; 
0
Feb 15 '12 at 8:30
source share



All Articles