为什么不Enumerable.Cast<>使用我的转换运算符?

时间:2011-01-25 08:08:13

标签: c# linq

使用此类型:

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

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

以下按预期工作

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

但使用Cast<>失败并出现InvalidCastException - 为什么?

var fail=test.Cast<int>().ToList();

3 个答案:

答案 0 :(得分:2)

阅读Jon Skeet关于重新实现Linq(EduLinq)的博客,特别是part 33,他在这里说:

  

值得注意的是(从.NET 3.5 SP1开始)Cast和OfType仅执行引用和拆箱转换。它们不会将boxed int转换为long或执行用户定义的转换。基本上它们遵循与从对象转换为泛型类型参数相同的规则。 (这对于实现来说非常方便!)

答案 1 :(得分:1)

转换运算符纯粹是C#编译器级别的功能,运行时对它们一无所知,因此没有简单的方法可以通过通用的Cast方法实现它。一种方法是执行运行时代码生成:


    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);
        }
    }

然后你将失去编译时检查:


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

另一种方法是使用Puzzling Enumerable.Cast InvalidCastException中的反射,但这不适用于从int到long的内置转换。

答案 2 :(得分:0)

Enumerable.Cast的文档实际上有点模糊,并讨论了强制转换和转换。但它确实说“如果一个元素不能转换为类型TResult,则此方法将抛出异常”并且您的类Foo不能转换为int但可以使用强制转换语法进行转换。后者是一种方法调用。

通常,Cast和OfType的工作方式类似于'as'和'is',如果你写了:

var foo = new Foo()
var bar = foo is int;

吧会是假的。 Cast似乎与此一致(尽管在MSDN上找到的文档并不完全)。当is-operator返回false时失败。 (有一种特殊情况,情况并非如此,如果序列中的值为null且T为引用类型,则会出现这种情况)