使用此类型:
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();
答案 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为引用类型,则会出现这种情况)