2010-07-12 22 views
12

Tôi muốn làm một vòng lặp foreach trong khi lấy ra các thành viên của vòng lặp foreach đó, nhưng đó là ném lỗi. Ý tưởng duy nhất của tôi là tạo một danh sách khác bên trong vòng lặp này để tìm Slices cần xóa và lặp qua danh sách mới để xóa các mục khỏi Pizza.Đi qua A Foreach khi nó có thể được sửa đổi?

foreach(var Slice in Pizza) 
{ 
    if(Slice.Flavor == "Sausage") 
    { 
     Me.Eat(Slice); //This removes an item from the list: "Pizza" 
    } 
} 
+0

... số những người khác – johnc

+1

1 pizza xúc xích thơm ngon. –

Trả lời

24

Bạn có thể làm điều này, bởi xa cách đơn giản nhất tôi đã tìm thấy (thích nghĩ tôi phát minh ra nó, chắc chắn đó là không đúng sự thật mặc dù;))

foreach (var Slice in Pizza.ToArray()) 
{ 
    if (Slice.Flavor == "Sausage") // each to their own.. would have gone for BBQ 
    { 
     Me.Eat(Slice); 
    } 
} 

..bởi vì nó đang lặp qua bản sao cố định của vòng lặp. Nó sẽ lặp lại tất cả các mục, ngay cả khi chúng được gỡ bỏ.

Tiện dụng phải không!

(Nhân tiện, đây là cách tiện dụng để lặp qua bản sao bộ sưu tập, với an toàn luồng và xóa thời gian đối tượng bị khóa: Khóa, lấy bản sao ToArray(), nhả khóa, sau đó lặp lại)

Hy vọng điều đó sẽ hữu ích!

+0

GENIUSES TẤT CẢ CÁC NƠI TRÊN AT, (và không phải là những người douchey Apple) Cảm ơn rất nhiều !!! : D – sooprise

+0

oau .. điều này nên đi đến C# hall of fame tips. – Radu094

+1

Joe Rattz đề cập đến điều này là "Vấn đề Halloween" trong Pro LINQ: http://books.google.com/books?id=12HliU9_D_kC&pg=PA203&lpg=PA203&dq=C%23+halloween+problem&source=bl&ots=3dvK3hqAd0&sig=bo4hgkhYHsKwKk4OznB1xuDB25g&hl= vi & ei = W5A7TLzbFYL7lwf_gtD5BQ & sa = X & oi = book_result & ct = kết quả & resnum = 4 & ved = 0CCQQ6AEwAw # v = onepage & q & f = false –

4

sử dụng một vòng lặp for không phải là một foreach

for(int i = 0; i < in Pizza.Count(), ++i) 
{ 

    var Slice = Pizza[i]; 
    if(Slice.Flavor == "Sausage") 
    { 
     Me.Eat(Slice); //This removes an item from the list: "Pizza" 
    } 
} 
+3

Tôi sẽ lặp lại ngược lại, nếu không thì "var Slice = Pizza [i];" cuộc gọi sẽ ném một IndexOutOfRange hoặc ngoại lệ tương tự. –

+0

(FYI Sửa lỗi nhẹ với "i

+0

Trong khi điều này giải quyết được vấn đề, điều này làm tăng khả năng mắc lỗi truy cập từng người một mà ném các ngoại lệ thời gian chạy. Trừ khi bạn thực sự có một cách sử dụng cho giá trị chỉ mục của một mục trong danh sách, tôi muốn xem và làm việc với mã "an toàn hơn" như câu trả lời của @ spender. –

3

Có lẽ cách rõ ràng nhất để tiếp cận điều này là xây dựng danh sách các lát để ăn, sau đó xử lý nó, tránh thay đổi điều tra ban đầu trong vòng lặp. Tôi đã không bao giờ là một fan hâm mộ của việc sử dụng các vòng lập chỉ mục cho điều này, vì nó có thể dễ bị lỗi.

List<Slice> slicesToEat=new List<Slice>(); 
foreach(var Slice in Pizza) 
{ 
    if(Slice.Flavor == "Sausage") 
    { 
     slicesToEat.Add(Slice); 
    } 
} 
foreach(var slice in slicesToEat) 
{ 
    Me.Eat(slice); 
} 
+0

+1 tại đây để giảm khả năng xảy ra lỗi. (-1 cho câu trả lời của @ Preet cho cùng một lý do ngược lại). –

7

Nếu bạn phải lặp qua một danh sách và cần phải loại bỏ các mục, lặp ngược sử dụng vòng lặp for:

// taken from Preet Sangha's answer and modified 
for(int i = Pizza.Count-1; i >= 0, i--) 
{ 
    var Slice = Pizza[i]; 
    if(Slice.Flavor == "Sausage") 
    { 
     Me.Eat(Slice); //This removes an item from the list: "Pizza" 
    } 
} 

Lý do để lặp ngược là để khi bạn loại bỏ yếu tố bạn don' t chạy vào một IndexOutOfRangeException đó là do truy cập Pizza [5] trên một Pizza mà chỉ có 5 yếu tố bởi vì chúng tôi loại bỏ thứ sáu.

Lý do để sử dụng một vòng lặp for là do biến iterator i không có liên quan đến Pizza, vì vậy bạn có thể thay đổi Pizza mà không có sự điều tra viên "phá vỡ"

2

Có lẽ thay đổi chữ ký Me.Eat() của bạn để có một IEnumerable<Slice>

Me.Eat(Pizza.Where(s=>s.Flavor=="Sausage").ToList()); 

Điều này cho phép bạn thực hiện tác vụ trong 1 dòng mã.

Sau đó Eat() của bạn có thể như:

public void Eat(IEnumerable<Slice> remove) 
{ 
    foreach (Slice r in remove) 
    { 
     Pizza.Remove(r); 
    } 
} 
+1

Điều này giải quyết vấn đề như thế nào? ** Trình tự vẫn đang được sửa đổi trong khi nó đang được lặp lại **. Hãy nhớ, Pizza.Where là lười biếng; nó không lặp qua bộ sưu tập cho đến khi được yêu cầu, và không ai yêu cầu lặp lại bộ sưu tập cho đến khi tìm kiếm ở Eat, sau đó thực hiện việc xóa. Những gì bạn muốn làm là tính toán tập hợp các thứ cần xóa * háo hức * và sau đó xóa chúng * sau * tập đã được tính toán, không * trong khi * nó đang được tính toán. –

+0

@Eric: cảm ơn vì đã chú ý đến điều này. Tôi đã thực hiện một sự thay đổi cho câu trả lời, nhờ đó việc tính toán các lát được loại bỏ thực sự được tạo ra háo hức, không lười biếng. Bất kỳ suy nghĩ hoặc ý kiến ​​được đánh giá cao! –

0

Các "Bộ sưu tập" đối tượng VB6 theo kiểu cho phép sửa đổi trong điều tra, và dường như làm việc một cách hợp lý khi thay đổi như vậy xảy ra. Quá xấu nó có những hạn chế khác (loại khóa được giới hạn trong chuỗi phân biệt chữ hoa chữ thường) và không hỗ trợ Generics, vì không có loại bộ sưu tập nào khác cho phép sửa đổi.

Thành thật mà nói, tôi không rõ lý do tại sao hợp đồng iEnumerable của Microsoft yêu cầu một ngoại lệ được ném nếu một bộ sưu tập được sửa đổi. Tôi sẽ hiểu một yêu cầu rằng một ngoại lệ sẽ bị ném nếu một thay đổi đối với một bộ sưu tập sẽ khiến cho một điều tra không thể tiếp tục mà không có sự lúng túng (bỏ qua hoặc nhân đôi các giá trị không thay đổi trong quá trình đếm, rơi, v.v.) nhưng không thấy lý do gì để không cho phép một bộ sưu tập có thể liệt kê một cách hợp lý để làm như vậy.

0

Bạn có thể yêu cầu pizza ở đâu lát có lớp trên bề mặt riêng? Dù sao ...

Sử dụng LINQ:

// Was "Me.Eat()" supposed to be "this.Eat()"? 
Pizza 
    .Where(slice => slice.Flavor == "Sausage") 
    .Foreach(sausageSlice => { Me.Eat(sausageSlice); });  

Hai dòng đầu tiên tạo ra một danh sách mới chỉ với xúc xích lát. Thứ ba sẽ lấy tập hợp con mới và chuyển từng lát sang Me.Eat(). {Và;} có thể không cần thiết. Đây không phải là phương pháp hiệu quả nhất bởi vì nó đầu tiên tạo một bản sao (cũng như nhiều phương pháp khác đã được đưa ra), nhưng nó chắc chắn là sạch sẽ và dễ đọc.

BTW, Đây chỉ dành cho hậu thế vì câu trả lời hay nhất đã được đưa ra - lặp lại theo chỉ mục.

0

Loại bộ sưu tập nào là Pizza? Nếu đó là một danh sách <T> sau đó bạn có thể gọi phương thức RemoveAll:

Pizza.RemoveAll(slice => string.Equals(slice.Flavor, "Sausage")); 
+0

Nếu 'Eat' làm nhiều hơn là chỉ loại bỏ các slice từ bộ sưu tập (và kiến ​​thức vắng mặt của các bên trong' Eat', chúng ta phải giả định nó), thì điều này là sai. Bạn muốn ăn * các lát, không chỉ ném chúng đi. – cHao

Các vấn đề liên quan