2

我有一个类型的集合:
Iesi.Collections.Generic

public ISet<ItemBinding> ItemBindings { get; set; }

ItemBinding在哪里Domain.Model

我以这种方式初始化集合:

ItemBindings = new HashedSet<ItemBinding>();

我用成员填充集合。

当我想从这个集合中删除一个项目时,我无法删除它。

private void OnRemove(ItemBinding itemToRemove) {
    ItemBindings.Remove(itemToRemove);
}

甚至与集合中的项目itemToRemove相同。hashCode

我还尝试在集合中找到该项目,将其保存在变量中,然后将其删除:

private void OnRemove(ItemBinding itemToRemove) {
    var foundItem = ItemBindings.Single( x => x.Id == itemToRemove.Id); // always if found
    ItemBindings.Remove(foundItem);
 }

但这不起作用。

一个可行的解决方法是:

private void OnRemove(ItemBinding itemToRemove) {
    var backUpItems = new List<ItemBinding>(ItemBindings);
    backUpItems.Remove(itemToRemove);

    ItemBindings.Clear();
    ItemBindings.AddAll(backUpItems);
 }

但这是一个肮脏的解决方法。我正在尝试以优雅的方式执行此简单的删除:)。

更改类型

如果我从 IList 中的 ISet 更改类型,它可以正常工作。

public IList<ItemBinding> ItemBindings { get; set; }
ItemBindings = new List<ItemBinding>();

当我想从这个集合中删除一个项目时,它被删除了。

private void OnRemove(ItemBinding itemToRemove) {
    ItemBindings.Remove(itemToRemove);
}

我无法从 ISet 中删除项目的方式中缺少什么...?

感谢您的建议,解决方案。

4

3 回答 3

2

这是一个非常简单的问题。只需下载 dotPeek 1.2 并启动符号服务器,然后您就可以检查 ISet.Remove() 的实际实现,看看为什么它很挑剔。正如@HansPassant 所说,可能是GetHashCode() 的情况,或者是HashedSet 的实际实现

至于我的猜测;看看DictionarySet(基类HashedSet):

https://www.symbolsource.org/Public/Metadata/Default/Project/NHibernate/3.0.0.Alpha2/Release/All/Iesi.Collections/Iesi.Collections/Generic/DictionarySet.cs

如您所见,Remove() 使用 Contains() 来实际测试它是否应该删除元素。包含()做什么?基本上它是 Dictionary.Contains() 的包装

http://msdn.microsoft.com/en-us/library/ms182358(v=vs.80).aspx

GetHashCode 基于适合散列算法和数据结构(如散列表)的当前实例返回一个值。相同类型且相等的两个对象必须返回相同的哈希码,以确保 System.Collections.HashTable 和 System.Collections.Generic.Dictionary 的实例正常工作。

请注意,重要的是:

1)你的 GetHashCode() 不能改变。这意味着 GetHashCode() 使用的所有字段都不能更改。将元素插入 Dictionary 后,将调用其 GetHashCode() 并将其放入特定的存储桶中。当您以后有不同的GetHashCode()时,您将无法恢复它。GetHashCode() 通过后,将调用您的 Equals 方法。确保您的字段是不可变的。

字典的相关资料:

private int FindEntry(TKey key)
{
  if ((object) key == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
  if (this.buckets != null)
  {
    int num = this.comparer.GetHashCode(key) & int.MaxValue;
    for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
    {
      if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
        return index;
    }
  }
  return -1;
}

请参阅此线程如何覆盖 Equals() 和 GetHashCode():

为什么在重写 Equals 方法时重写 GetHashCode 很重要?

请注意@Albic 在该线程中的答案。

于 2014-07-30T07:58:53.930 回答
0

我无法重现这种行为。

 private class ItemBinding
    {
        public string ID { get; set; }
    }

    [TestMethod]
    public void TestMethod1()
    {
        System.Collections.Generic.HashSet<ItemBinding> set = new System.Collections.Generic.HashSet<ItemBinding>();
        ItemBinding item1 = new ItemBinding() { ID = "Jaffa" };
        set.Add(item1);
        Assert.IsTrue(set.Count == 1);
        set.Remove(item1);
        Assert.IsTrue(set.Count == 0);

        ItemBinding item2 = new ItemBinding() { ID = "Moon" };
        set.Add(item2);
        ItemBinding item3 = new ItemBinding() { ID = "Moon" };

        Assert.IsTrue(item2.GetHashCode() != item3.GetHashCode());
        Assert.IsTrue(set.Remove(item3) == false);
        Assert.IsTrue(set.Count == 1);

    }

上面的测试显示 Hashset 按预期工作。您是否可能陷入第二个测试中显示的陷阱,即比较具有相同值但实际上是不同类实例的类的两个实例(因此 GetHashCode 相等测试失败?)。

如果您可以更改此处发布的代码以更准确地表示您的特定问题,那将很有帮助。

于 2014-07-28T14:22:55.080 回答
0

您可以尝试使用...

private void OnRemove(ItemBinding itemToRemove)
{
    ItemBindings.RemoveWhere(x => x.Id == itemToRemove.Id);
}
于 2014-07-28T14:39:50.907 回答