Почему состояние просмотра ASP.NET теряет идентичность сериализованных объектов?

Краткий вопрос: когда я дважды помещаю один и тот же экземпляр объекта в состояние просмотра, после десериализации получается два экземпляра. Я хочу, чтобы был только один экземпляр. Можно ли это сделать и как?

Многословное объяснение:

Рассмотрим следующий код:

public partial class MyControl : System.Web.UI.UserControl
{
    [Serializable]
    class MyClass
    {
        public string x;
    }
    public void Page_Load(object sender, EventArgs e)
    {
        if (this.IsPostBack)
        {
            MyClass a = (MyClass)this.ViewState["a"];
            MyClass b = (MyClass)this.ViewState["b"];
            MessageManager.Show((a == b).ToString(), MessageSeverity.Debug);
        }
        else
        {
            var x = new MyClass() { x = "stackoverflow" };
            this.ViewState["a"] = x;
            this.ViewState["b"] = x;
            MessageManager.Show("Init", MessageSeverity.Debug);
        }
    }
}

Когда он запускается и инициируется обратная передача, я получаю сообщение «false». То есть, хотя я поместил один объект в состояние просмотра, он дважды сериализовался. Это можно проверить, проверив содержимое состояния просмотра.

Если я попытаюсь поместить объекты перекрестных ссылок в состояние просмотра, каждый элемент будет сериализован как отдельный граф. Проиллюстрировать:

public partial class MyControl : System.Web.UI.UserControl
{
    [Serializable]
    class MyClass
    {
        public string x;
        public MyClass other;
    }
    public void Page_Load(object sender, EventArgs e)
    {
        if (this.IsPostBack)
        {
            MyClass a = (MyClass)this.ViewState["a"];
            MyClass b = (MyClass)this.ViewState["b"];
            MessageManager.Show((a.other == b).ToString(), MessageSeverity.Debug);
            MessageManager.Show((a.other.other == a).ToString(), MessageSeverity.Debug);
        }
        else
        {
            var a = new MyClass() { x = "stack" };
            var b = new MyClass() { x = "overflow" };
            a.other = b;
            b.other = a;
            this.ViewState["a"] = a;
            this.ViewState["b"] = b;
            MessageManager.Show("Init", MessageSeverity.Debug);
        }
    }
}

Теперь я получаю сообщения «Ложь» и «Верно» (именно в таком порядке). Опять же, проверка Viewstate показывает, что каждый объект сериализовался дважды. Что дает? Я проверил источник System.Web.UI.StateBag с помощью ILSpy, но он просто помещает все значения в ArrayList, и в нем также нет специального кода сериализации. Итак, кто бы ни сериализовал состояние просмотра (System.Web.UI.ObjectStateFormatter?), Каким-то образом берет каждый объект и сериализует его как отдельный граф ... почему ??? И я могу обойти это?

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

Я мог бы реализовать собственное сравнение десятком разных способов, но, поскольку я хочу сделать это для произвольных объектов, это становится довольно сложно. ;)


person Vilx-    schedule 19.05.2011    source источник
comment
@leppie - вот как работает эта конкретная сериализация; это не обязательно верно для других   -  person Marc Gravell    schedule 19.05.2011


Ответы (2)


вы можете переопределить оператор == для своего класса или переопределить Equals и провести сравнение через a.Equals(b), чтобы увидеть, совпадают ли они, вместо того, чтобы полагаться на равенство по умолчанию на основе адреса.

person Sam Holder    schedule 19.05.2011
comment
Да, я знаю об этом, но тогда мне нужно было бы реализовать индивидуальное сравнение для каждого объекта, который я собираюсь туда поместить. Я бы хотел что-нибудь более элегантное, если это возможно. - person Vilx-; 19.05.2011
comment
да, я подумал, что это будет так, просто указав на очевидное, на случай, если лучшее решение не придет, и кто-то другой пройдет мимо в будущем. - person Sam Holder; 19.05.2011
comment
Я приму ваш ответ, хотя вместо переопределения Equals я создал собственный компаратор IEqualityComparer. Менее инвазивный. - person Vilx-; 19.05.2011

Viewstate - это словарь, именно так он и должен работать. Если вы хотите сохранить одно и то же значение и иметь его только один раз, вам следует записать тот же «ключ», который перезапишет предыдущее значение.

Также вы пробовали сравнение равенства, выполнив a.Equals (b)

person Michael Christensen    schedule 19.05.2011
comment
Смотрите обновление. И я не понимаю, почему он так задумал. На самом деле, я бы сказал, что это довольно нелогично. Если я вставлю одно и то же дважды, я ожидаю, что получу одно и то же дважды. Это где-нибудь задокументировано? - person Vilx-; 19.05.2011