To expand on itsme86's post, notice that the Cast<T>() extension method, unlike most extension methods defined in System.Linq.Enumerable, extends non-generic IEnumerable, not generic IEnumerable.
That said, how else could the the Cast<T>() extension method treat the elements of the non-generic IEnumerable than as System.Objects?
The conversion from the type of the elements of the non-generic IEnumerable to System.Object is straightforward. It's just an implicit boxing conversion.
So the problem is when you try to convert - implicitly or explicitly - from an instance of System.Object to an instance of a type other than the underlying type of the System.Object. You can get away with this if the type you are trying to convert to is a base type of the underlying type of the System.Object, but it doesn't work with other kinds of conversions.
In other words, the code fails because you are essentially trying to do this:
Code:
int x = 10;
object o = x; //boxing
long y = (long)o; //InvalidCastException - long is not a base type of int
Which is different from doing this:
Code:
class A { }
class B : A { }
static void Main(string[] args)
{
B b = new B();
object o = b;
A a = (A)o; // no problem
}
Until a Cast extension method that takes in 2 type parameters is introduced to the .NET framework, you can define your own Cast<T,U> extension method:
Code:
namespace ConsoleApplication1
{
class ImplicitlyConvertibleFromFoo
{
public ImplicitlyConvertibleFromFoo(Foo foo)
{
this.foo = foo;
}
Foo foo;
}
class Foo
{
public static implicit operator ImplicitlyConvertibleFromFoo(Foo foo)
{
return new ImplicitlyConvertibleFromFoo(foo);
}
}
static class F
{
public static IEnumerable<T> Cast<T,U>(this IEnumerable<U> items) where T : class
{
foreach (U item in items)
yield return item as T;
}
}
class Program
{
static void Main(string[] args)
{
Foo[] fooArray = new Foo[4];
for (int i = 0; i < fooArray.Length; ++i)
{
fooArray[i] = new Foo();
}
ImplicitlyConvertibleFromFoo implicitlyConverted = fooArray[0];
// this is fine
ImplicitlyConvertibleFromFoo[] castArray = fooArray.Cast<ImplicitlyConvertibleFromFoo, Foo>().ToArray();
}
}
}
Notice that (T)item doesn't work since the compiler has no way to tell if a conversion operator exists for type parameters T and U. The "as" operator works, but since it returns null if the conversion cannot be performed we need to add a generic constraint specifying that T must be a reference type.