Enumerable.Zip强制执行相同的长度

KFL*_*KFL 6 c# linq

我发现自己经常需要使用Enumerable.Zip(),但要确保两个IEnumerables具有相同的长度(或两者都是无限的).例如,如果一个可枚举到达终点但另一个没有,我希望它抛出.根据文档,Zip()只要其中一个结束就会停止枚举.

我最终总是需要像下面这样的东西.解决这个问题的最"内置"/优雅方式是什么?

void Foo(IEnumerable<int> a, IEnumerable<int> b)
{
    // caching them. they are not huge or infinite in my scenario
    var a = a.ToList();
    var b = b.ToList();

    if (a.Count() != b.Count())
    {
        throw ...;
    }

    Enumerable.Zip(a, b, ...);
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 14

我可能只是重新实现Zip你想要的方式.这非常简单 - 以下内容可以从MoreLINQ中轻松改编.你会想给它一个更好的名字,请注意......

public static IEnumerable<TResult> ZipForceEqual<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
{
    if (first == null) throw new ArgumentNullException("first");
    if (second == null) throw new ArgumentNullException("second");
    if (resultSelector == null) throw new ArgumentNullException("resultSelector");

    return ZipForceEqualImpl(first, second, resultSelector);
}

static IEnumerable<TResult> ZipForceEqualImpl<TFirst, TSecond, TResult>(
    IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
{
    using (var e1 = first.GetEnumerator())
    using (var e2 = second.GetEnumerator())
    {
        while (e1.MoveNext())
        {
            if (e2.MoveNext())
            {
                yield return resultSelector(e1.Current, e2.Current);
            }
            else
            {
                throw new InvalidOperationException("Sequences differed in length");
            }
        }
        if (e2.MoveNext())
        {
            throw new InvalidOperationException("Sequences differed in length");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @KFL:嗯,其他任何东西都可能不止一次迭代序列,而不会缓冲所有结果,从而大大降低了它的实用性.鉴于它相当简单,我肯定会这样做.能够编写类似LINQ的方法很方便. (2认同)
  • @binki几年前,但它是一个模式,它允许`ZipForceEqual`验证参数并立即抛出它被调用的点,而不是在结果序列的枚举开始时.在`Impl`方法中使用`yield`将其转换为迭代器,这意味着直到第一个`yield`的所有代码仅在您尝试枚举返回值时运行.因此外部(非迭代器)方法立即进行验证,然后遵循迭代器.如今,C#具有本地函数,可以让您无需将`Impl`代码作为单独的方法公开. (2认同)