Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a Linq Cast<T> operation fail when I have an implicit cast defined?

Tags:

.net

casting

linq

I've created two classes, with one of them having an implicit cast between them:

public class Class1
{
    public int Test1;
}

public class Class2
{
    public int Test2;

    public static implicit operator Class1(Class2 item)
    {
        return new Class1{Test1 = item.Test2};
    }
}

When I create a new list of one type and try to Cast<T> to the other, it fails with an InvalidCastException:

List<Class2> items = new List<Class2>{new Class2{Test2 = 9}};
foreach (Class1 item in items.Cast<Class1>())
{
    Console.WriteLine(item.Test1);
}

This, however, works fine:

foreach (Class1 item in items)
{
    Console.WriteLine(item.Test1);
}

Why is the implicit cast not called when using Cast<T>?

like image 674
Ryan Versaw Avatar asked Apr 30 '09 19:04

Ryan Versaw


3 Answers

Because, looking at the code via Reflector, Cast doesnt attempt to take any implicit cast operators (the LINQ Cast code is heavily optimised for special cases of all kinds, but nothing in that direction) into account (as many .NET languages won't).

Without getting into reflection and other things, generics doesnt offer any out of the box way to take such extra stuff into account in any case.

EDIT: In general, more complex facilities like implicit/explict, equality operators etc. are not generally handled by generic facilities like LINQ.

like image 71
Ruben Bartelink Avatar answered Oct 31 '22 22:10

Ruben Bartelink


You can also use this to do casting with conversions if needed:

public static IEnumerable<TDest> CastAll<TItem, TDest>(this IEnumerable<TItem> items)
{
 var p = Expression.Parameter(typeof(TItem), "i");
 var c = Expression.Convert(p, typeof(TDest));
 var ex = Expression.Lambda<Func<TItem, TDest>>(c, p).Compile();

 foreach (var item in items)
 {
    yield return ex(item);
 }
}

From http://adventuresdotnet.blogspot.com/2010/06/better-more-type-safe-alternative-to.html

like image 32
Maslow Avatar answered Oct 31 '22 20:10

Maslow


Thanks for that I was about to use that exact case somewhere. You have saved me a pile of time. As a possible solution to your problem you could use ConvertAll<> instead, like so:

foreach (Class1 item in items.ConvertAll<Class1>((i) => (Class1)i))
{
     Console.WriteLine(item.Test1);
}

EDIT: or if you want to be more explicit that the cast is implicit then this works too:

foreach (Class1 item in items.ConvertAll<Class1>(i => i))
{
     Console.WriteLine(item.Test1);
}
like image 1
David McEwing Avatar answered Oct 31 '22 20:10

David McEwing