【问题标题】:WinForms: Binding functionality is broken when overriding GetHashCode in base classWinForms:在基类中覆盖 GetHashCode 时绑定功能被破坏
【发布时间】:2016-08-24 13:35:39
【问题描述】:

我已将代码简化为以下重现错误的简短示例。我正在尝试将派生类的值绑定到控件。派生类和绑定逻辑是:

bindingSource = new BindingSource();
numericUpDown1.DataBindings.Add("Value", bindingSource, nameof(SinglePoint.Val), false, DataSourceUpdateMode.OnPropertyChanged);
bindingSource.DataSource = new[] { new SinglePoint(100) };
[...]
public class SinglePoint : PointList
{
    public SinglePoint(int val) { points.Add(val); }
    public int Val
    {
        get { return points[0]; }
        set
        {
            if (value > 300) value = 300; //(*)
            points[0] = value;
        }
    }
}

因此,例如在NumericUpDown 上设置值 500 时,离开它后我们应该看到 300,因为 (*) 行。如果基类未实现GetHashCode,则此方法有效:

public class PointList
{
    protected List<int> points = new List<int>();
    public PointList() { }
    // UNCOMMENT this and the binding will stop working properly
    //public override int GetHashCode()
    //{
    //    unchecked
    //    {
    //        int hashCode = 17;
    //        foreach (var item in points)
    //            hashCode += 13 * item.GetHashCode();

    //        return hashCode;
    //    }
    //}
}

但是,如果您尝试取消注释 GetHashCode 实现,绑定将会中断。这意味着在设置 500 并离开 NumericUpDown 之后,控件仍将显示 500,但实际上基础值将是 300。为什么会发生这种情况,如何在不关闭 GetHashCode 的自定义实现的情况下解决它?

编辑

正如你们中的一些人所指出的,GetHashCode 不应更改,因为它被绑定机制使用。这是对我问题的第一部分的答复。但是,如何使我的 PointList 类满足另一个 rule 的要求,“如果两件事相等 (Equals(...) == true) 那么它们必须为 GetHashCode() 返回相同的值 em>”。如果我有两个 PointList 对象具有相同的点列表(序列),我希望它们被发现相等(因为我的代码中的其他逻辑),这应该意味着具有相同的哈希码。你会怎么做?

【问题讨论】:

    标签: c# winforms data-binding


    【解决方案1】:

    拇指规则:

    GetHashCode 返回的整数不能改变,而对象包含在依赖于哈希码保持稳定的数据结构中

    请参阅 Eric Lippert 的文章 here

    GetHashCode 中,您应该只使用只读或不可变字段;否则,该对象不能用于DictionaryHashtable 和其他基于散列的集合。

    【讨论】:

    • 谢谢我现在看到了!您能否检查我对问题第二部分的修改?
    • 按照惯例,如果 Equals 包含相同的值,.NET 集合不会返回 true。你应该让原来的GetHashCodeEquals 来反映引用相等。但是,您可以实现IEnumerable&lt;T&gt; 接口并使用SequenceEqual 扩展方法,也可以实现自定义相等检查或重载== 运算符等。
    【解决方案2】:

    WinForms 使用数据对象和数据成员的哈希码生成密钥,用于跟踪内部存储中的数据绑定对象。如果您更改哈希计算,以便在数据成员更改值时更改哈希,则先前生成的密钥不会随之更新,因此与当前密钥不同。这打破了数据绑定机制。

    来源:http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/BindingContext.cs

    【讨论】:

      【解决方案3】:

      您应该使用 base.GetHashCode() 来初始化它,而不是将哈希码初始化为 17。 int hashCode = base.GetHashCode(); // 不是 17

      【讨论】:

      • 问题没有改变;请同时检查您提出的解决方案
      猜你喜欢
      • 2022-08-20
      • 2014-08-01
      • 2011-06-23
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 2012-07-13
      • 1970-01-01
      相关资源
      最近更新 更多