我有一个类型的集合:
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中移除物品的方式中遗漏了什么......?
感谢您提出建议和解决方案。
答案 0 :(得分:2)
这是一个非常简单的问题。只需下载dotPeek 1.2并启动符号服务器&amp;然后你可以检查ISet.Remove()的ACTUAL实现,看看为什么它是挑剔的。正如@HansPassant所说,可能是GetHashCode()的情况,或HashedSet的实际实现
至于我的推测;看一下DictionarySet
(基类HashedSet
):
如您所见,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);
}