我有方法 Foo
,执行一些 CPU 密集型计算并返回 IEnumerable<T>
顺序。我需要检查该序列是否为空。如果没有,调用方法 Bar
以该序列作为参数。
我考虑了三种方法...
- 检查
Any()
序列是否为空.这没关系,如果序列真的是空的,大多数时候都是这样。但如果序列包含一些元素和Foo
,它的性能会很糟糕。将需要他们再次计算... - 将序列转换为列表,检查该列表是否为空...并将其传递给
Bar
.这也有局限性。Bar
只需要先x
项目,所以Foo
会做不必要的工作... - 检查序列是否为空而不实际重置序列。这听起来像是双赢,但我找不到任何简单的内置方法,如何去做。因此,我创建了这个晦涩的解决方法,并想知道这是否真的是最佳方法。
条件
var source = Foo();
if (!IsEmpty(ref source))
Bar(source);
与 IsEmpty
实现为
bool IsEmpty<T>(ref IEnumerable<T> source)
{
var enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{
source = CreateIEnumerable(enumerator);
return false;
}
return true;
IEnumerable<T> CreateIEnumerable(IEnumerator<T> usedEnumerator)
{
yield return usedEnumerator.Current;
while (usedEnumerator.MoveNext())
{
yield return usedEnumerator.Current;
}
}
}
另请注意,调用 Bar
空序列不是选项...
编辑: 经过一番考虑,我的案例的最佳答案来自 Olivier Jacot-Descombes - 完全避免这种情况。公认的解决方案回答了这个问题 - 如果真的没有其他办法的话。
最佳答案
我不知道您在 Foo
中的算法是否允许在不进行计算的情况下确定枚举是否为空。但如果是这种情况,如果序列为空,则返回 null
:
public IEnumerable<T> Foo()
{
if (<check if sequence will be empty>) {
return null;
}
return GetSequence();
}
private IEnumerable<T> GetSequence()
{
...
yield return item;
...
}
请注意,如果方法使用yield return
,则不能使用简单的return
来返回null
。因此需要第二种方法。
var sequence = Foo();
if (sequence != null) {
Bar(sequence);
}
阅读您的评论后
Foo need to initialize some resources, parse XML file and fill some HashSets, which will be used to filter (yield) returned data.
我建议另一种方法。耗时的部分似乎是初始化。为了能够将它与迭代分开,创建一个 foo 计算器类。像这样的东西:
public class FooCalculator<T>
{
private bool _isInitialized;
private string _file;
public FooCalculator(string file)
{
_file = file;
}
private EnsureInitialized()
{
if (_isInitialized) return;
// Parse XML.
// Fill some HashSets.
_isInitialized = true;
}
public IEnumerable<T> Result
{
get {
EnsureInitialized();
...
yield return ...;
...
}
}
}
这确保了代价高昂的初始化工作只执行一次。现在您可以安全地使用 Any()
。
其他优化是可以想象的。 Result
属性可以记住第一个返回元素的位置,因此如果再次调用它,它可以立即跳到那里。
关于c# - 以 IEnumerable<T> 序列作为参数调用方法,如果该序列不为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53582900/