2012-01-27 79 views
0

我希望能夠在相同情況下通過鍵查看值,並在其他情況下遍歷集合。IDictionary是IEnumerable <ValueType>而不是KeyValue?

以前我是這樣做的:

public class Company 
{ 
    public string Name { get; set; } 
    public ICollection<Department> Departments { get; set; } 

} 

public class Department 
{ 
    public string Name  { get; set; } //display name 
    public string UniqueName { get; set; } //like a code value 
    public bool IsSelected { get; set; } 
} 

foreach(var d in someCompany.Departments) ... 

var departments = someCompany.Departments.Select(d => some projection stuff ... 

現在我有幾個地方我還需要通過一些其他集合像複選框來迭代匹配的UniqueName字符串,所以它是有效的,我改變了列出將部門聲明爲IDictionary,但這會使其他用例更加繁瑣,總是要求我鑽取一個Value或Values。因此,現在Departments屬性不再是Department類的集合,而是一組KeyValue對。

foreach(ListItem item in someCheckBoxes.Items) 
{ 
    someCompany.Departments[item.Value].Selected = true; 
} 

foreach(var d in someCompany.Departments.Values) ... 

var departments = someCompany.Departments.Values.Select(d => some projection stuff ... 

我也不在乎必須將列表轉換爲IDictionary,或者在我初始化這些列表時添加一個KeyValue。

理想情況下,我將擁有一個集合,其行爲像一個ICollection,但也有一個索引操作符幷包含一個內部訪問字典的函數。

OR我有兩個屬性,像這樣,那留在同步:

public Company(string uniqueName, string name, ICollection<Department> departments) 
{ 
    Name = name; 
    UniqueName = uniqueName; 
    DepartmentsByUniqueName = departments.ToDictionary<Department, string>(p => p.UniqueName); 
} 

public IDictionary<string,Department> DepartmentsByUniqueName { get; set;} 

public ICollection<Department> Departments { get { return DepartmentsByUniqueName.Values; } } 

public void AddDepartment(Department department) 
{ 
    DepartmentsByUniqueName.Add(department,department.UniqueName) 
} 

這裏的問題是,有人可以通過部門屬性獲取值集合,並添加/刪除項目到它,而不是reallizing他們確實需要被添加到字典中(實現集並不能解決這個問題,因爲他們通過獲取來得到回調,然後可以添加項)。

嘗試不重新創建太多的代碼,而是繼承從Dictionary自動使它繼承IEnumerable。即使我做了明確的接口實現,如果用戶將其轉換爲基本接口,也會出現問題。

所以基本上我想要一個實現IEnumerable的類,但也有一個Contains和[]運算符,它們在內部利用字典來提高效率。

或者能夠創建與Department部門集合保持同步的額外DepartmentByUniqueName字典屬性。

+1

爲什麼不直接使用字典的'Values'屬性時需要迭代? – 2012-01-27 22:28:37

+0

因爲該屬性稱爲Departments,而不是DepartmentKeyValuePairs。在每個用例中總是訪問.Value或.Values時,它的語義意義不大,因爲實際上字典只是一個索引。總是將密鑰傳遞給.Add。也是不必要的。相比之下,KeyedCollection非常棒,因爲您將該邏輯烘焙到集合中,每次添加項目時都會使用該屬性作爲Key。而不是ICollection 它是一個ICollection 。 – AaronLS 2012-02-02 22:50:09

回答

3

你可能想要做的是細分KeyedCollection,從而定義它:

public class DepartmentCollection : KeyedCollection<String, Department> { 
    protected override String GetKeyForItem(Department item) 
    { 
     // EDIT: For your use case, this should work 
     return item.UniqueName; 
    } 
} 

而且用它在你的公司類的部門屬性:

public class Company 
{ 
    public string Name { get; set; } 
    public DepartmentCollection Departments { get; set; } 
} 

的KeyedCollection然後可以按名稱或索引使用:

var departments = new DepartmentCollection(); 
departments.Add(new Department(...)); 
var accounting = departments["Accounting"]; 
foreach (var department in departments) { .... } 
var accountingExists = departments.Contains("accounting"); 
// etc 
+0

對於'KeyedCollection'建議+1。我比較喜歡這個答案,因爲它允許O(1)索引查找(與'Enumerable.ElementAt'相反,它是非列表結構的O(n))。 – Douglas 2012-01-28 13:23:03

+0

我通常必須從某個數據庫初始化這些數據,通常涉及到ToList或ToDictionary。將IEnumerable或ICollection轉換爲KeyedCollection的最佳方法是什麼? – AaronLS 2012-01-31 15:32:42

+0

您需要創建'DepartmentCollection',然後通過IEnumerable或ICollection進行foreach並逐個添加每個項目。 KeyedCollection沒有AddAll方法。或者,您可以將構造函數重載添加到需要'IEnumerable '的'DepartmentCollection'並執行迭代。 – 2012-01-31 17:49:40

1

這就是你想要的(我想!)你也可以實現ICollection<T>並將調用委託給內部的_storage字典。

public class HybridLookup<TKey, TValue> : IEnumerable<TValue> 
{ 
    private readonly IDictionary<TKey, TValue> _storage; 

    public HybridLookup() 
    { 
     _storage = new Dictionary<TKey, TValue>(); 
    } 

    public TValue this[TKey key] 
    { 
     get { return _storage[key]; } 
    } 

    public Boolean Contains(TKey key) 
    { 
     return _storage.ContainsKey(key); 
    } 
    public IEnumerator<TValue> GetEnumerator() 
    { 
     return _storage.Values.GetEnumerator(); 
    } 

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

} 
1

我想你的代碼的最後段提出的解決方案工作正常,除了AddDepartment方法,它需要一個小的修正:

public void AddDepartment(Department department) 
{ 
    DepartmentsByUniqueName.Add(department.UniqueName, department); 
} 

Contains已經存在於Dictionary<TKey, TValue>.ValueCollection,你可以使用LINQ ElementAt方法而不是索引器(雖然它承認不會有效)。

您不必擔心消費者更改您的Dictionary<TKey,TValue>.Values集合,因爲任何此類嘗試都會引發NotSupportedException:「不允許從字典中派生值集合。

下面是相關的代碼從類的摘錄:

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback 
{ 
    public Dictionary<TKey, TValue>.ValueCollection Values { get; } 

    public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable 
    { 
     void ICollection<TValue>.Add(TValue item) 
     { 
      ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); 
     } 
    } 
} 
相關問題