Как проверить, изменился ли объект? - PullRequest
18 голосов
/ 31 октября 2011

У меня есть объект, который содержит другие объекты, которые содержат другие объекты, включая списки и так далее. Этот объект привязан к форме, предоставляя пользователю множество полей на разных вкладках. Я также использую обзор данных master-child.

Есть идеи, как проверить, изменилось ли что-либо в этом объекте относительно более раннего момента? Без (вручную) добавления измененной переменной, которая установлена ​​в true во всех (> 100) заданных методах.

Ответы [ 6 ]

8 голосов
/ 31 октября 2011

Как сказал Sll, грязный интерфейс, безусловно, хороший путь. Далее мы хотим, чтобы коллекции были грязными, но мы не хотим обязательно устанавливать ВСЕ дочерние объекты как грязные. Однако мы можем объединить результаты их грязного состояния с нашим собственным грязным состоянием. Поскольку мы используем интерфейсы, мы предоставляем объектам возможность определять, являются ли они грязными или нет.

Мое решение не скажет вам , что грязно, просто что состояние любого объекта в любое время грязное или нет.

public interface IDirty
{
    bool IsDirty { get; }
}   // eo interface IDirty


public class SomeObject : IDirty
{
    private string name_;
    private bool dirty_;

    public string Name
    {
        get { return name_; }
        set { name_ = value; dirty_ = true; }
    }
    public bool IsDirty { get { return dirty_; } }
}   // eo class SomeObject


public class SomeObjectWithChildren : IDirty
{
    private int averageGrades_;
    private bool dirty_;
    private List<IDirty> children_ = new List<IDirty>();

    public bool IsDirty
    {
        get
        {
            bool ret = dirty_;
            foreach (IDirty child in children_)
                dirty_ |= child.IsDirty;
            return ret;
        }
    }

}   // eo class SomeObjectWithChildren
4 голосов
/ 31 октября 2011

Вы можете реализовать интерфейс INotifyPropertyChanged, и если вы используете VS2010, есть надстройка, которая автоматически изменяет все свойства в IL (поэтому вам не нужно реализовывать это вручную).

Я верю, что есть и другие методы, которые используют технику плетения.

Я нашел дополнение в галерее vs2010:

http://visualstudiogallery.msdn.microsoft.com/bd351303-db8c-4771-9b22-5e51524fccd3

Вот хороший пример - ваш код:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string GivenNames { get; set; }
}

Что компилируется:

public class Person : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    private string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }
}

Это из первого результата из unce G (может быть полезным):

http://justinangel.net/AutomagicallyImplementingINotifyPropertyChanged

http://www.codeproject.com/KB/WPF/AutonotifyPropertyChange.aspx

3 голосов
/ 31 октября 2011

Как вы определяете «равенство» (между старым и новым состоянием)?

  • Вы сравниваете только свойства или поля?
  • Вы сравниваете только общедоступные свойства / поля?
  • Вы когда-нибудь игнорировали какие-либо свойства / поля (т.е. их модификации не имеют значения)?
  • Как вы сравниваете «атомарные» типы (например, все сравнения строк не чувствительны к регистру, или вам также нужно учитывать регистр в некоторых местах).

Если ответы на эти вопросы достаточно общие (т. Е. Вы можете разработать набор правил, применимых ко всем вашим объектам), то теоретически вы можете достичь того, что вы хотите, с помощью рефлексии : основная идея состоит в том, прочитать все свойства / поля «корневого» объекта, затем сохранить «атомарные» и рекурсивно спуститься в «неатомные» (и повторить весь процесс). Позже, когда вы захотите проверить, изменилось ли что-нибудь, вы должны повторить рекурсивное снижение и сравнить результаты с сохраненными значениями.

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

0 голосов
/ 26 мая 2016

я могу предложить что-то вроде этого:

public class ObjectBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(propertyName, true);
        }

        protected virtual void OnPropertyChanged(string propertyName, bool makeDirty)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            if (makeDirty)
                _IsDirty = true;
        }


        bool _IsDirty;

        public bool IsDirty
        {
            get { return _IsDirty; }
        }

    }

тогда в accesor свойства вы можете

public class Vehicle : ObjectBase
{


  int _CarId;

 public int CarId
        {
            get { return _CarId; }
            set
            {
                if (_CarId != value)
                {
                    _CarId = value;
                    OnPropertyChanged(nameof(CarId));
                }

            }
        }
  }

надеяться, что это поможет

0 голосов
/ 31 октября 2011

Сравнение хеш-кодов с течением времени может быть вариантом.Если вы не хотите добавлять эту логику, вы можете дважды сериализовать объект и сравнить хэш-коды двух результирующих строк.

РЕДАКТИРОВАТЬ, чтобы включить некоторые комментарии Посмотрите на этот вопрос/ answer: Может ли сериализация одного и того же объекта создавать разные потоки?

Так что помните о сериализаторе, который ** не ** гарантирует одинаковый выход для одного и того же объекта дважды.

0 голосов
/ 31 октября 2011

Вы можете переопределить GetHashCode, а затем создать хеш-код, который представляет собой смесь свойств объекта. Таким образом, ваша программа получит хеш-код объекта, сохранит его и затем при следующей проверке сравнит его с текущим хеш-кодом.

Очень упрощенный подход:

internal class Foo
{
    public string Bar { get; set; }
    public int Baaz { get; set; }

    public override int GetHashCode()
    {
        return Bar.GetHashCode() - Baaz.GetHashCode();
    }
}

Будьте осторожны , поскольку вы должны искать Bar, не являясь null, а также учитывать целочисленные переполнения.


EDIT

После просмотра блога Eirc Lippert ,

Я согласен, что GetHashCode не должен использоваться.

Я сохраняю ответ, чтобы сохранить богатствообсуждений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...