我认为这是我们遇到的常见问题。

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基于namePerson,但是在我的情况下,我正在创建一个自定义的collection,每个Person仅包含一个place。换句话说,placecollection中将是唯一的。

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,但实际上Jeancollection中尚不存在。所以我的问题是,仅当KeyedCollection<K, T>仅基于EqualsK部分时,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;
}

09-11 18:42