为什么LINQ操作会丢失集合的静态类型?

时间:2011-07-23 18:18:58

标签: c# linq scala collections types

无论我用作输入的集合类型,LINQ始终返回IEnumerable<MyType>而不是List<MyType>HashSet<MyType>

来自MSDN tutorial

int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

// numQuery is an IEnumerable<int> <====== Why IEnumerable<int> instead of int[]?
var numQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

我想知道决定不保留集合类型(如元素类型)的原因是什么,而不是建议的解决方法。

我知道存在toArraytoDictionarytoList,这不是问题。

编辑:C#中的实现与Scala的工作方式有何不同,而不会覆盖各处的返回类型?

5 个答案:

答案 0 :(得分:7)

Linq标准查询运算符在IEnumerable<T>上定义,而不是在任何其他特定集合类型上定义。它适用于大多数集合,因为它们都实现了IEnumerable<T>

这些标准查询运算符的输出是一个新的IEnumerable<T> - 如果要支持所有可用的集合,则需要这些标准查询运算符的 N实现,而不仅仅是一个。

Linq的许多方面,如懒惰评估,如果被迫工作,即List<T>而不是IEnumerable<T>,则效果不佳。

答案 1 :(得分:3)

简答: C#的类型系统过于原始,不支持在高阶函数中保留集合类型(没有代码重复,即)。

答案很长:请参阅我的帖子here。 (实际上它是关于Java的,但也适用于C#。)

答案 2 :(得分:1)

在LINQ-to-objects的特定情况下,LINQ-provider是System.Linq.Enumerable类中定义的一组扩展方法。为了尽可能通用,它们被定义为扩展实现IEnumerable<T>接口的任何类型。因此,当执行这些方法时,他们不知道集合是哪种具体类型;它只知道它是IEnumerable<T>。因此,无法保证对集合执行的操作生成相同类型的集合。相反,它产生了下一个最好的东西 - IEnumerable<T>对象。

这是一个非常具体的设计选择。 LINQ旨在以非常高的抽象级别工作。它的存在使您不必关心基础数据源,而只需说出您想要的内容而不关心这些细节。

答案 3 :(得分:0)

LINQ采用IEnumerable&lt;&gt;并返回一个。很自然。 IEnumerable是LINQ采用的原因 - 几乎所有集合都实现它。

答案 4 :(得分:0)

许多System.Linq.Enumerable方法被懒惰地评估。这允许您在不执行查询的情况下创建查询。

//a big array - have to start somewhere
int[] source = Enumerable.Range(0, 1000000).ToArray();

var query1 = source.Where(i => i % 2 == 1); //500000 elements
var query2 = query1.Select(i => i.ToString()); //500000 elements
var query3 = query2.Where(i => i.StartsWith("2"); //50000? elements
var query4 = query3.Take(5); //5 elements

string[] result = query4.ToArray();

此代码调用ToString大约15次。它分配一个字符串[5]数组。

如果Where和Select返回的数组,此代码将调用ToString 500000次并且必须分配两个大型数组。谢天谢地,事实并非如此。