我希望能够在相同的情况下通过键查找值,并在其他情况下遍历集合。
以前我这样做:
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匹配字符串的复选框列表,所以为了提高效率,我将Departments的声明更改为IDictionary,但是这使得其他用例更麻烦,总是要求我深入研究价值或价值观。所以现在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 ...
每当我初始化这些列表时,我也不必关心将Lists转换为IDictionary或添加KeyValue。
理想情况下,我会有一个集合,其行为类似于ICollection,但也有一个索引运算符,并包含一个内部访问字典的函数。
或者我会有两个这样的属性,保持同步:
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)
}
这里的问题是有人可以通过Departments属性获取Values集合并向其添加/删除项目,而不是重新调整它们确实需要添加到字典中。(实现集合并不能解决这个问题,因为它们得到了通过get进行cllection,然后可以添加项目。)
尝试不重新编写太多代码,但继承自Dictionary会自动使其继承IEnumerable。即使我对接口进行了显式实现,如果用户将其强制转换为该基接口,也可能会出现问题。
基本上我想要一个实现IEnumerable的类,但也有一个Contains和[]运算符,它们在内部使用字典来提高效率。
或者能够创建另外的DepartmentByUniqueName字典属性,该属性与Departments集合保持同步。
答案 0 :(得分: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;
}
}
并在公司类中将其用作Departments属性:
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
答案 1 :(得分: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();
}
}
答案 2 :(得分: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);
}
}
}