我对枚举器和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的用法。
我将分享一个被误用的概念:
var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};
var startingWith_M = names.Where(x => x.StartsWith("m"));
var startingWith_F = names.Where(x => x.StartsWith("f"));
// updating existing list
names[0] = "ford";
// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );
预期结果
// I was expecting
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari
实际结果
// what printed actualy
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari
解释
根据其他答案,结果的评估被推迟到调用ToList或类似的调用方法(例如ToArray)。
因此,我可以将这种情况下的代码重写为:
var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};
// updating existing list
names[0] = "ford";
// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));
var startingWith_F = names.Where(x => x.StartsWith("f"));
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );
在附近玩耍
https://repl.it/E8Ki/0
实现IEnumerable的类允许您使用foreach语法。
基本上,它有一个获取集合中下一个项的方法。它不需要将整个集合存储在内存中,也不知道其中有多少项,foreach只需要不断获取下一项,直到用完为止。
这在某些情况下非常有用,例如在大型数据库表中,您不希望在开始处理行之前将整个内容复制到内存中。
现在List实现IEnumerable,但表示内存中的整个集合。如果您有一个IEnumerable,并调用.ToList(),则会在内存中创建一个包含枚举内容的新列表。
linq表达式返回一个枚举,默认情况下,当您使用foreach遍历时,该表达式将执行。迭代foreach时执行IEnumerable linq语句,但可以使用.ToList()强制其更快地迭代。
我的意思是:
var things =
from item in BigDatabaseCall()
where ....
select item;
// this will iterate through the entire linq statement:
int count = things.Count();
// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();
// this will execute the linq statement *again*
foreach( var thing in things ) ...
// this will copy the results to a list in memory
var list = things.ToList()
// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();
// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...
我将分享一个被误用的概念:
var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};
var startingWith_M = names.Where(x => x.StartsWith("m"));
var startingWith_F = names.Where(x => x.StartsWith("f"));
// updating existing list
names[0] = "ford";
// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );
预期结果
// I was expecting
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari
实际结果
// what printed actualy
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari
解释
根据其他答案,结果的评估被推迟到调用ToList或类似的调用方法(例如ToArray)。
因此,我可以将这种情况下的代码重写为:
var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};
// updating existing list
names[0] = "ford";
// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));
var startingWith_F = names.Where(x => x.StartsWith("f"));
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );
在附近玩耍
https://repl.it/E8Ki/0