2013-08-23 11 views
28

Hi czy istnieje sposób, aby robić rzeczy tak:używając foreach do iteracji jednocześnie przez wielu list (cukier składnia)

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

z foreach?

ponieważ byłoby miło, aby napisać coś jak

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

EDIT

ok Przepraszam, że nie było wystarczająco jasne dla niektórych ludzi, więc tutaj jestem nadzieją lepszego wyjaśnienia

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

teraz chcę wykonać pewnego rodzaju ForEach, ponieważ nie chcę tego zrobić z dla pętla powinno być proste i jasne jak coś

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

AFAIK cant Modifié taki Keay słowo klasa rzeczą więc pomyślałem ok budować iteracyjnej jak Parallel.ForEach zrobił ForEach<TSource>(IEnumerable<TSource>, Action<TSource>) ale ją dostanę stucked bo don „t wiedzieć, jak wdrożyć go

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

Odpowiedz

55

Możesz robić co foreach ma pod maską, ale z dwóch rachmistrzów:

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 
    } 
} 

Dla wygody, można napisać metodę rozszerzenia jak poniżej, które ma działanie:

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); 
     } 
    } 
} 

i używać go jak:

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

Nawiasem mówiąc, można rozszerzyć, aby używać 3 lub więcej list:

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); 
     } 
    } 
} 

Podejście powyżej jest wymagana, gdy zbiory mają różne typy generyczne.Jednakże, jeśli wszystkie mają samo typu rodzajowego, to może napisać elastyczną metodę, która pobiera dowolną liczbę 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(); 
    } 
} 

i używać go:

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 
+0

czy twoja odpowiedź ma jakieś zalety, jeśli nie odpowie Ani? – WiiMaxx

+0

@ WiiMaxx można rozszerzyć to podejście dla 3 lub więcej list (patrz edycja), ale jeśli potrzebujesz tylko zip 2 list, są one prawie takie same - praktycznie nie ma różnicy wydajności ani czytelności. –

+1

Co zrobić, jeśli jedna lista jest krótsza od drugiej? Czy to się nie skończy? – Kris

25

Jedyne co mogę myśleć, że zbliża się Enumerable.Zip wraz z tuples:

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

oczywiście, jeśli zamiast use, mieliśmy non boczny-dokonujące sposób, że wytwarzany trzecią wartość z dwóch elementów, można zrobić:

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

, więc nie ma sposobu na stworzenie "akcji"? Pomyślałem, że może istnieć sposób na napisanie czegoś takiego jak "Parallel.Foreach()' – WiiMaxx

+0

Nic nie powstrzyma cię przed użyciem Parallel.ForEach tutaj zamiast foreach języka. Przepraszam, brakuje mi czegoś? – Ani

+0

nono mam na myśli w Parallel.Foreach możesz napisać to jak Parallel.ForEach (list, item => {// weff}) gdzie first param = ienmum, second param = action. więc pomyślałem, że mógłbym napisać funkcję typu "StaticClass.ForEach (Ienum, ienum, ??? someaction ???)' – WiiMaxx

4

można użyć Zip metody (choć tylko dostępne w .net 4 i wyżej) coś takiego?

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) 
{ 


} 
+0

no nie dokładnie dlatego, że typeof (a)! = Typeof (b) i może również istnieć lista c, d, ... – WiiMaxx

+0

typy nie mają tutaj znaczenia, druga lista może być typu string. – Ehsan

3

Można też po prostu użyj zmiennej lokalnej liczby całkowitej, jeśli lista ma tę samą długość:

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

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