c# - 以 IEnumerable<T> 序列作为参数调用方法,如果该序列不为空

标签 c# ienumerable enumerator yield-return

我有方法 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/

相关文章:

c# - 从 Gmail 中获取已读和未读邮件

c# - 可以获取 DynamicObject 的链式值吗?

c# - 清除网络浏览器控件中的选择

c# - asp.net如何区分服务器端的并发进度条请求

ruby - `File.enum_for(:readlines, ...)` 未枚举

c# - 为什么枚举空数组不在堆上分配?

ruby - 什么是枚举器对象? (使用 String#gsub 创建)

c# - 拆分逗号分隔的字符串并比较列表中的每个值

c# - 奇怪,IEnumerable.ToList() 创建了全新的对象

c# - 对 IEnumerable<T> 以外的类型(monads?)进行操作的 LINQ 查询表达式——可能的用途?