为啥覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值?

Posted

技术标签:

【中文标题】为啥覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值?【英文标题】:Why would overwriting .GetHashCode clear these databound values in WinForms?为什么覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值? 【发布时间】:2014-08-01 22:50:58 【问题描述】:

我们遇到了一个奇怪的错误,我们在调试时遇到了问题。

我们有一个使用 Microsoft CAB、DevExpress 组件和 .Net 3.5 的 MDI 工作区。

如果用户在工作区中打开两个窗口,每个窗口都包含绑定到两个单独数据模型的 UserControl,然后将它们都最小化,则要最小化的第一个窗口会在第二个最小化时清除其绑定字段。

数据模型的.Equals.GetHashCode 方法已被覆盖,因此这两个数据模型被视为相等。如果我们改变它,使它们是唯一的,我们就不会得到这种行为。

这是一些显示问题的示例伪代码

var a = new MyWindow();
a.DataModel = new SomeClass(123);
a.ShowInMdiWorkspace();

var b = new MyWindow();
b.DataModel = new SomeClass(123);
b.ShowInMdiWorksace();

a.Minimize();

// If SomeClass.GetHashCode() is overwritten to consider two objects  
// as equal based on the value passed in, then the data bindings for A
// get cleared on this call. If SomeClass.GetHashCode is unique, then 
// this problem does not happen.
b.Minimize();

这是第二个窗口最小化时的调用堆栈:

在上面的堆栈跟踪中的EndEditSession() 调用中,它正在调用EndEditSession 以使第二 窗口最小化,而当堆栈跟踪通过[External Code] 到OnChange我设置的断点,它正在触发 first 窗口中的 change 方法。

EndEditSession() 是我们自定义的东西,看起来像这样

protected void EndEditSession()

    IBindingValue bv = null;

    if (_bindingValues == null)
        return;

    if (_data != null)
    
        foreach (KeyValuePair<string, IBindingValue> kvp in _bindingValues)
        
            bv = kvp.Value;
            if (bv.IsBindable)
                ((PropertyManager)bv.Component.BindingContext[_data]).EndCurrentEdit();
        
    


_bindingValues 在 UserControl 初始化其数据绑定时填充。键字段是绑定控件的名称,值字段是自定义对象,其中存储控件本身、控件名称、绑定值和默认值。 bv.Component 返回设置绑定的控件,在我的测试中是自定义的 DevExpress LookupEdit

_data 包含UserControl 的数据模型,我可以验证它是否设置为第二个窗口的实例。

我最初的想法是BindingContext 是共享的,因此返回了错误的PropertyManager,但是我已经验证了两个表单和控件的.BindingContext 是分开的。

GetHashCode 方法被覆盖以使这两个对象被视为相等时,是否有可能将UserControl 的两个单独副本绑定到数据模型的两个单独实例时会混淆其绑定?

我不太熟悉 WinForms 绑定系统的内部工作原理,也不了解 CAB 的 MDI 工作区是如何管理的。

我的理论是,当第一个窗口最小化时,它正在卸载控件以节省内存,然后当第二个窗口最小化管理绑定的内部哈希表时,会错误地混淆并运行更新以从第一个最小化窗口(现在为空白)并更新其数据源。这个理论有很多漏洞,但这是我唯一能想到的。

【问题讨论】:

【参考方案1】:

我不知道 WinForm 小部件的内部工作原理,但似乎既然您遇到了 overriding equals 的问题,那么您最好解决强>。

如果您需要为自己的目的评估平等:

一种方法是提供您自己的方法来评估相等性,而不是更改默认行为。

如果您的意图是更改小部件处理对象的方式:

一种方法是为您的类创建一个静态对象工厂。工厂可以维护使用弱引用创建的所有对象的集合。弱引用允许 GC 收集对象。然后工厂可以检查以前创建的对象的集合。如果找到匹配项,则返回现有匹配项。如果没有,则创建它。这样,您将拥有一个具有两个相等引用(相同内存)的单个对象,而不是让两个不同的对象评估两个相等(覆盖等于)。

希望这些其他方法之一可以解决您的问题。

【讨论】:

【参考方案2】:

BindingContext 对象不与任何其他 BindingContext 共享其字段和属性,因为它的字段和属性不是静态的。 但是,可以有一个BindingContext 对象用于多个控件。

在第一种情况下,如果多个控件具有相同的父级并且没有自己的BindingContext,则此控件的BindingContext 属性将返回Control.Parent(.Parent...).BindingContext 对象。 在第二种情况下,可能是这样的:

var bindingContext = new BindingContext();
var a = new SomeControl();
var b = new SomeControl();
a.BindingContext = bindingContext;
b.BindingContext = bindingContext;

在第三种情况下,BindingContext 可以用这种方式覆盖。 我不知道你的情况是怎么回事,所以我只能建议在初始化数据绑定之前做这样的事情:

var a = new SomeControl();
var b = new SomeControl();
a.BindingContext = new BindingContext();
b.BindingContext = new BindingContext();

如果这不能解决您的问题,那么您需要检查 _bindingValues 对象的填充情况。可能在填充此对象期间填充了错误的值。

【讨论】:

这也是我的第一个想法,但是在分配每个控件时仍然会出现同样的问题BindingContext @Rachel 你能提供一个示例项目吗?似乎没有足够的信息来说明您的问题。我认为问题不在于BindingContext。问题可能出在您的 _bindingValues 对象中。 我实际上无法在示例项目中重现这一点,我认为这是因为我没有在示例中使用智能部件创建完整的 MDI 工作区。我使用的示例代码只是一个带有 TabControl、一个自定义对象和一个带有自定义绑定代码的自定义 UserControl 的表单。我今天再试一次,看看能否在更大的示例项目中重现该问题。

以上是关于为啥覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我需要覆盖 C# 中的 .Equals 和 GetHashCode [重复]

如果在覆盖 Equals() 时未能覆盖 GetHashCode(),会出现啥问题? [复制]

覆盖 GetHashCode [重复]

未调用 C#GetHashCode/Equals 覆盖

如何在没有任何数字作为字段的情况下覆盖 GetHashCode()?

包含通用数组的对象的 GetHashCode 覆盖