无法从Iesi.Collections.Generic ISet <t> </t>中删除项目

时间:2014-04-04 09:09:37

标签: c# wpf hashcode iesi-collections

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

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

其中ItemBindingDomain.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中移除物品的方式中遗漏了什么......?

感谢您提出建议和解决方案。

3 个答案:

答案 0 :(得分:2)

这是一个非常简单的问题。只需下载dotPeek 1.2并启动符号服务器&amp;然后你可以检查ISet.Remove()的ACTUAL实现,看看为什么它是挑剔的。正如@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()来实际测试它是否应该删除元素。 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():

Why is it important to override GetHashCode when Equals method is overridden?

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

答案 1 :(得分: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相等测试失败?)。

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

答案 2 :(得分:0)

您可以尝试使用...

private void OnRemove(ItemBinding itemToRemove)
{
    ItemBindings.RemoveWhere(x => x.Id == itemToRemove.Id);
}