当使用内部具有嵌套条件的foreach循环时,我曾用以下方式编写:
foreach (RadioButton item in listOfRadioButtons)
{
if (item.IsChecked == true)
{
// sometging
}
}
但是我已经安装了ReSharper并建议将此循环更改为以下形式(删除if并使用lambda):
foreach (RadioButton item in listOfRadioButtons.Where(item => item.IsChecked == true))
{
// something
}
根据我的经验,ReSharper方式将循环两次:一次生成过滤的IEnumerable,然后再循环.Where查询的结果。
我是对的?如果是这样,为什么ReSharper建议这样做?因为在我看来,第一个也更可靠。
注意:WPF RadioButton的默认IsChecked属性是Nullable bool,因此需要在条件内使用== true,.Value或强制转换来返回bool。
答案 0 :(得分:5)
根据我的经验,ReSharper方式将循环两次:一次到 生成过滤后的IEnumerable,然后循环结果 。再次查询。
不,它只会循环一次。 Where
不会循环您的集合 - 它只会创建迭代器,用于枚举您的集合。以下是LINQ解决方案的样子:
using(var iterator = listOfRadioButtons.Where(rb => rb.IsChecked == true))
{
while(iterator.MoveNext())
{
RadioButton item = iterator.Current;
// something
}
}
您的原始代码更适合性能 - 您将避免创建委托并将其传递给WhereEnumerableIterator
的实例,然后为源序列中的每个项执行委托。但你应该注意,正如@dcastro指出的那样,差异将非常小,并且在你必须优化这个特定循环之前不值得注意。
ReSharper建议的解决方案(可能)更易于阅读。我个人喜欢循环中的简单if
条件。
UPDATE:Where
迭代器可以简化为(也省略了一些接口)
public class WhereEnumerableIterator<T> : IEnumerable<T>, IDisposable
{
private IEnumerator<T> _enumerator;
private Func<T,bool> _predicate;
public WhereEnumerableIterator(IEnumerable<T> source, Func<T,bool> predicate)
{
_predicate = predicate;
_enumerator = source.GetEnumerator();
}
public bool MoveNext()
{
while (_enumerator.MoveNext())
{
if (_predicate(_enumerator.Current))
{
Current = _enumerator.Current;
return true;
}
}
return false;
}
public T Current { get; private set; }
public void Dispose()
{
if (_enumerator != null)
_enumerator.Dispose();
}
}
这里的主要想法 - 它只在您要求它移动到下一个项目时枚举原始来源。然后迭代器转到原始源中的下一个项目并检查它是否与谓词匹配。如果找到匹配,则返回当前项并将枚举源置于保持状态。
因此,在您不会询问此迭代器中的项目之前,它不会枚举源代码。如果您将在此迭代器上调用ToList()
,它将枚举源序列并返回所有匹配的项目,这些项目将保存到新列表中。