Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 可枚举.Cast<;T>;扩展方法无法从int转换为long,为什么?_C#_Linq_Casting - Fatal编程技术网

C# 可枚举.Cast<;T>;扩展方法无法从int转换为long,为什么?

C# 可枚举.Cast<;T>;扩展方法无法从int转换为long,为什么?,c#,linq,casting,C#,Linq,Casting,可能重复: 嗨 我刚刚注意到Enumerable.Cast扩展方法有些奇怪。。。似乎它不能从int转换到long,即使这种转换是完全合法的 以下代码失败,出现InvalidCastException: foreach (var item in Enumerable.Range(0,10).Cast<long>()) { Console.WriteLine(item); } 有人能解释这种行为吗?我用Refle

可能重复:

我刚刚注意到
Enumerable.Cast
扩展方法有些奇怪。。。似乎它不能从
int
转换到
long
,即使这种转换是完全合法的

以下代码失败,出现
InvalidCastException

        foreach (var item in Enumerable.Range(0,10).Cast<long>())
        {
            Console.WriteLine(item);
        }

有人能解释这种行为吗?我用Reflector查看了Cast方法的代码,但是Reflector无法解释迭代器块,因此很难理解…

Cast中的相关行:

 this.<>2__current = (TResult)this.<obj>5__ab;
this.2_uuucurrent=(TResult)this.5_uuab;
我们可以使用以下代码来模拟这一点:

int foo = 1;
long bar = Cast<long>(foo); //oh noes!

T Cast<T>(object input)
{
    return (T)input;
}
intfoo=1;
长杆=铸造(foo)//哦,不!
T转换(对象输入)
{
返回(T)输入;
}
这也失败了。这里的关键是,在施法点,它是一个对象。不是int。这会失败,因为我们只能从一个对象取消装箱到我们想要的确切类型。我们从对象开始,它可以是一个盒装的长,但它不是。这是一个装箱的整数:

我们已经决定取消装箱只能取消装箱到确切的类型。如果您想调用执行所有这些操作的slow方法,它是可用的–您可以随时调用Convert


在有效的代码中,您没有处理装箱的int(对象),而是得到了int。

与大多数其他LINQ扩展方法不同,它扩展了非泛型接口,而不是

这意味着由
Range
调用生成的
int
值由
Cast
调用的基础枚举数装箱,然后该枚举数尝试将它们强制转换为
long
,但失败,因为值只能被解装箱为完全相同的类型

通过显式装箱
int
值,可以在第二个循环中模拟相同的异常行为:

foreach (var item in Enumerable.Range(0, 10).Select(i => (long)(object)i))
{
    Console.WriteLine(item);
}

问题是CastIterator的MoveNext将当前值装箱,并尝试将其取消装箱到目标类型(装箱的值不是正确的类型),因此在类型检查期间取消装箱失败

参考资料:

L_003c:ldarg.0
L_003d:ldarg.0
L_003e:ldfld class[mscorlib]System.Collections.IEnumerator System.Linq.Enumerable/d_uuaa::7_uwrapac
L_0043:callvirt实例对象[mscorlib]System.Collections.IEnumerator::get_Current()
L_0048:stfld object System.Linq.Enumerable/d_aa::5_ab
L_004d:ldarg.0
L_004e:ldarg.0
L_004f:ldfld object System.Linq.Enumerable/d_aa::5_ab
L_0054:unbox.any!特雷苏特

解决方法是使用Select()

。请注意,早期版本的Cast-sequence操作符出错,并且意外地允许这样的转换在设计上失败时成功。(而且速度也非常非常慢。)这是最糟糕的错误情况,为了使代码正确运行,您必须潜在地破坏现有客户。我们受到了打击;代码现在是正确的和快速的,不是过于宽松和缓慢,但我们对此感到非常糟糕。我感觉特别糟糕,因为我的代码审查了不正确的实现,在发布之前没有发现问题。很抱歉当然,我忘了拳击。。。完美的解释,谢谢!呜呼!我说了些什么,埃里克·利珀特说“真的”。刚完成我的月份:DDude。保持冷静。@Eric Lippert:看来你只需要适应你新发现的名人身份可能重复:是的,你是对的。。。在我的搜索中我错过了它。我很惊讶没有一个答案能真正回答为什么这个问题。答案是因为从int到long的转换不是强制转换。这是一种转换。不幸的是,C#对这两种语言使用了相同的语法,因为它只会让人困惑(显然)。您也不能使用
.Cast()
调用自定义定义的显式转换运算符,因为这也不是强制转换。
foreach (var item in Enumerable.Range(0, 10).Select(i => (long)(object)i))
{
    Console.WriteLine(item);
}
    L_003c: ldarg.0 
    L_003d: ldarg.0 
    L_003e: ldfld class [mscorlib]System.Collections.IEnumerator System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<>7__wrapac
    L_0043: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
    L_0048: stfld object System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<obj>5__ab
    L_004d: ldarg.0 
    L_004e: ldarg.0 
    L_004f: ldfld object System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<obj>5__ab
    L_0054: unbox.any !TResult