如何同时迭代两个IEnumerables?

Dan*_*vil 53 c# iteration ienumerable

我有两个枚举:IEnumerable<A> list1IEnumerable<B> list2.我想同时迭代它们,如:

foreach((a, b) in (list1, list2))
{
    // use a and b
}
Run Code Online (Sandbox Code Playgroud)

如果它们不包含相同数量的元素,则应抛出异常.

做这个的最好方式是什么?

Jon*_*eet 50

你想要ZipLINQ运算符之类的东西- 但是.NET 4中的版本总是只在序列完成时截断.

MoreLINQ实现EquiZip这将抛出一个方法InvalidOperationException来代替.

var zipped = list1.EquiZip(list2, (a, b) => new { a, b });

foreach (var element in zipped)
{
    // use element.a and element.b
}
Run Code Online (Sandbox Code Playgroud)


ang*_*son 32

这是此操作的一个实现,通常称为Zip:

using System;
using System.Collections.Generic;

namespace SO2721939
{
    public sealed class ZipEntry<T1, T2>
    {
        public ZipEntry(int index, T1 value1, T2 value2)
        {
            Index = index;
            Value1 = value1;
            Value2 = value2;
        }

        public int Index { get; private set; }
        public T1 Value1 { get; private set; }
        public T2 Value2 { get; private set; }
    }

    public static class EnumerableExtensions
    {
        public static IEnumerable<ZipEntry<T1, T2>> Zip<T1, T2>(
            this IEnumerable<T1> collection1, IEnumerable<T2> collection2)
        {
            if (collection1 == null)
                throw new ArgumentNullException("collection1");
            if (collection2 == null)
                throw new ArgumentNullException("collection2");

            int index = 0;
            using (IEnumerator<T1> enumerator1 = collection1.GetEnumerator())
            using (IEnumerator<T2> enumerator2 = collection2.GetEnumerator())
            {
                while (enumerator1.MoveNext() && enumerator2.MoveNext())
                {
                    yield return new ZipEntry<T1, T2>(
                        index, enumerator1.Current, enumerator2.Current);
                    index++;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = new[] { 1, 2, 3, 4, 5 };
            string[] names = new[] { "Bob", "Alice", "Mark", "John", "Mary" };

            foreach (var entry in numbers.Zip(names))
            {
                Console.Out.WriteLine(entry.Index + ": "
                    + entry.Value1 + "-" + entry.Value2);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果只有其中一个序列用完了值,则使其抛出异常,更改while循环,以便:

while (true)
{
    bool hasNext1 = enumerator1.MoveNext();
    bool hasNext2 = enumerator2.MoveNext();
    if (hasNext1 != hasNext2)
        throw new InvalidOperationException("One of the collections ran " +
            "out of values before the other");
    if (!hasNext1)
        break;

    yield return new ZipEntry<T1, T2>(
        index, enumerator1.Current, enumerator2.Current);
    index++;
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*eer 17

简而言之,该语言没有提供干净的方法来做到这一点.枚举的目的是一次完成一个可枚举.你可以很容易地模仿foreach为你做的事情:

using(IEnumerator<A> list1enum = list1.GetEnumerator())
using(IEnumerator<B> list2enum = list2.GetEnumerator())    
while(list1enum.MoveNext() && list2enum.MoveNext()) {
        // list1enum.Current and list2enum.Current point to each current item
    }
Run Code Online (Sandbox Code Playgroud)

如果它们长度不同,该怎么办取决于你.也许在while循环完成后找出哪个仍然有元素并继续使用那个元素,如果它们应该是相同的长度,则抛出异常等.

  • 这段代码有问题,因为它无法处理枚举器. (10认同)

Ant*_*ram 5

在.NET 4中,您可以在.NET中使用.Zip扩展方法 IEnumerable<T>

IEnumerable<int> list1 = Enumerable.Range(0, 100);
IEnumerable<int> list2 = Enumerable.Range(100, 100);

foreach (var item in list1.Zip(list2, (a, b) => new { a, b }))
{
    // use item.a and item.b
}
Run Code Online (Sandbox Code Playgroud)

但是,它的长度不会相等。不过,您始终可以进行测试。