2012-06-14 38 views
34

考慮這一點做作,簡單的例子:爲什麼Linq在使用ToList時失敗?

var foo = new byte[] {246, 127}; 
    var bar = foo.Cast<sbyte>(); 
    var baz = new List<sbyte>(); 
    foreach (var sb in bar) 
    { 
     baz.Add(sb); 
    } 
    foreach (var sb in baz) 
    { 
     Console.WriteLine(sb); 
    } 

二進制補碼的魔力,-10和127打印到控制檯。到現在爲止還挺好。敏銳的眼睛的人會看到我遍歷一個枚舉並將其添加到列表中。聽起來像ToList

var foo = new byte[] {246, 127}; 
    var bar = foo.Cast<sbyte>(); 
    var baz = bar.ToList(); 
    //Nothing to see here 
    foreach (var sb in baz) 
    { 
     Console.WriteLine(sb); 
    } 

除了不起作用。我得到此異常:

異常類型:System.ArrayTypeMismatchException

消息:源陣列型不能被分配給目的地的數組類型。

我覺得這個例外非常奇特,因爲

  1. ArrayTypeMismatchException - 我不會跟數組做任何事情,我自己。這似乎是一個內部例外。
  2. Cast<sbyte>工作正常(如在第一個例子中),它使用ToArrayToList時出現問題。

我的目標是.NET v4 x86,但在3.5中也是如此。

我不需要任何關於如何解決問題的建議,我已經設法做到了。我想知道的是爲什麼這種行爲首先發生?

編輯

即使怪異,增加了無意義的select語句使ToList正常工作:

var baz = bar.Select(x => x).ToList(); 
+0

隨着'Select'它導致'{-10,127}'。這裏有一個鑄造問題。肯定有趣的錯誤消息。 – asawyer

+0

@Lieven是的,我收集了多少,爲什麼'ToList'前的'Select(x => x)'更正了它?這是一個毫無意義的預測,因爲同樣的事情會回來。 – vcsjones

+2

我有一個解釋...它只是花一點時間寫出來。很好的問題。 –

回答

26

好吧,這實際上取決於幾個奇特組合:

  • 儘管在C#中不能直接將byte[]轉換爲sbyte[],在CLR允許它:

    var foo = new byte[] {246, 127}; 
    // This produces a warning at compile-time, and the C# compiler "optimizes" 
    // to the constant "false" 
    Console.WriteLine(foo is sbyte[]); 
    
    object x = foo; 
    // Using object fools the C# compiler into really consulting the CLR... which 
    // allows the conversion, so this prints True 
    Console.WriteLine(x is sbyte[]); 
    
  • Cast<T>()優化,例如,如果它認爲它並不需要做任何事情(通過is檢查像上面的)它返回原來的基準 - 使這裏發生。

  • ToList()代表們的List<T>構造服用IEnumerable<T>

  • 該構造函數爲ICollection<T>優化使用CopyTo ...和什麼失敗。下面是它有沒有方法的版本調用其他CopyTo

    object bytes = new byte[] { 246, 127 }; 
    
    // This succeeds... 
    ICollection<sbyte> list = (ICollection<sbyte>) bytes; 
    
    sbyte[] array = new sbyte[2]; 
    
    list.CopyTo(array, 0); 
    

現在,如果你在任何時候使用Select,你不與ICollection<T>結束了,所以它會通過合法(對於CLR)byte/sbyte轉換對於每個元件,而不是欲以CopyTo陣列實現。

+2

是的,我注意到,在運行時間欄的值類型是字節[],而不是System.Linq.Enumerable.CastIterator 。感謝您的解釋! – JDB

+0

在閱讀問答之後,是否意味着.ToList將需要在類型安全轉換中使用? – Turbot

+1

@Turbot:我不明白你的評論。你能重述嗎? –

相關問題