Подтвердить что ты не робот

Используя foreach для итерации одновременно через несколько списков (синтаксический сахар)

Привет, есть ли способ делать такие вещи:

for (int i = 0; i < Math.Min(a.Count, b.Count); i++)
{
    // Do stuff
    //a[i]
    //b[i]
}

с Foreach?

потому что было бы неплохо написать что-то вроде

foreach(var item1 in list1 and var item2 in list2 /* ....*/)
{
   item1.use(item2);
}

ИЗМЕНИТЬ

ОК, извините, я не был достаточно ясен для некоторых людей, поэтому здесь, надеюсь, лучшее объяснение.

List<classA> listA = fillListA();
List<classB> listB = fillListB();
//here could be infinity many lists of sometimes diffrent T types

Теперь я хочу выполнить какой-то ForEach, потому что я не люблю делать это с помощью цикла для это должно быть простым и ясным, что-то вроде

foreach(var item1 in list1 and var item2 in list2 /* and ...*/)
{
    item1.use(item2);
}

AFAIK я can not modifie такая вещь класса keay так что я думал, что ОК построит итератор, как Parallel.ForEach сделал ForEach<TSource>(IEnumerable<TSource>, Action<TSource>) но ее я застрял, потому что я не знаю, как реализовать его

Static.ForEach<TSource>(IEnumerable<TSource>,IEnumerable<TSource>, ???Action<TSource,???>????)
4b9b3361

Ответ 1

Вы можете сделать то, что foreach делает под капотом, но с двумя счетчиками:

using(var e1 = list1.GetEnumerator())
using(var e2 = list2.GetEnumerator())
{
    while(e1.MoveNext() && e2.MoveNext())
    {
         var item1 = e1.Current;
         var item2 = e2.Current;

         // use item1 and item2
    }
}

Для удобства вы можете написать метод расширения, например следующий, который принимает действие:

public static void ZipDo<T1, T2>( this IEnumerable<T1> first, IEnumerable<T2> second, Action<T1, T2> action)
{
    using (var e1 = first.GetEnumerator())
    using (var e2 = second.GetEnumerator())
    {
        while (e1.MoveNext() && e2.MoveNext())
        {
            action(e1.Current, e2.Current);
        }
    }
}

и используйте его как:

list1.ZipDo(list2, (i1,i2) => i1.Use(i2));

Кстати, вы можете развернуть это, чтобы использовать 3 или более списков:

public static void ZipDo<T1, T2, T3>(this IEnumerable<T1> first,
    IEnumerable<T2> second, IEnumerable<T3> third,
    Action<T1, T2, T3> action)
{
    using (var e1 = first.GetEnumerator())
    using (var e2 = second.GetEnumerator())
    using (var e3 = third.GetEnumerator())
    {
        while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
        {
            action(e1.Current, e2.Current, e3.Current);
        }
    }
}

Приведенный выше подход требуется, когда коллекции имеют разные общие типы. Однако, если все они имеют один и тот же общий тип, вы можете написать гибкий метод, который принимает любое число IEnumerable<T> s:

public static void ZipAll<T>(this IEnumerable<IEnumerable<T>> all, Action<IEnumerable<T>> action)
{
    var enumerators = all.Select(e => e.GetEnumerator()).ToList();
    try
    {
        while (enumerators.All(e => e.MoveNext()))
            action(enumerators.Select(e => e.Current));
    }
    finally
    {
        foreach (var e in enumerators) 
            e.Dispose();
    }
}

и используйте его:

var lists = new[] {
     new[]{ 1, 1, 1 }, 
     new[]{ 2, 2, 2 }, 
     new[]{ 3, 3, 3 }};

lists.ZipAll(nums => Console.WriteLine(nums.Sum()));
// 6
// 6
// 6

Ответ 2

Единственное, что я могу придумать, - это Enumerable.Zip вместе с кортежами:

foreach(var tuple in list1.Zip(list2, Tuple.Create))
{
    tuple.Item1.use(tuple.Item2);
}

Конечно, если вместо use у нас был метод без побочных эффектов, который произвел третье значение из двух элементов, вы могли бы сделать:

var result = list1.Zip(list2, (item1, item2) => item1.ProduceObject(item2))
                  .ToList(); // if required

Ответ 3

вы можете использовать Zip (хотя доступно только в .net 4 и выше) что-то вроде этого?

List<int> l4 = new List<int> { 1, 2, 3, 4 };
List<int> l5 = new List<int> { 5, 6, 7 };

var l4Andl5 = l4.Zip(l5, (l, m) => new { List1 = l, List2 = m });
foreach (var x in l4Andl5)
{


}

Ответ 4

Вы также можете просто использовать переменную локального целого, если списки имеют одинаковую длину:

List<classA> listA = fillListA();
List<classB> listB = fillListB();

var i = 0;
foreach(var itemA in listA)
{
    itemA.use(listB[i++]);
}