Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't Enumerable.Cast<> use my conversion operator?

Tags:

c#

linq

Using this type:

class Foo
{
  public static implicit operator int(Foo obj)
  {
    return 5;
  }
}

var test=new[] { new Foo() };

The following works as expected

var ok=test.Select(x => (int)x).ToList();

but using Cast<> fails with an InvalidCastException - why?

var fail=test.Cast<int>().ToList();
like image 498
laktak Avatar asked Jan 24 '26 06:01

laktak


2 Answers

Read Jon Skeet's blog about reimplementing Linq (EduLinq), specifically part 33, where he says this:

It's worth noting that (as of .NET 3.5 SP1) Cast and OfType only perform reference and unboxing conversions. They won't convert a boxed int to a long, or execute user-defined conversions. Basically they follow the same rules as converting from object to a generic type parameter. (That's very convenient for the implementation!)

like image 161
Damien_The_Unbeliever Avatar answered Jan 25 '26 20:01

Damien_The_Unbeliever


Casting operators are purely C# compiler level features, the run-time doesn't know anything about them so there is no simple way to implement this via generic Cast method. One way to do this is to perform run-time code generation:


    public static class Converter<TSource, TResult>
    {
        static Converter()
        {
            var sourceParameter = Expression.Parameter(typeof(TSource));
            var conversionExpression = Expression.Lambda<Func<TSource, TResult>>(
                Expression.Convert(sourceParameter, typeof(TResult)),
                sourceParameter);

            Instance = conversionExpression.Compile();
        }

        public static Func<TSource, TResult> Instance
        {
            get;
            private set;
        }
    }

    public static class EnumerableEx
    {
        public static IEnumerable<TResult> Cast<TSource, TResult>(this IEnumerable<TSource> source)
        {
            return source.Select(Converter<TSource, TResult>.Instance);
        }
    }

but then you'll loose compile-time checking:


var test = new[] { new Foo() };
var ok = test.Cast<Foo, int>().ToList(); // compiles and works ok
var error = test.Cast<Foo, double>().ToList(); // compiles but fails at run-time

Another way is to use reflection as in Puzzling Enumerable.Cast InvalidCastException but this will not work with built-in conversions like from int to long.

like image 25
Konstantin Oznobihin Avatar answered Jan 25 '26 18:01

Konstantin Oznobihin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!