2009-11-05 2 views
16

可能性の重複:
Puzzling Enumerable.Cast InvalidCastExceptionEnumerable.Cast <T>拡張メソッドをintからlongにキャストできません。なぜですか?

こんにちは、

私はちょうどそれはlongintからキャストすることができないようだ... Enumerable.Cast<T>拡張メソッドではかなり奇妙な何かに気づきましたこのキャストは完全に合法ですが

次のコードは、InvalidCastExceptionで失敗します。

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

しかし、私は同等であると考え、このコードは、仕事ん:

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

誰もがその動作を説明できますか?私たちは、模倣することができ

this.<>2__current = (TResult)this.<obj>5__ab; 

+2

可能な重複:http://stackoverflow.com/questions/445471 –

+0

はい、あなた」そうです...私の検索でそれを逃した –

+2

私は答えのどれも実際に質問*なぜ*に答えて驚いています。答えはintからlong **への変換がキャスト**ではないからです。 *変換*です。 C#は人を混乱させるだけであるため(同じように)、C#は両方とも同じ構文を使用することは残念です。 '.Cast ()'を使って、カスタム定義の明示的変換演算子を呼び出すこともできません。これはキャストでもないからです。 – Timwi

答えて

16

に... Castで該当する行を私はリフレクターでキャスト法のコードを見て、それは理解するのはかなり難しいので、リフレクターは、反復子ブロックをinterpreteすることはできませんこれは次のコードを使用します:

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

T Cast<T>(object input) 
{ 
    return (T)input; 
} 

これも失敗します。ここで重要なのは、キャスト時点では、それがオブジェクトだということです。 intではありません。これは、オブジェクトからの正確なタイプにのみアンボックスすることができるため、失敗します。私たちはオブジェクトから出ています - それは箱入りの長さかもしれませんが、そうではありません。それは箱入りのintです。 Eric Lippert discussed this on his blog

私たちは、アンボックスが正しいタイプにアンボックスすることができると判断しました。

あなたのコードで、あなたはボックス化されたint(オブジェクト)を扱っていませんが、あなたは、 、あなたはintを持っています。

+11

確かに。キャストシーケンス演算子*の以前のリリースでは間違った*があり、誤ってこのような変換を*成功*することができました。 (そして非常に遅かったです)これは、コードが正しく動作するように、潜在的に既存の顧客を破らなければならない最悪のバグ状況です。私たちはヒットしました。コードは現在正解で速く、過度に寛大で遅くはありませんが、本当に悪いと感じました。誤った実装をコードレビューし、リリース前に問題を把握していなかったので、特に悪いと感じています。ごめんなさい! –

+0

もちろん、私はボクシング...完全な説明、感謝を忘れてしまった! –

+8

Woohoo!私は何か言って、エリック・リッペルトは「確かに」言った。ちょうど私の月を作った:D –

8

他のほとんどのLINQ拡張メソッドとは異なり、Castは、IEnumerable<T>ではなく、汎用でないIEnumerableインターフェイスを拡張します。

これは、値が唯一まったく同じタイプにアンボクシングすることができますので、Range呼び出しによって生成されたint値は、その後longにそれらをキャストしようとすると失敗しCastコールの基礎となる列挙子で箱詰めされていることを意味します。

あなたが明示的にint値をボクシングして、第二のループ内で同じ例外動作を模倣することができます。

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

まさに! Rex Mが最初に、申し訳ありません...とにかく感謝! –

+0

これはなぜより正確な説明です。 +1 –

1

問題がCastIteratorのMoveNextメソッドボックスという現在の値であり、ターゲット型にVHS版を試みます(ボックス化された値は正しい型ではありません)、型チェック中にアンボックス化は失敗します。

参考情報:回避策は、Select()を使用することです

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 

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.unbox_any.aspx

関連する問題