2009-11-05 67 views
16

可能重複:
Puzzling Enumerable.Cast InvalidCastExceptionEnumerable.Cast <T>擴展方法無法從int轉換爲long,爲什麼?

嗨,

我只是發現了一些與Enumerable.Cast<T>擴展方法很奇怪......它似乎不能施放從intlong ,儘管這個演員是完全合法的。

下面的代碼失敗,並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); 
     } 

任何人都可以解釋這種行爲?我看着與反射鑄造法的代碼,但反射不能interprete迭代塊,所以這是很費解......

+2

可能重複:http://stackoverflow.com/questions/445471 – 2009-11-06 00:06:22

+0

是的,你」對吧......我錯過了我的搜索 – 2009-11-06 01:16:28

+2

我很驚訝,沒有一個答案實際上回答了這個問題*爲什麼*。答案是因爲從int到long **的轉換不是cast **。這是一個*轉換*。不幸的是,C#對這兩種語言使用相同的語法,因爲它只會混淆人們(顯然)。您也不能使用'.Cast ()'調用自定義的顯式轉換運算符,因爲這也不是轉換。 – Timwi 2010-08-27 08:07:53

回答

16

Cast相關線路:

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

我們可以模仿這使用下面的代碼:

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

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

這也失敗了。這裏的關鍵是,在演員角度,這是一個對象。不是一個整數。這失敗了,因爲我們只能從對象中取消裝箱到我們想要的確切類型。我們正在從對象 - 這可能是一個長盒子,但事實並非如此。這是一個盒裝詮釋。 Eric Lippert discussed this on his blog

我們已經決定拆箱只能拆箱到確切類型。如果你想打電話,做所有咕緩慢的方法,它的可用 - 你可以隨時撥打轉換...

在你的代碼工作,你不處理裝箱的int(對象) ,你有一個int。

+11

確實。請注意,Cast的早期版本序列運算符*得到了錯誤*並且意外地允許像這樣的轉換*成功*在設計時它們應該失敗。 (它也非常非常緩慢)。這是最糟糕的錯誤情況,爲了使代碼正確行爲,您必須潛在地破壞現有客戶。我們取得了成功;代碼現在正確和快速,不是過分寬鬆和緩慢,但我們感覺真的很糟糕。我感覺特別糟糕,因爲我代碼審查了不正確的實現,並且在發佈之前沒有發現問題。抱歉! – 2009-11-06 00:03:45

+0

當然,我忘了拳擊......完美的解釋,謝謝! – 2009-11-06 00:04:33

+8

嗚呼!我說了些什麼,Eric Lippert說「確實」。剛做了我的月:D – 2009-11-06 00:04:36

8

與大多數其他LINQ擴展方法不同,Cast擴展了非通用接口IEnumerable,而不是IEnumerable<T>

這意味着由Range調用生成的int值由Cast呼叫的基礎枚舉,然後嘗試將它們轉換爲long和失敗,因爲值只能是裝箱的確切相同類型盒裝。

您可以通過顯式拳擊int值模仿你的第二個循環相同的異常行爲:

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

準確!但雷克斯M是第一個,抱歉...謝謝! – 2009-11-06 00:07:16

+0

這是爲什麼更正確的解釋。 +1 – 2009-11-06 00:31:29

1

的問題是,CastIterator的MoveNext箱的電流值,並試圖把它拆箱爲目標類型(其中盒裝值不是正確的類型),因此在類型檢查過程中拆箱失敗。

參考信息:

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

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 

的解決方法是使用一個選擇()

相關問題