2009-01-30 73 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