我认为这是我们遇到的常见问题。
class Person
{
public string place;
public string name;
public Person(string place, string name)
{
this.place = place;
this.name = name;
}
public bool Equals(Person other)
{
if (ReferenceEquals(null, other))
return false;
return name == other.name;
}
public override bool Equals(object obj)
{
return Equals(obj as Person);
}
public override int GetHashCode()
{
return name.GetHashCode();
}
public override string ToString()
{
return place + " - " + name;
}
}
说我上这堂课。我可以这样实现
KeyedCollection
:class Collection : KeyedCollection<string, Person>
{
protected override string GetKeyForItem(Person item)
{
return item.place;
}
}
这里的情况是默认的
Equals
基于name
的Person
,但是在我的情况下,我正在创建一个自定义的collection
,每个Person
仅包含一个place
。换句话说,place
在collection
中将是唯一的。Person p1 = new Person("Paris", "Paul");
Person p2 = new Person("Dubai", "Ali");
var collection = new Collection { p1, p2 };
var p3 = new Person("Paris", "Jean");
if (!collection.Contains(p3))
collection.Add(p3); // explosion
我了解这个问题。
Contains(Person)
重载是Collection<T>.Contains(T)
的重载,它执行基于值的线性搜索,而Add(Person)
确实将值添加到内部字典中,这可能导致重复键异常。在这里,如果平等基于place
,那么这个问题就不会存在。我可以提供一种解决方法:
class Collection : KeyedCollection<string, Person>
{
protected override string GetKeyForItem(Person item)
{
return item.place;
}
new public bool Contains(Person item)
{
return this.Contains(GetKeyForItem(item));
}
}
但这又意味着如果我做一般
var p3 = new Person("Paris", "Jean");
bool b = collection.Contains(p3); //true
返回
true
,但实际上Jean
在collection
中尚不存在。所以我的问题是,仅当KeyedCollection<K, T>
仅基于Equals
的K
部分时,T
才有意义吗?我的问题很少涉及语义方面。我不是在寻求解决方案,而是想知道KeyedCollection
何时有意义?我从文档中找不到与此主题相关的任何内容。更新:
我发现这里提到的确切问题http://bytes.com/topic/net/answers/633980-framework-bug-keyedcollection-t
询问者向MS提交了错误报告。引用他的意见(日期为07年4月18日):
我将此错误提交给Microsoft,他们已经对其进行了验证,
接受了。它的问题ID 271542,可以通过here进行跟踪:
“我们已经在WinXP pro SP2和VSTS2005 SP1上重现了该错误,并且
我们将此错误发送到Visual中的适当组
Studio产品团队进行分类和解决。”
虽然我不认为这是一个错误,但这肯定是一件烦人的事。但是只是想知道MS最初是如何将其作为错误接受的(预计现在无法找到该页面)。 Imo,它只是考虑周全的继承模型。
最佳答案
询问收藏似乎有两件事:
是否包含住在巴黎的名叫吉恩的人,以及
是否包含居住在巴黎的人。
KeyedCollection<TKey, TItem> Class提供了恰好两种方法来问这些问题:
Contains(TItem) Method,以及
Contains(TKey) Method。
住在巴黎的名叫Jean的人与住在巴黎的名叫Paul的人(Person.Equals)不同。但是,如果有一个人住在巴黎,那么根据您的规定,就不可能有另一个人住在巴黎。
因此,基本上,您必须在添加新人员之前向集合询问正确的问题:
if (!collection.Contains(p3.place))
collection.Add(p3);
为了方便起见,您可以在类中添加一个TryAdd方法,如果成功添加了此人,或者如果集合中已经有一个人住在同一个地方,则返回该方法:
public bool TryAdd(Person person)
{
if (Contains(GetKeyForItem(person)))
return false;
Add(person);
return true;
}