Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting list of objects to List vs IList

Tags:

c#

casting

Just came across this:

Func<List<object>> foo = () => new List<object>();
List<string> s = (List<string>)foo();
IList<string> s1 = (IList<string>)foo();

Compiler complains about casting to List (makes sense), but says nothing about IList. Makes me wonder why is that?

like image 680
Evgeni Avatar asked Dec 05 '13 19:12

Evgeni


3 Answers

The compiler knows that a List<X> cannot be a List<Y>.
It therefore gives a compiler error.

However, the second cast could succeed if the List<X> is actually some derived class that also implements IList<Y>.

You will only get a compile-time error from a cast if neither type is an interface, or if one type is an unrelated interface and the other type is sealed (or a struct).

To quote the spec (§6.4.2)

The explicit reference conversions are:

  • From object and dynamic to any other reference-type.
  • From any class-type S to any class-type T, provided S is a base class of T.
  • From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
  • From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
  • From any interface-type S to any interface-type T, provided S is not derived from T.
  • [snip]

(emphasis added)

The provided... clauses exclude conversions that are actually implicit.

like image 79
SLaks Avatar answered Oct 01 '22 17:10

SLaks


An object that is known to be a List<object> might implement IList<string> as well as IList<object>, so it's possible that the cast can succeed. It can't in this case because we know that the statement is simply new List<object>(), but the compiler doesn't consider that. You might've extended List<T> and implemented the other, e.g.

// not recommended, but second cast works
public class MyWeirdList : List<object>, IList<string>

An object that is known to be a List<object> cannot possibly also be a List<string>, because you can only inherit from a single type.

public class MyWeirdList : List<object>, List<string> // compiler error

If List<T> were sealed, both casts would be invalid, because then the compiler would know for sure that the class couldn't implement IList<string>. You can try this by using this class instead:

public sealed class SealedList<T> : List<T> { }
like image 36
Tim S. Avatar answered Oct 01 '22 16:10

Tim S.


The first line fails at compile time, the second gives an "Unable to cast object of type 'System.Collections.Generic.List1[System.Object]' to type 'System.Collections.Generic.IList1[System.String]'." exception during runtime.

If you look at this question (Cast IList<string> to IList<object> fails at runtime), the answer clarifies why this compile works and also provides an example for a class that could satisfy the conditions provided.

like image 34
drew_w Avatar answered Oct 01 '22 16:10

drew_w