我经常需要一组带有数字标识符的非顺序对象。我喜欢使用KeyedCollection,但我认为这有一个严重的缺点。如果对密钥使用int,则不能再通过索引访问集合的成员(集合[index]现在真的是集合[key])。这是一个严重的问题,以避免使用int作为键?什么是更好的选择? (也许是int.ToString()?)
我之前没有遇到任何重大问题,但是最近我遇到了一个令人讨厌的障碍,如果密钥是int,那么针对KeyedCollection的XML序列化不工作,由于{{3} }。
答案 0 :(得分:7)
基本上你需要决定这个类的用户是否可能因为他们不能这样做而感到困惑:
for(int i=0; i=< myCollection.Count; i++)
{
... myCollection[i] ...
}
虽然他们当然可以使用foreach,或者使用演员:
for(int i=0; i=< myCollection.Count; i++)
{
... ((Collection<MyType>)myCollection)[i] ...
}
这不是一个简单的决定,因为它很容易导致heisenbugs。我决定在我的一个应用程序中允许它,其中该类用户的访问几乎完全是按键。
我不确定我是否会为共享类库执行此操作:通常我会避免在公共API中公开KeyedCollection:相反,我会公开IList&lt; T&gt;在公共API中,需要键控访问的API的消费者可以使用构造函数定义他们自己的内部KeyedCollection,该构造函数采用IEnumerable&lt; TItem&gt;并用它填充集合。这意味着您可以从API中检索的列表中轻松构建新的KeyedCollection。
关于序列化,还存在性能问题that I reported to Microsoft Connect:KeyedCollection维护内部字典和列表,并对两者进行序列化 - 序列化列表就足够了,因为在反序列化时可以轻松地重新创建字典
由于这个原因以及XmlSerialization错误,我建议你避免序列化KeyedCollection - 而只是序列化KeyedCollection.Items列表。
我不喜欢the suggestion of wrapping your int key in another type。简单地添加复杂性似乎是错误的,因此类型可以用作KeyedCollection中的项目。我使用字符串键(ToString)而不是这样做 - 这就像VB6 Collection类。
FWIW,我前段时间在MSDN论坛上问the same question。有一个FxCop团队成员的回复,但没有确凿的指导。
答案 1 :(得分:3)
一个简单的解决方案可能是将int
包装到另一个类型中,以便为重载解析创建一个不同的类型。如果使用struct
,则此包装器不会产生任何额外开销:
struct Id {
public int Value;
public Id(int value) { Value = value; }
override int GetHashCode() { return Value.GetHashCode(); }
// … Equals method.
}
答案 2 :(得分:2)
最好将 GetById(int)
方法添加到集合类型中。如果您不需要任何其他密钥来访问包含的对象,则可以使用Collection<T>
:
public class FooCollection : Collection<Foo>
{ Dictionary<int,Foo> dict = new Dictionary<int,Foo>();
public Foo GetById(int id) { return dict[id]; }
public bool Contains(int id) { return dict.Containskey(id);}
protected override void InsertItem(Foo f)
{ dict[f.Id] = f;
base.InsertItem(f);
}
protected override void ClearItems()
{ dict.Clear();
base.ClearItems();
}
protected override void RemoveItem(int index)
{ dict.Remove(base.Items[index].Id);
base.RemoveItem(index);
}
protected override void SetItem(int index, Foo item)
{ dict.Remove(base.Items[index].Id);
dict[item.Id] = item;
base.SetItem(index, item);
}
}
}
答案 3 :(得分:1)
KeyedCollection中的键应该是唯一的,并且可以从正在收集的对象中快速导出。例如,给定一个person类,它可以是SSN属性,或者甚至可以连接FirstName和LastName属性(如果结果已知是唯一的)。如果ID是合法地收集的对象的字段,则它是该密钥的有效候选者。但也许尝试将其作为字符串转换,以避免碰撞。