我对枚举器和LINQ的工作方式有些怀疑。考虑以下两个简单的选择:
List<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct().ToList();
or
IEnumerable<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct();
我更改了原始对象的名称,使其看起来像一个更通用的示例。查询本身并不那么重要。我想问的是:
foreach (Animal animal in sel) { /*do stuff*/ }
我注意到,如果我使用IEnumerable,当我调试和检查“sel”(在这种情况下是IEnumeraable)时,它有一些有趣的成员:“internal”、“outer”、“innerKeySelector”和“outerKeySelecter”,最后两个似乎是委托。“内部”成员中没有“动物”实例,而是“物种”实例,这对我来说很奇怪。我想这两位代表会决定哪一个进入,哪一个退出?我注意到,如果我使用“Distinct”,“inner”包含6项(这是不正确的,因为只有2项是Distinct),但“outer”包含正确的值。同样,可能是委托方法决定了这一点,但这比我对IEnumerable的了解要多得多。最重要的是,这两个选项中哪一个性能最佳?
通过.ToList()进行的邪恶列表转换?
或者直接使用枚举器?
如果可以的话,也请解释一下或提供一些链接来解释IEnumerable的用法。
IEnumerable描述行为,而List是该行为的实现。当您使用IEnumerable时,您给编译器一个机会,将工作推迟到以后,可能会在这一过程中进行优化。如果使用ToList(),则强制编译器立即将结果具体化。
每当我“堆叠”LINQ表达式时,我都会使用IEnumerable,因为通过只指定行为,我给了LINQ一个延迟求值并可能优化程序的机会。还记得LINQ如何在枚举数据库之前不生成SQL来查询数据库吗?考虑一下:
public IEnumerable<Animals> AllSpotted()
{
return from a in Zoo.Animals
where a.coat.HasSpots == true
select a;
}
public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
return from a in sample
where a.race.Family == "Felidae"
select a;
}
public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
return from a in sample
where a.race.Family == "Canidae"
select a;
}
现在您有了一个选择初始样本(“AllSpoted”)的方法,以及一些过滤器。现在你可以这样做了:
var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());
那么,使用List比IEnumerable更快吗?仅当您希望防止查询被多次执行时。但总体而言,情况是否更好?在上面的例子中,Leopards和Hyenas分别被转换为单个SQL查询,数据库只返回相关的行。但是,如果我们从AllSpotted()返回了一个List,那么它可能会运行得更慢,因为数据库可能会返回比实际需要的数据多得多的数据,而且我们在客户端进行过滤会浪费时间。
在程序中,最好将查询转换为列表,直到最后,所以如果我要多次枚举Leopards和Hyenas,我会这样做:
List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();
IEnumerable(延迟执行)的缺点是,在调用.ToList()之前,列表可能会发生更改。举一个非常简单的例子,这是可行的
var persons;
using (MyEntities db = new MyEntities()) {
persons = db.Persons.ToList(); // It's mine now. In the memory
}
// do what you want with the list of persons;
这是行不通的
IEnumerable<Person> persons;
using (MyEntities db = new MyEntities()) {
persons = db.Persons; // nothing is brought until you use it;
}
persons = persons.ToList(); // trying to use it...
// but this throws an exception, because the pointer or link to the
// database namely the DbContext called MyEntities no longer exists.