2013-04-24 30 views
25

Tôi thường thấy mình tạo ra một Dictionary với một lớp giá trị không tầm thường (ví dụ: List), và sau đó luôn viết cùng một mẫu mã khi điền dữ liệu..NET Dictionary: nhận hoặc tạo mới

Ví dụ:

var dict = new Dictionary<string, List<string>>(); 
string key = "foo"; 
string aValueForKey = "bar"; 

Đó là, tôi muốn chèn "bar" vào danh sách tương ứng với chìa khóa "foo", nơi chính "foo" có thể không được ánh xạ tới bất cứ điều gì.

Đây là nơi mà tôi sử dụng mô hình không ngừng lặp đi lặp lại:

List<string> keyValues; 
if (!dict.TryGetValue(key, out keyValues)) 
    dict.Add(key, keyValues = new List<string>()); 
keyValues.Add(aValueForKey); 

Có một cách thanh lịch hơn để làm điều này?

câu hỏi

liên quan mà không có câu trả lời cho câu hỏi này:

+0

Điều gì sẽ xảy ra nếu khóa tồn tại nhưng Danh sách là rỗng? –

+3

@Barabba Nói chung tôi nghĩ rằng việc thêm một giá trị null sẽ được coi là không phù hợp và bạn mong đợi * mã để chỉ ra bom và sửa lỗi thêm khóa không, không phải bằng cách cố gắng xử lý nó ở đây. – Servy

+1

@Servy ok, nhưng nếu chúng ta có được một danh sách kết quả từ một nguồn của bên thứ ba thì sao? Điều duy nhất để đối phó với nó là xử lý các giá trị null, tôi có sai không? Nó xảy ra với tôi mỗi ngày :) –

Trả lời

30

Chúng tôi có một hơi khác nhau về vấn đề này, nhưng hiệu quả là tương tự:

public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) 
    where TValue : new() 
{ 
    TValue val; 

    if (!dict.TryGetValue(key, out val)) 
    { 
     val = new TValue(); 
     dict.Add(key, val); 
    } 

    return val; 
} 

gọi:

var dictionary = new Dictionary<string, List<int>>(); 

List<int> numbers = dictionary.GetOrCreate("key"); 

Nó làm cho việc sử dụng hạn chế chung cho nhà xây dựng parameterless công cộng: where TValue : new().

Để giúp phát hiện, trừ khi phương pháp mở rộng là khá cụ thể cho một vấn đề hẹp, chúng ta có xu hướng đặt phương pháp khuyến nông trong không gian tên của các loại họ đang mở rộng, trong trường hợp này:

namespace System.Collections.Generic 

Hầu hết các thời gian, người sử dụng loại có tuyên bố using được xác định ở trên cùng, vì vậy IntelliSense cũng sẽ tìm thấy các phương pháp mở rộng cho nó được xác định trong mã của bạn.

+1

@ Darthenius Không, cách tiếp cận đầu tiên là cụ thể cho các danh sách (chúng tôi có những người khác cho ' HashSet' và các mục 'Dictionary <>' khác. Tôi không thể nhớ chính xác lý do nhưng tôi nghĩ bởi vì 'Danh sách <>' cũng chứa một loại chung, làm theo cách này chơi đẹp hơn với suy luận kiểu. –

+2

@ Darthenius Vâng, tôi vừa quay lại mã của chúng tôi và nhận xét phiên bản 'Danh sách <>' mà chúng tôi đã có và nó không còn sử dụng nữa, nó hoạt động giống như tiêu chuẩn ở trên. Vì vậy, tôi đã gỡ bỏ nó. Nó phải được bỏ lại sau một cách tình cờ vì tôi đã mã hóa nó lần đầu tiên. –

+1

@ Darthenius Vâng, chúng không chính xác về mặt chức năng, vì vậy tôi chưa loại bỏ chúng. Tuy nhiên, về mặt câu hỏi này, nó không liên quan. Họ khác nhau ở chỗ họ đã kiểm tra một mục là null, không phải là một mục không có trong từ điển, do đó, một khóa với một mục null cũng sẽ tạo ra một mục. –

3

Như với rất nhiều vấn đề lập trình, khi bạn thấy mình đang làm một cái gì đó rất nhiều, cấu trúc lại nó thành một phương pháp:

public static void MyAdd<TKey, TCollection, TValue>(
    this Dictionary<TKey, TCollection> dictionary, TKey key, TValue value) 
    where TCollection : ICollection<TValue>, new() 
{ 
    TCollection collection; 
    if (!dictionary.TryGetValue(key, out collection)) 
    { 
     collection = new TCollection(); 
     dictionary.Add(key, collection); 
    } 
    collection.Add(value); 
} 
+0

@ Darthenius Nó chỉ là một cách khác. Nó thu hẹp phạm vi của phương thức xuống để thêm các mục vào bất kỳ bộ sưu tập nào đang thực thi 'ICollection'. Điểm chính là để chứa mã ở một vị trí riêng biệt để tái sử dụng. Tôi không nghĩ anh ta đang cố gắng cải thiện mã nguồn đã được cung cấp. –

+2

@ Darthenius Bạn cũng có thể trả lại bộ sưu tập nếu muốn, giả sử đó là một điều đặc biệt phổ biến. Theo kinh nghiệm của tôi khi sử dụng mẫu 'Từ điển ' Tôi không thường xuyên thêm nhiều giá trị cho cùng một khóa cùng một lúc. Nếu đó là điều xảy ra thường xuyên, bạn có thể thêm một tình trạng quá tải khác trong đó bạn chấp nhận một 'IEnumerable 'và chỉ cần thêm tất cả chúng vào trong phương thức. – Servy

0

Ok, cách tiếp cận khác nhau:

public static bool TryAddValue<TKey,TValue>(this System.Collections.Generic.IDictionary<TKey,List<TValue>> dictionary, TKey key, TValue value) 
    { 
     // Null check (useful or not, depending on your null checking approach) 
     if (value == null) 
      return false; 

     List<TValue> tempValue = default(List<TValue>); 

     try 
     { 
      if (!dictionary.TryGetValue(key, out tempValue)) 
      { 
       dictionary.Add(key, tempValue = new List<TValue>()); 
      } 
      else 
      { 
       // Double null check (useful or not, depending on your null checking approach) 
       if (tempValue == null) 
       { 
        dictionary[key] = (tempValue = new List<TValue>()); 
       } 
      } 

      tempValue.Add(value); 
      return true; 
     } 
     catch 
     { 
      return false; 
     } 
    } 

Bằng cách này bạn có để "cố gắng thêm" giá trị của bạn vào Danh sách chung chung (rõ ràng là có thể khái quát chung cho một bộ sưu tập chung), hãy kiểm tra và cố gắng lấy khóa/giá trị hiện có trong Từ điển của bạn. Cách sử dụng và ví dụ:

var x = new Dictionary<string,List<string>>(); 
x.TryAddValue("test", null); // return false due to null value. Doesn't add the key 
x.TryAddValue("test", "ok"); // it works adding the key/value 
x.TryAddValue("test", "ok again"); // it works adding the value to the existing list 

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

0

Và còn điều này thì sao?

var keyValues = dictionary[key] = dictionary.ContainsKey(key) ? dictionary[key] : new List<string>(); 
keyValues.Add(aValueForKey); 
Các vấn đề liên quan