在内部,第一个版本被编译成如下所示:
public IEnumerable<T> GoodItems
{
get
{
foreach (var item in _list)
if (item.IsGood)
yield return item;
}
}
而第二个现在看起来像:
public IEnumerable<T> GoodItems
{
get
{
foreach (var item in GoodItemsHelper)
yield return item;
}
}
private IEnumerable<T> GoodItemsHelper
{
get
{
foreach (var item in _list)
if (item.IsGood)
yield return item;
}
}
LINQ 中的Where
子句是通过延迟执行实现的。所以没有必要应用foreach (...) yield return ...
模式。您正在为自己做更多的工作,并可能为运行时做更多的工作。
我不知道第二个版本是否与第一个版本相同。从语义上讲,两者的区别在于,第一个执行单轮延迟执行,而第二个执行两轮。基于这些理由,我认为第二个会更复杂。
您需要问的真正问题是:当您公开 IEnumerable 时,您要做出什么保证?你是说你想简单地提供前向迭代吗?或者你是说你的界面提供了延迟执行?
在下面的代码中,我的意图是简单地提供没有随机访问的前向枚举:
private List<Int32> _Foo = new List<Int32>() { 1, 2, 3, 4, 5 };
public IEnumerable<Int32> Foo
{
get
{
return _Foo;
}
}
但在这里,我想防止不必要的计算。我希望仅在请求结果时才执行昂贵的计算。
private List<Int32> _Foo = new List<Int32>() { 1, 2, 3, 4, 5 };
public IEnumerable<Int32> Foo
{
get
{
foreach (var item in _Foo)
{
var result = DoSomethingExpensive(item);
yield return result;
}
}
}
尽管这两个版本在外观上Foo
看起来相同,但它们的内部实现却做了不同的事情。这是您需要注意的部分。当您使用 LINQ 时,您无需担心延迟执行,因为大多数操作员都会为您执行此操作。在您自己的代码中,您可能希望根据需要使用第一个或第二个。