2012-01-27 47 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); 
     } 
    } 
} 
相关问题