2008-11-28 29 views
9

Tôi đang cố gắng cập nhật một hashtable trong một vòng lặp nhưng nhận được một lỗi: System.InvalidOperationException: Bộ sưu tập đã được sửa đổi; hoạt động điều tra có thể không thực hiện được.Làm thế nào để cập nhật C# hashtable trong một vòng lặp?

private Hashtable htSettings_m = new Hashtable(); 
htSettings_m.Add("SizeWidth", "728"); 
htSettings_m.Add("SizeHeight", "450"); 
string sKey = ""; 
string sValue = ""; 
foreach (DictionaryEntry deEntry in htSettings_m) 
{ 
    // Get value from Registry and assign to sValue. 
    // ... 
    // Change value in hashtable. 
    sKey = deEntry.Key.ToString(); 
    htSettings_m[sKey] = sValue; 
} 

Có cách nào xung quanh hoặc có thể có cấu trúc dữ liệu tốt hơn cho mục đích đó?

+0

Tin rằng đây là một câu hỏi dup xem: http://stackoverflow.com/questions/287195/how-to-add-items-to-a-collection -nút-tiêu thụ-nó –

Trả lời

14

bạn có thể đọc các tập hợp các phím vào một ví dụ IEnumerable đầu tiên, sau đó foreach qua danh sách đó

 System.Collections.Hashtable ht = new System.Collections.Hashtable(); 

     ht.Add("test1", "test2"); 
     ht.Add("test3", "test4"); 

     List<string> keys = new List<string>(); 
     foreach (System.Collections.DictionaryEntry de in ht) 
      keys.Add(de.Key.ToString()); 

     foreach(string key in keys) 
     { 
      ht[key] = DateTime.Now; 
      Console.WriteLine(ht[key]); 
     } 
+0

Điều này sẽ làm. Cảm ơn! –

1

Bạn không thể thay đổi tập hợp các mục được lưu trữ trong bộ sưu tập trong khi bạn đang liệt kê nó, vì điều đó khiến cuộc sống rất khó cho trình lặp trong hầu hết các trường hợp. Hãy xem xét trường hợp bộ sưu tập đại diện cho một cây cân bằng, và cũng có thể trải qua phép quay sau khi chèn. Liệt kê sẽ không có cách nào đáng tin cậy để theo dõi những gì nó đã thấy.

Tuy nhiên, nếu bạn đang cố gắng để cập nhật các giá trị sau đó bạn có thể viết:

deEntry.Value = sValue 

Cập nhật giá trị ở đây không ảnh hưởng đến các điều tra viên.

+1

Điều đó không biên dịch: Không thể sửa đổi thành viên của 'deEntry' vì nó là 'biến lặp foreach' –

+1

Điều này sẽ không hoạt động – swordfish

4

Trong khái niệm tôi sẽ làm:

Hashtable table = new Hashtable(); // ps, I would prefer the generic dictionary.. 
Hashtable updates = new Hashtable(); 

foreach (DictionaryEntry entry in table) 
{ 
    // logic if something needs to change or nog 
    if (needsUpdate) 
    { 
     updates.Add(key, newValue); 
    } 
} 

// now do the actual update 
foreach (DictionaryEntry upd in updates) 
{ 
    table[upd.Key] = upd.Value; 
} 
+0

Giải pháp tốt là tốt. Cảm ơn. –

-4

lẽ bạn có thể sử dụng bộ sưu tập Hashtable.Keys? Đếm thông qua đó có thể là có thể trong khi thay đổi Hashtable. Nhưng nó chỉ là một phỏng đoán ...

+0

Không, điều đó không hiệu quả. –

-1
private Hashtable htSettings_m = new Hashtable(); 

htSettings_m.Add("SizeWidth", "728");  
htSettings_m.Add("SizeHeight", "450");  
string sValue = "";  
foreach (string sKey in htSettings_m.Keys)  
{  
    // Get value from Registry and assign to sValue  
    // ...  
    // Change value in hashtable.  
    htSettings_m[sKey] = sValue;  
} 
+0

Sản xuất cùng một lỗi. –

+0

Đó là vấn đề khi trả lời từ bộ nhớ bị lỗi mà không cần kiểm tra trước. Tôi nghĩ về điều này một lần nữa và tôi nhớ rằng Hashtable sử dụng cùng một kiểu điều tra viên cho một Hashtable và cho các phím của nó. Một thực hiện thiếu sót nghiêm trọng theo ý kiến ​​của tôi. –

+0

Không, vấn đề không phải là loại điều tra viên - đó là thuộc tính Keys không lấy * bản sao * của tất cả các khóa, nó chỉ lặp lại trên bộ sưu tập cơ bản. Khi bạn * cần * để lấy một bản sao, hãy làm như vậy một cách rõ ràng. Đó là hành vi tôi muốn và mong đợi, cá nhân. –

0

Nó phụ thuộc vào lý do tại sao bạn đang lặp qua các mục trong hashtable. Nhưng có lẽ bạn sẽ có thể lặp lại các phím thay vào đó. Vì vậy,

foreach (String sKey in htSettings_m.Keys) 
{ // Get value from Registry and assign to sValue. 
    // ...  
    // Change value in hashtable. 
    htSettings_m[sKey] = sValue; 
} 

Tùy chọn khác là tạo HashTable mới. Lặp lại lần đầu tiên trong khi thêm các mục vào thứ hai rồi thay thế bản gốc bằng bản mới.
Việc lặp qua các phím yêu cầu phân bổ ít đối tượng hơn.

2

Cách đơn giản nhất là sao chép các khóa vào một bộ sưu tập riêng biệt, sau đó lặp lại thông qua đó.

Bạn đang sử dụng .NET 3.5? Nếu vậy, LINQ làm cho mọi thứ dễ dàng hơn một chút.

3

Nếu bạn đang sử dụng một từ điển thay vì một Hashtable, do đó loại các phím được biết, cách dễ nhất để tạo một bản sao của bộ sưu tập Keys để tránh ngoại lệ này là:

foreach (string key in new List<string>(dictionary.Keys)) 

Tại sao bạn nhận được một ngoại lệ cho bạn biết rằng bạn đã sửa đổi bộ sưu tập mà bạn đang lặp lại, khi thực tế bạn chưa có?

Trong nội bộ, lớp Hashtable có trường phiên bản. Các phương thức Add, Insert và Remove tăng phiên bản này. Khi bạn tạo một điều tra viên trên bất kỳ bộ sưu tập nào mà Hashtable hiển thị, đối tượng liệt kê bao gồm phiên bản hiện tại của Hashtable. Phương thức MoveNext của liệt kê kiểm tra phiên bản của người đếm ngược với Hashtable, và nếu chúng không bằng nhau, nó sẽ ném ra InvalidOperationException mà bạn đang thấy.

Đây là một cơ chế rất đơn giản để xác định có hay không Hashtable đã được sửa đổi. Trong thực tế nó là một chút quá đơn giản. Bộ sưu tập Keys thực sự phải duy trì phiên bản riêng của nó, và phương pháp GetEnumerator của nó nên lưu phiên bản của bộ sưu tập trong điều tra viên, chứ không phải phiên bản của Hashtable.

Có một lỗi thiết kế phụ khác trong phương pháp này. Phiên bản là Int32. Phương thức UpdateVersion không kiểm tra giới hạn. Do đó có thể, nếu bạn thực hiện đúng số sửa đổi cho Hashtable (2 lần Int32.MaxValue, cho hay lấy), đối với phiên bản trên Hashtable và điều tra là giống nhau mặc dù bạn đã thay đổi triệt để Hashtable từ khi tạo điều tra viên. Vì vậy, các phương pháp MoveNext sẽ không ném ngoại lệ mặc dù nó cần, và bạn sẽ nhận được kết quả bất ngờ.

2

Phần quan trọng là ToArray() phương pháp

var dictionary = new Dictionary<string, string>(); 
foreach(var key in dictionary.Keys.ToArray()) 
{ 
    dictionary[key] = "new value"; 
} 
+0

giải pháp đơn giản hơn nhiều so với giải pháp hàng đầu hiện tại. – Lars

0

Đây là cách tôi đã làm nó trong một cuốn từ điển; reset mỗi giá trị trong dict false:

Dictionary<string,bool> dict = new Dictionary<string,bool>(); 

for (int i = 0; i < dict.Count; i++) 
{ 
    string key = dict.ElementAt(i).Key; 
    dict[key] = false; 
} 
0
List<string> keyList = htSettings_m.Keys.Cast<string>().ToList(); 
foreach (string key in keyList) { 

Nó cũng giống như câu trả lời khác, nhưng tôi thích một dòng để có được các phím.

+1

Điều này sẽ tốt hơn khi nhận xét về câu trả lời được chấp nhận. –

0

Chuyển đổi nó vào một mảng:

private Hashtable htSettings_m = new Hashtable(); 
htSettings_m.Add("SizeWidth", "728"); 
htSettings_m.Add("SizeHeight", "450"); 
string sKey = ""; 
string sValue = ""; 

ArrayList htSettings_ary = new ArrayList(htSettings_m.Keys) 
foreach (DictionaryEntry deEntry in htSettings_ary) 
{ 
    // Get value from Registry and assign to sValue. 
    // ... 
    // Change value in hashtable. 
    sKey = deEntry.Key.ToString(); 
    htSettings_m[sKey] = sValue; 
} 
Các vấn đề liên quan