2009-01-30 42 views
2

我必須在這裏錯過一些明顯的東西。我不明白爲什麼這個linq查詢結果的轉換返回null,而不是我請求的類型化列表。Linq - 將IQueryable投射到IList返回null - 爲什麼?

IList<IMyDataInterface> list = query.ToList() as IList<IMyDataInterface>; 

運行此操作的完整代碼如下所示。這是我需要彌補的知識缺口。我嘗試了各種各樣的演員陣容,以使其發揮作用。我沒有得到例外,只是一個空。值得注意的是,LINQ查詢是選擇它的成果轉化爲我定製的「MyDataClass」它實現IMyDataInterface

class Program 
{ 
    static void Main(string[] args) 
    { 
     IMyFunctionalInterface myObject = new MyClass(); 


     //myObject.Get() returns null for some reason... 
     IList<IMyDataInterface> list = myObject.Get(); 

     Debug.Assert(list != null, "Cast List is null"); 
    } 
} 

public interface IMyFunctionalInterface 
{ 
    IList<IMyDataInterface> Get(); 
} 

public class MyClass : IMyFunctionalInterface 
{ 
    public IList<IMyDataInterface> Get() 
    { 
     string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" }; 

     var query = from n in names 
        where n.Contains("a") 
        select new MyDataClass 
        { 
         Name = n.ToString() 
        }; 

     //There IS data in the query result 
     Debug.Assert(query != null, "result is null"); 
     //but the cast here makes it return null 
     IList<IMyDataInterface> list = query.ToList() as IList<IMyDataInterface>; 

     return list; 
    } 

} 
public interface IMyDataInterface 
{ 
    string Name { get; set; } 
} 

public class MyDataClass : IMyDataInterface 
{ 
    public string Name { get; set; } 
} 
+0

會發生什麼( 「爲IList的」)?我認爲你可以執行.ToList()並將它粘貼在你的IList 變量中。 – 2009-01-30 03:13:41

+0

我也這麼認爲,但沒有奏效。 – jlembke 2009-02-01 22:39:19

回答

13

這裏的問題是協方差之一。

首先,你的例子太複雜了。我已經刪除了一些絨毛。另外,我添加了一些診斷來闡明問題。

class Program 
{ 
    static void Main(string[] args) 
    { 
     var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }; 

     var query = from n in names 
        select new C 
        { 
         S = n 
        }; 

     //There IS data in the query result 
     Debug.Assert(query != null, "result is null"); 

     //but the conversion here makes it return null 
     var list = query.ToList() as IList<I>; 
     Console.WriteLine(query.ToList().GetType()); 

     // this assert fires. 
     Debug.Assert(list != null, "Cast List is null"); 
    } 
} 

interface I 
{ 
    string S { get; set; } 
} 

class C : I 
{ 
    public string S { get; set; } 
} 

這個程序的輸出是:

 
System.Collections.Generic.List`1[C] 

請注意,我們正在努力蒙上了List<C>List<I>不工作在C#3.0。

在C#4.0中,您應該可以做到這一點,這要歸功於通用接口上類型參數的新共同和反方差。

此外,您原來的問題與IQueryable有關,但這與此處無關:您提供的查詢表達式創建IEnumerable<string>而不是IQueryable<string>

編輯:我想指出的是,你的「投」使用as運營商在技術上是不是演員,而是一個「類型轉換」。如果你使用了演員,你會得到一個有用的信息的例外。如果我更改爲:

var list = (IList<I>)query.ToList(); 

我得到一個InvalidCastException用:當你只是刪除了安全投

 
Additional information: Unable to cast object of type 'System.Collections.Generic.List1[C]' to type 'System.Collections.Generic.IList1[I]'. 

5

的情況下,試試這個:

var query = from n in names 
      where n.Contains("a") 
      select new MyDataClass 
      { 
       Name = n.ToString() 
      } as IMyDataInterface; 

您的問題是在這條線:

IList<IMyDataInterface> list = query.ToList() as IList<IMyDataInterface>; 

這也可以寫成:

List<MyDataClass> tmp = query.ToList(); 
IList<IMyDataInterface> list = tmp as IList<IMyDataInterface>; 

不幸的是,在C#中,as運算符無法按照您希望的方式運行。 as運算符只需將列表對象轉換爲不同類型的列表;它不會嘗試通過列表並投射每個項目。爲了將一個列表轉換爲別的東西,你需要調用它的Cast擴展方法。例如: -

IList<IMyDataInterface> list = query.ToList().Cast<IMyDataInterface>(); 

所以,你的選擇是:投過的項目中查詢你想要的界面(我的第一個例子),或者你已經執行的查詢(我的第二個例子)投後的整個列表。

我建議前者。

+0

我很驚訝在方法1的作品。每次我嘗試這樣的事情時,我都會遇到同樣的問題。我找到的唯一解決方案是Cast <>()擴展方法,它可以在.ToList()之前或之後調用。 – 2009-01-30 03:11:13