2011-01-17 31 views
12

Rất lâu tôi cần một cái gì đó như thế:Thực tiễn tốt nhất để tránh InvalidOperationException: Bộ sưu tập đã được sửa đổi?

foreach (Line line in lines) 
{ 
    if (line.FullfilsCertainConditions()) 
    { 
     lines.Remove(line) 
    } 
} 

này không làm việc, bởi vì tôi luôn có được một InvalidOperationException vì Enumerator được thay đổi trong vòng lặp.

Vì vậy, tôi đã thay đổi tất cả các vòng của tôi thuộc loại này như sau:

List<Line> remove = new List<Line>(); 
foreach (Line line in lines) 
{ 
    if (line.FullfilsCertainConditions()) 
    { 
     remove.Add(line) 
    } 
} 

foreach (Line line in remove) { 
{ 
    lines.Remove(line); 
} 

Tôi không chắc chắn nếu điều này thực sự là cách tốt nhất vì trong trường hợp xấu nhất tôi phải lặp 2 lần so với ban đầu danh sách và vì vậy nó cần thời gian 2n thay vì n.

Có cách nào tốt hơn để thực hiện việc này không?

EDIT:

tôi đã có thể làm điều đó bằng câu trả lời của Mark Nhưng nếu bộ sưu tập của tôi không cụ RemoveAll()?

Ví dụ một

System.Windows.Controls.UIElementCollection

EDIT 2:

Một lần nữa với sự giúp đỡ của Mark Tôi bây giờ có thể thực hiện cuộc gọi sau đây để loại bỏ tất cả ScatterViewItems:

CollectionUtils.RemoveAll(manager.getWindow().IconDisplay.Items, elem => elem.GetType() == typeof(ScatterViewItem)); 
+0

tôi gặp vấn đề tương tự một thời gian trước và không có giải pháp. Nó thậm chí còn tồi tệ hơn - nó không phải là 2n nhưng n^2 vì 'dòng.Remove (dòng)' lặp lại một lần nữa trên bộ sưu tập. – Matten

+0

Nhưng O (2n) bằng O (n) :-) thực sự: Trong Java, bạn có thể sử dụng Iterator để thực hiện việc này hoặc thực hiện việc thực hiện Sao chép ghi trên bản ghi cho phép sửa đổi trong khi lặp lại. – Waldheinz

+0

Cập nhật lại - xem chỉnh sửa của tôi –

Trả lời

17

Đây là trực tiếp nướng ly vào List<T>:

lines.RemoveAll(line => line.FullfilsCertainConditions()); 

hoặc trong C# 2.0:

lines.RemoveAll(delegate(Line line) { 
    return line.FullfilsCertainConditions(); 
}); 

Trong List<T> trường hợp không (chỉnh sửa của bạn cho câu hỏi), bạn có thể quấn một cái gì đó này như dưới đây (chưa được kiểm tra) :

static class CollectionUtils 
{ 
    public static void RemoveAll<T>(IList<T> list, Predicate<T> predicate) 
    { 
     int count = list.Count; 
     while (count-- > 0) 
     { 
      if (predicate(list[count])) list.RemoveAt(count); 
     } 
    } 
    public static void RemoveAll(IList list, Predicate<object> predicate) 
    { 
     int count = list.Count; 
     while (count-- > 0) 
     { 
      if (predicate(list[count])) list.RemoveAt(count); 
     } 
    } 
} 

UIElementCollection thực hiện (không chung) IList điều này sẽ làm việc. Và khá thuận tiện, với C# 3.0, bạn có thể thêm this trước IList/IList<T> và đặt nó làm phương thức mở rộng. Sự tinh tế duy nhất là tham số cho phương thức anon sẽ là object, vì vậy bạn cần phải bỏ nó đi.

+0

Có cách nào để đạt được điều tương tự trong .NET 2.0 không? – Matten

+2

@Matten - đã thêm ví dụ C# 2.0. –

+0

@Marc Gravell - rất đơn giản, rất thanh lịch. Cảm ơn :) – Matten

1

Bạn chỉ có thể thay thế danh sách ban đầu với lọc một:

lines = lines.Where(line => line.FullfilsCertainConditions()).ToList(); 
1

Xây dựng một danh sách mới instaed:

public IList<Line> GetListWithoutFullfilsCertainConditions(IList<Line> fullList) 
{ 
    IList<Line> resultList = new List<Line>(fullList.Count); 

    foreach (Line line in fullList) 
    { 
     if (!line.FullfilsCertainConditions()) 
     { 
      resultList.Add(line) 
     } 
    } 

    return resultList; 
} 
+0

Lý do bỏ phiếu xuống, xin vui lòng? –

1

Ngoài ra, bạn chỉ có thể sử dụng vòng lặp while.

int i = 0; 
while(i < lines.Count) 
{ 
    if (lines[i].FullfilsCertainConditions()) 
    { 
    lines.RemoveAt(i); 
    } 
    else {i++;} 
} 
Các vấn đề liên quan