.Cast<T> 扩展方法总是抛出 InvalidCastException - 你应该如何使用这个方法?

.Cast<T> extension method always throws InvalidCastException - How are you supposed to use this method?

有几次我对 .Cast<T> LINQ 扩展方法感到好奇。我心想 "that sounds like just what I need in this situation" 但每当我尝试使用它时,我总是得到一个 InvalidCaseException。我从来没有能够成功地使用这种方法。这是检查异常的示例行:

Enumerable.Range(0,10).Cast<float>().ForEach(Console.Out.WriteLine);

int转换为float没有什么争议,那么为什么这种方法拒绝这样做呢?我可以解决这个问题并通过简单地将 .Cast<float> 替换为 .Select(x => (float)x)

来获得所需的效果
Enumerable.Range(0, 10).Select(x => (float)x).ForEach(Console.Out.WriteLine);

这不是太麻烦,但我仍然不明白为什么 Cast<float> 方法不能为我完成这项工作。

问题简而言之:如何使用 .Cast<T> 扩展方法?

这与问题无关,但以防万一有人想知道,我在上面的代码片段中使用了自定义 ForEach 扩展方法(标准方法仅适用于列表):

static class Extensions
{
    public static void ForEach<T>(this IEnumerable<T> x, Action<T> l)
    {
        foreach (var xs in x) l(xs);
    }
}

当您写下以下内容时:

int a = GetInt();
var b = (float)a;

你实际上并没有铸造价值。你正在转换它。 C# 只是为您提供了很好的语法。此处生成的 IL 是 conv.r4,它将堆栈顶部的值转换为浮点数。

Enumerable.Cast<T> 实际上是在进行转换,并没有得到语法糖的好处。

如果你写:

Enumerable.Range(0,10).Cast<object>().ForEach(Console.Out.WriteLine);

你会没事的,因为一个整数可转换为一个对象。

如果你查看 ReferenceSourceCast<TResult> 方法的源代码,你会看到最后 CastIterator<TResult> 方法被调用,类似于:

static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source)
{
    foreach (object obj in source) yield return (TResult)obj;
}

如您所见,C# 尝试从 boxed 对象转换为 TResult,这是异常的原因。如果您想模仿问题,请尝试以下操作:

int a = 5;
object o = a;
float f = (float)o;