2010-06-09 40 views
6

Possible Duplicates:
Exception during iteration on collection and remove items from that collection
How to remove elements from a generic list while iterating around it?
Better way to remove matched items from a listthế nào đúng cách loại bỏ mục từ danh sách

// tmpClientList is List<Client> type 

if (txtboxClientName.Text != "") 
    foreach (Client cli in tmpClientList) 
     if (cli.Name != txtboxClientName.Text) 
      tmpClientList.Remove(cli); 

Lỗi: "Bộ sưu tập đã được sửa đổi; hoạt động liệt kê không thể thực hiện"

Làm cách nào để xóa các mục khỏi danh sách, một cách đơn giản mà không lưu chỉ mục của các mục này trong danh sách hoặc mảng khác và xóa chúng ở một vị trí khác trong mã. Đã thử cũng RemoveAt (chỉ mục) nhưng nó chính xác tình hình tương tự, sửa đổi khi chạy vòng lặp.

+0

Đồng ý về các vấn đề trùng lặp chính xác, xem liên kết Một http://stackoverflow.com/questions/1154325/better-way-to-remove-matched-items-from-a-list và liên kết hai: http: //stackoverflow.com/questions/1541777/can-you-remove-an-item-from-a-list-whilst-iterating-through-it-in-c – CrimsonX

Trả lời

1

Vấn đề là bạn đang cố gắng sửa đổi danh sách trong một lần lặp lại foreach. Thay thế bằng một cho và bạn nên được ok.

Ngoài ra, vì bạn dường như đang sử dụng đầu vào của người dùng cho tên, hãy cân nhắc dọn dẹp đầu vào một chút, ít nhất với Trim() để xóa thêm khoảng trắng. Nếu không, 'John' và 'John' sẽ là hai thứ khác nhau. Tương tự cho lần đầu tiên! = "" Kiểm tra.

+0

ý của bạn là "" và "" sẽ là tương tự, và tôi nên cắt đầu vào trong tình huống đó quá (khi người dùng chỉ nhập các ký tự trắng làm đầu vào)? – qlf00n

+0

@dygi: vâng. loại bỏ khoảng trống trắng chỉ để lại thông tin liên quan - mà trong trường hợp này là không. Đối với một trường hợp khi điều này có thể xảy ra: Người dùng bắt đầu nhập tên, bao gồm một khoảng trống sau đó, sau đó quyết định xóa các ký tự và không gian còn lại, vì anh ta không thể 'nhìn thấy' nó. – Rox

2

Không sử dụng foreach. Sử dụng và xuống danh sách (ví dụ: bắt đầu từ đầu), sử dụng RemoveAt.

Vì vậy,

// tmpClientList is List<Client> type 

if (txtboxClientName.Text != "") 
    foreach (int pos = tmpClientList.Length - 1; pos >= 0; pos--) 
    { 
     Client cli = tmpClientList[pos]; 
     if (cli.Name != txtboxClientName.Text) 
      tmpClientList.RemoveAt(pos); 
    } 
4

Hoặc sử dụng một cho/trong khi vòng lặp, hoặc tmpClientList.RemoveAll(a => a.Name == txtboxClientName.Text). Vì bạn không chỉ định phiên bản C# nào bạn đang sử dụng, ymmw.

+1

Và khi giới hạn trong 2.0, nó chỉ là một chi tiết hơn một chút: 'tmpClientList.RemoveAll (delegate (Client a) {return a.Name == txtboxClientName.Text;}); ' – Humberto

11

Di chuyển về phía sau qua danh sách .. bằng cách đó việc xóa một mục không ảnh hưởng đến mục tiếp theo.

for(var i=tmpClientList.Count-1;i>=0;i--) 
{ 
    if (tmpClientList[i].Name != txtboxClientName.Text) 
      tmpClientList.RemoveAt(i); 

} 
+0

giải pháp đơn giản, đẹp, nhờ chia sẻ – qlf00n

11

Trên một List<T>, có một phương pháp RemoveAll mà phải mất một đại biểu để cho biết để loại bỏ mặt hàng đó. Bạn có thể sử dụng nó như thế này:

tmpCLientList.RemoveAll(cli => cli.Name != txtboxClientName.Text); 
+0

+1 Một nên lưu ý rằng điều này chỉ hoạt động cho C# 3.0 trở lên. OP đã gắn thẻ câu hỏi với các phiên bản 2, 3 và 4 :-) –

+1

@Jakob: Nếu OP không sử dụng C# 3 hoặc mới hơn thì họ có thể gọi 'RemoveAll' bằng cú pháp ủy nhiệm cũ-skool:' tmpCLientList.RemoveAll (delegate (Client cli) {return cli.Name! = txtboxClientName.Text;}); ' – LukeH

+0

Ồ đúng - nghĩ rằng RemoveAll là một phương thức mở rộng. Xin lỗi về điều đó :-) –

1

Bạn có thể tạo một danh sách với các mục mà bạn muốn xóa và lặp danh sách mới để xoá các mục từ danh sách "txtboxClientName" của bạn.

+0

Phải, tôi có thể sử dụng các phương thức Chứa() và Xoá() trên danh sách sao chép, trong khi lặp lại trên bản gốc. Cảm ơn. – qlf00n

1

Thực ra, foreach sử dụng Các toán tử để lặp lại thông qua Mục-Bộ sưu tập đã cho. Đi xa hơn, hãy thực hiện System.Collections.Generic.List<T>IEnumarable-Interface đến provide a Class, biết cách lặp qua các mục trong danh sách, ví dụ: Enumerator. Bây giờ nếu bạn lặp qua danh sách đó bằng cách sử dụng foreach the Enumerator theo dõi vị trí hiện tại, làm thế nào để đạt được vị trí tiếp theo và một số thứ khác. Logic nội bộ có thể giống như lưu trữ số lượng các mục trong một biến n và sau đó truy cập tất cả các đối tượng từ 0 đến n-1. Như bạn có thể nhận thấy nếu bất kỳ đối tượng nào bị xóa giữa các bước lặp lại, chúng ta sẽ kết thúc bằng một số NullReferenceException khi Toán tử cố gắng phân phối đối tượng cuối cùng của danh sách. Vì vậy, để ngăn chặn bất kỳ lỗi lặp nào, danh sách chính nó không được phép sửa đổi trong quá trình liệt kê.

Hy vọng tôi có thể nêu rõ điều đó một cách toàn diện một chút. :-)

+0

cảm ơn thông tin rất chính xác, GTK nó hoạt động như thế nào trong nội bộ – qlf00n

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