2013-08-23 24 views
28

Hi là có một cách để làm những việc như thế này:sử dụng foreach để lặp đồng thời thông qua nhiều danh sách (cú pháp đường)

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

với Foreach?

bởi vì nó sẽ được tốt đẹp để viết một cái gì đó giống như

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

EDIT

ok xin lỗi tôi đã không đủ rõ ràng cho một số người vì vậy đây là lời giải thích tốt hơn hy vọng

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

Bây giờ tôi muốn thực hiện một số loại ForEach vì tôi không thích làm điều đó với một số cho loop nó phải được đơn giản và rõ ràng cũng giống như

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

AFAIK tôi không thể modifie như một từ Keay lớp điều vì vậy tôi nghĩ ok xây dựng iterator như Parallel.ForEach đã ForEach<TSource>(IEnumerable<TSource>, Action<TSource>) nhưng tôi cô có được stucked vì tôi don 't biết làm thế nào thực hiện nó

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

Trả lời

55

Bạn có thể làm những gì foreach không dưới mui xe, nhưng với hai điều tra viên:

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

Để thuận tiện, bạn có thể viết một phương pháp mở rộng như sau mà phải mất một hành động:

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

và sử dụng nó như:

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

Bằng cách này, bạn có thể mở rộng này để sử dụng 3 hoặc nhiều danh sách:

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

Phương pháp trên được yêu cầu khi các bộ sưu tập có khác nhau kiểu generic.Tuy nhiên, nếu tất cả họ đều có cùng chung loại, sau đó bạn có thể viết một phương pháp linh hoạt mà có bất kỳ số lượng 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(); 
    } 
} 

và sử dụng nó:

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

Câu trả lời của bạn có một số lợi thế nữa không? Câu trả lời của Ani? – WiiMaxx

+0

@WiiMaxx bạn có thể mở rộng phương pháp này cho 3 danh sách trở lên (xem chỉnh sửa), nhưng nếu bạn chỉ cần nén 2 danh sách, chúng giống nhau - thực tế không có sự khác biệt về hiệu suất hoặc khả năng đọc. –

+1

Điều gì sẽ xảy ra nếu một danh sách ngắn hơn một danh sách khác? Nó sẽ không dừng lại để lặp lại? – Kris

25

điều duy nhất tôi có thể nghĩ đến mà đến gần là Enumerable.Zip cùng với tuples:

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

Tất nhiên, nếu thay vì use, chúng tôi đã có một phương pháp phụ ảnh hưởng không mà tạo ra một giá trị thứ ba từ hai yếu tố này, bạn có thể làm:

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

vì vậy không có cách nào để tạo một số loại 'hành động'? tôi nghĩ rằng có thể có một cách để viết một cái gì đó giống như 'Parallel.Foreach()' – WiiMaxx

+0

Không có gì ngăn cản bạn sử dụng Parallel.ForEach ở đây thay vì ngôn ngữ foreach. Xin lỗi, tôi đang thiếu cái gì? – Ani

+0

nono i có nghĩa là trong Parallel.Foreach bạn có thể viết nó như Parallel.ForEach (danh sách, item => {// dostuff}) trong đó param = ienmum đầu tiên, tham số thứ hai = action. vì vậy tôi nghĩ tôi có thể viết một hàm như 'StaticClass.ForEach (Ienum, ienum, ??? someaction ???)' – WiiMaxx

4

bạn có thể sử dụng Zip phương pháp (mặc dù chỉ có sẵn trong .net 4 trở lên) một cái gì đó như thế này?

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

không chính xác vì typeof (a)! = Typeof (b) và nó cũng có thể tồn tại một danh sách c, d, ... – WiiMaxx

+0

loại không quan trọng ở đây, danh sách thứ hai có thể là loại chuỗi. – Ehsan

3

Bạn cũng có thể chỉ đơn giản sử dụng biến số nguyên cục bộ nếu các danh sách có cùng độ dài:

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

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