2012-02-24 34 views
11

Tôi đã tự hỏi nếu có một kiểu được xây dựng trong C# giống như 'Từ điển' nhưng ở đó cả TKey và TValue phải là duy nhất.Loại từ điển C# với các khóa và giá trị duy nhất

Ví dụ ::

d.Add(1, "1"); 
d.Add(2, "1"); // This would not be OK because "1" has already been used as a value. 

Tôi biết đây là loại kỳ lạ, nhưng có vẻ như rằng kể từ khi có khoảng một tỷ loại bộ sưu tập trong BCL nó có thể tồn tại. Bất kỳ ý tưởng?

+3

Tạo phần giá trị của khóa. –

+3

Không có lớp nào trong .NET Framework. Nhưng bạn có thể dễ dàng xây dựng một trong một từ điển và một HashSet hoặc hai từ điển. – dtb

+1

@Robert Harvey: nếu anh ta làm điều đó anh ta không thể làm 'd [1]' nữa, điều đó đánh bại mục đích của từ điển. Cũng có thể sử dụng HashSet <> –

Trả lời

13

Làm thế nào để có từ điển và HashSet/từ điển ngược thứ cấp - nó sẽ giải quyết vấn đề và sẽ thực hiện tốt hơn so với kiểm tra trên một từ điển duy nhất.

Something như thế này, bao bọc như lớp:

HashSet<string> secondary = new HashSet<string>(/*StringComparer.InvariantCultureIgnoreCase*/); 
Dictionary<int, string>dictionary = new Dictionary<int, string>(); 
object syncer = new object(); 

public override void Add(int key, string value) 
{ 
    lock(syncer) 
    { 
    if(dictionary.ContainsKey(key)) 
    { 
     throw new Exception("Key already exists"); 
    } 

    if(secondary.Add(value) 
    { 
     throw new Exception("Value already exists"); 
    } 
    dictionary.Add(key, value); 
    } 
} 
+1

Và sau đó bọc nó trong một lớp tùy chỉnh – Jason

+0

@ Jason Nếu bạn cần thêm hơn 1 thời gian - chắc chắn có –

+1

@OlegDok Nếu bạn phải đọc mã nhiều hơn 1 lần - chắc chắn có! (Và bạn nên luôn luôn phải đọc lại mã này một lần nữa.) –

0

Có một dự án nằm here rằng có một kiểu như thế này. Nó được gọi là PairDictionary và nó hoạt động khá tốt. Không phải là câu trả lời hay nhất, nhưng đối với bất cứ ai cần lớp tùy chỉnh đó.

+0

The 'PairDictionary Bên trong nó hoạt động với các danh sách và mỗi hoạt động là một hoạt động O (n) không giống như O (1) của từ điển _real_ --- [source] (http://curations.codeplex.com/SourceControl/ mới nhất # Curations/PairDictionary.cs) – t3chb0t

+0

@ t3chb0t Bạn đang tập trung vào việc thực hiện chứ không phải API. Yeah, nó không phải là tốt nhất, nhưng việc sử dụng nó là điều quan trọng. ince là một phần của một số mã nguồn mở, bạn nên gửi một bản vá để cải thiện chức năng :) –

0

Tôi đã giải quyết vấn đề này bằng cách lưu trữ dữ liệu dưới dạng Dictionary<TKey, HashSet<TValue>>. Bạn có thể thay thế HashSet bằng một từ điển khác nếu bạn muốn có một giá trị có 2 khóa chính.

Dictionary<int, HashSet<int>> _myUniquePairOfIntegerKeys; 
// OR 
Dictionary<string, Dictionary<string, bool>> _myUniquePairOfStringKeysWithABooleanValue; 
1

Đối với học sinh nội bộ tôi đã viết BiDictionary. Nó không phải là bằng chứng chống đạn bởi tôi không phơi bày nó cho người dùng để nó hoạt động tốt cho tôi. Nó cho phép tôi lấy chìa khóa khi tôi cần.

KeyPair<,> là cần thiết để có thể triển khai IEnumerable<,> và do đó phương pháp Add để chúng tôi có thể sử dụng trình khởi tạo đối tượng.

internal class KeyPair<TKey1, TKey2> 
{ 
    public TKey1 Key1 { get; set; } 
    public TKey2 Key2 { get; set; } 
} 

Đây là lớp học chính là một đối tượng năng động để chúng tôi có thể sử dụng tên chủ chốt trên đó khi lấy giá trị:

internal class BiDictionary<TKey1, TKey2> : DynamicObject, IEnumerable<KeyPair<TKey1, TKey2>> 
{ 
    private readonly Dictionary<TKey1, TKey2> _K1K2 = new Dictionary<TKey1, TKey2>(); 
    private readonly Dictionary<TKey2, TKey1> _K2K1 = new Dictionary<TKey2, TKey1>(); 

    private readonly string _key1Name; 
    private readonly string _key2Name; 

    public BiDictionary(string key1Name, string key2Name) 
    { 
     _key1Name = key1Name; 
     _key2Name = key2Name; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     if (binder.Name == _key1Name) 
     { 
      result = _K1K2; 
      return true; 
     } 

     if (binder.Name == _key2Name) 
     { 
      result = _K2K1; 
      return true; 
     } 

     result = null; 
     return false; 
    } 

    public void Add(TKey1 key1, TKey2 key2) 
    { 
     _K1K2.Add(key1, key2); 
     _K2K1.Add(key2, key1); 
    } 

    public IEnumerator<KeyPair<TKey1, TKey2>> GetEnumerator() 
    { 
     return _K1K2.Zip(_K2K1, (d1, d2) => new KeyPair<TKey1, TKey2> 
     { 
      Key1 = d1.Key, 
      Key2 = d2.Key 
     }).GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

Ví dụ:

dynamic bidic = new BiDictionary<string, string>("Key1", "Key2") 
{ 
    { "foo", "bar" }, 
    { "baz", "qux" } 
}; 
var bar = bidic.Key1["foo"]; 
var foo = bidic.Key2["bar"]; 

Họ có thể không đồng bộ nếu bạn sửa đổi bất kỳ từ điển nào bên ngoài. Với mục đích này, tôi sử dụng ObservableDictionary để tôi có thể cập nhật một cái khác nếu một thay đổi nhưng vì mục đích đơn giản, tôi đã xóa phần này của mã để chỉ phân tách logic chính.

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