安全检查不可重复IEnumerables的空虚 [英] Safely checking non-repeatable IEnumerables for emptiness

查看:166
本文介绍了安全检查不可重复IEnumerables的空虚的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有些时候是有帮助的检查的不可重复的IEnumerable 来看看它是否是空的。 LINQ的任何不适合这项工作做得很好,因为它消耗的序列的第一个元素,例如:

There are times when it's helpful to check a non-repeatable IEnumerable to see whether or not it's empty. LINQ's Any doesn't work well for this, since it consumes the first element of the sequence, e.g.

if(input.Any())
{
    foreach(int i in input)
    {
        // Will miss the first element for non-repeatable sequences!
    }
}

(注:我知道,有没有必要做的检查在这种情况下 - 这只是一个例子,实际的例子是执行邮编对!右手<​​code>的IEnumerable 这有可能是空的。如果它是空的,我希望得到的结果是左手的IEnumerable 原样。)

(Note: I'm aware that there's no need to do the check in this case - it's just an example! The real-world example is performing a Zip against a right-hand IEnumerable that can potentially be empty. If it's empty, I want the result to be the left-hand IEnumerable as-is.)

我想出了一个潜在的解决方案是这样的:

I've come up with a potential solution that looks like this:

private static IEnumerable<T> NullifyIfEmptyHelper<T>(IEnumerator<T> e)
{
    using(e)
    {
        do
        {
            yield return e.Current;
        } while (e.MoveNext());
    }
}

public static IEnumerable<T> NullifyIfEmpty<T>(this IEnumerable<T> source)
{
    IEnumerator<T> e = source.GetEnumerator();
    if(e.MoveNext())
    {
        return NullifyIfEmptyHelper(e);
    }
    else
    {
        e.Dispose();
        return null;
    }
}

这然后可以使用如下:

input = input.NullifyIfEmpty();
if(input != null)
{
    foreach(int i in input)
    {
        // Will include the first element.
    }
}

我对这个两个问题:

I have two questions about this:

1),这是一个合理的事是什么?是不是可能是从性能的角度来看问题? (我想不会,但值得一问。)

1) Is this a reasonable thing to do? Is it likely to be problematic from a performance point of view? (I'd guess not, but worth asking.)

2)是否有实现相同的最终目标的一个更好的办法?

2) Is there a better way of achieving the same end goal?

编辑#1:

下面是一个例子一个不可重复的的IEnumerable ,以澄清:

Here's an example of a non-repeatable IEnumerable, to clarify:

private static IEnumerable<int> ReadNumbers()
{
    for(;;)
    {
        int i;
        if (int.TryParse(Console.ReadLine(), out i) && i != -1)
        {
            yield return i;
        }
        else
        {
            yield break;
        }
    }
}

基本上,东西是来自用户的输入或流等。

Basically, things which come from user input or a stream, etc.

编辑#2:

我要澄清,我正在寻找一个解决方案,preserves中的懒人的的的IEnumerable 性质 - 将其转换为列表或数组可以在某些情况下一个答案,但不是我以后在这里我。 (真实世界的原因是,在的IEnumerable 的项目数量可能是巨大的在我的情况,而不是把它们全部储存在内存中一次很重要。)

I need to clarify that I'm looking for a solution that preserves the lazy nature of the IEnumerable - converting it to a list or an array can be an answer in certain circumstances, but isn't what I'm after here. (The real-world reason is that the number of items in the IEnumerable may be huge in my case, and it's important not to store them all in memory at once.)

推荐答案

您不必复杂化。普通的的foreach 循环使用一个额外的布尔变量会做的伎俩。

You don't need to complicate it. A regular foreach loop with a single extra bool variable will do the trick.

如果您有

if(input.Any())
{
    A
    foreach(int i in input)
    {
        B
    }
    C
}

和你不想读输入两次,你可以更改为

and you don't want to read input twice, you can change this to

bool seenItem = false;
foreach(int i in input)
{
    if (!seenItem)
    {
        seenItem = true;
        A
    }
    B
}
if (seenItem)
{
    C
}

根据什么 B 呢,你可能能够避免 seenItem 变量完全。

Depending on what B does, you may be able to avoid the seenItem variable entirely.

在你的情况, Enumerable.Zip 是很容易重新实现一个相当基本的功能,和你的替换功能可以使用类似于上面的东西。

In your case, Enumerable.Zip is a fairly basic function that is easily reimplemented, and your replacement function can use something similar to the above.

修改的:你可以考虑

public static class MyEnumerableExtensions
{
    public static IEnumerable<TFirst> NotReallyZip<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TFirst> resultSelector)
    {
        using (var firstEnumerator = first.GetEnumerator())
        using (var secondEnumerator = second.GetEnumerator())
        {
            if (secondEnumerator.MoveNext())
            {
                if (firstEnumerator.MoveNext())
                {
                    do yield return resultSelector(firstEnumerator.Current, secondEnumerator.Current);
                    while (firstEnumerator.MoveNext() && secondEnumerator.MoveNext());
                }
            }
            else
            {
                while (firstEnumerator.MoveNext())
                    yield return firstEnumerator.Current;
            }
        }
    }
}

这篇关于安全检查不可重复IEnumerables的空虚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆