Зачем переписывать .GetHashCode очистить эти значения данных в WinForms?

Мы столкнулись с странной ошибкой, с которой у нас возникают проблемы с отладкой.

У нас есть рабочее пространство MDI, которое использует компоненты Microsoft CAB, DevExpress и .Net 3.5.

Если пользователи открывают два окна в рабочей области, каждая из которых содержит 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();

Здесь стек вызовов при сверке второго окна:

enter image description here

При вызове EndEditSession() в трассировке стека выше он вызывает EndEditSession для минимизации второго окна, а к тому времени, когда трассировка стека пройдет мимо [External Code] до точки останова OnChange, которую я установил, это запуск метода изменения в первом окне.

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 для двух форм и элементов управления является отдельным.

Возможно ли, что наличие двух отдельных копий a UserControl, привязанных к двум отдельным экземплярам модели данных, приведет к смешению своих привязок, когда метод GetHashCode был переопределен, так что эти два объекта считаются равными?

Я не очень хорошо знаком с внутренней работой системы привязки WinForms или с тем, как управляется рабочее пространство CAB MDI.

Моя теория заключается в том, что когда первое окно минимизирует, оно выгружает элементы управления для сохранения в памяти, а затем, когда второе окно сводит к минимуму внутреннюю хеш-таблицу, которая управляет привязками, неправильно путает и запускает обновление, чтобы принимать данные из сначала свернутое окно (которое теперь пусто) и обновление его источника данных. В этой теории много дыр, однако это единственное, о чем я могу думать.

Ответы

Ответ 1

Я не знаю внутренней работы виджета WinForm, но кажется, что, поскольку вы столкнулись с проблемой с переопределением, вам будет лучше работать.

Если вам нужно оценить равенство для своих целей:

Подходом является предоставление собственного метода для оценки равенства, а не для изменения поведения по умолчанию.

Если вы намерены изменить способ обработки объектов виджетами:

Подходом является создание статического объекта factory для вашего класса. factory может содержать коллекцию всех объектов, созданных с использованием слабых ссылок. Слабые ссылки позволяют GC собирать объекты. factory может проверить коллекцию ранее созданных объектов. Если совпадение найдено, верните существующий. Если нет, то создайте его. Таким образом, вместо того, чтобы иметь два разных объекта, которые оценивают два равных (override equals), у вас будет один объект с двумя одинаковыми ссылками (одинаковая память).

Надеемся, что один из этих подходов поможет решить вашу проблему.

Ответ 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. Возможно, что во время заполнения этого объекта он заполнен неправильными значениями.