背景:
我已经使用 MVVM 编写了一个大型 WPF 应用程序,但它一直遇到一些间歇性问题。我最初询问“已添加具有相同键的项目”从代码问题中选择 ListBoxItem 的异常,这解释了问题,但没有得到答案。
一段时间后,我设法找出了Exception
我得到的 s 的原因,并将其记录在What to return when overriding Object.GetHashCode() in classes with no immutable fields? 问题。基本上,这是因为我在公式中使用了可变字段来返回GetHashCode
.
从我收到的关于该问题的非常有用的答案中,我设法加深了对该领域的理解。以下是三个相关规则:
- 如果 x 等于 y,则 x 的哈希码必须等于 y 的哈希码。等效地,如果 x 的哈希码不等于 y 的哈希码,则 x 和 y 必须不相等。
- 当 x 在哈希表中时,x 的哈希码必须保持稳定。
- 散列函数应该在所有输入的所有整数中生成随机分布。
这些规则影响了我不知道从该GetHashCode
方法返回什么的问题的可能解决方案:
- 我不能返回一个常量,因为这会破坏上面的第一条和第三条规则。
- 我不能为每个类创建一个额外的字段,出于同样的原因
readonly
,只能在方法中使用。GetHashCode
我最终采用的解决方案是ObservableCollection
在编辑方法中使用的任何属性之前从其中删除每个项目GetHashCode
,然后再重新添加它。虽然到目前为止这在许多视图中都可以正常工作,但我遇到了进一步的问题,因为我的 UI 项目是使用 custom 动画Panel
的。当我重新添加一个项目时(即使将它插入回集合中的原始索引),它也会再次触发条目动画。
我已经添加了一些基类方法,例如AddWithoutAnimation
, RemoveWithoutAnimation
,这有助于解决其中一些问题,但它不会影响任何Storyboard
动画,重新添加后仍然会触发。所以最后,我们来到了这个问题:
问题:
首先,我想明确说明我没有在我的代码中使用任何Dictionary
对象...... 在我上一个问题中,大多数人似乎都忽略了这一点。因此,如果可以的话,我不能选择简单地不使用...。Dictionary
Exception
ObservableCollection<T>
Dictionary
所以,我的问题是'有没有其他方法可以GetHashCode
在可变类中实现而不违反上述三个规则,或者避免首先实现它?'
我收到了@HansPassant 对上一个问题的评论,该评论表明
一个好的起点是完全删除 Equals 和 GetHashCode 覆盖,从 Object 继承的默认实现非常好,并保证了对象的唯一性。
谁能告诉我如何删除Equals
和GetHashCode
覆盖?在 MSDN 上的IEquatable<T>
Interface页面上,它说它应该为可能存储在通用集合中的任何对象实现,然后在IEquatable<T>.Equals
Method页面上它说If you implement Equals
,您还应该覆盖 and 的基类实现,Object.Equals(Object)
以便GetHashCode
它们的行为是与 的一致IEquatable<T>
。
如果这是可能的,这将是我的首选解决方案。
更新>>>
下载并安装 dotPeek 后,我已经能够查看Exception
实际发生的 PresentationFramework 命名空间。我找到了使用Dictionary
导致此问题的确切部分。它在internal InternalSelectedItemsStorage
类构造函数中:
internal InternalSelectedItemsStorage(Selector.InternalSelectedItemsStorage collection, IEqualityComparer<ItemsControl.ItemInfo> equalityComparer = null)
{
this._equalityComparer = equalityComparer ?? collection._equalityComparer;
this._list = new List<ItemsControl.ItemInfo>((IEnumerable<ItemsControl.ItemInfo>) collection._list);
if (collection.UsesItemHashCodes)
this._set = new Dictionary<ItemsControl.ItemInfo, ItemsControl.ItemInfo>((IDictionary<ItemsControl.ItemInfo, ItemsControl.ItemInfo>) collection._set, this._equalityComparer);
this._resolvedCount = collection._resolvedCount;
this._unresolvedCount = collection._unresolvedCount;
}
Selector
这在调用方法后由类在内部使用ListBoxItem.OnSelected
,所以我只能假设这与在Listbox
.