Как захватить старое значение и новое значение в INotifyPropertyChanged реализации события PropertyChanged в C#

Мне нужно захватить старое значение, новое значение и имя свойства в обработчике событий PropertyChanged. Реализация по умолчанию INotifyPropertyChanged предоставляет только имя свойства. Поэтому после поиска в интернете я обнаружил, что подобный вопрос здесь с расширенной реализацией свойства изменен.

Ниже приведена ссылка на это Q и объявление интерфейса:

Событие NotifyPropertyChanged, в котором в аргументах события содержится старое значение

public interface INotifyPropertyChangedExtended<T>
{
    event PropertyChangedExtendedEventHandler<T> PropertyChanged;
}

открытый делегат void PropertyChangedExtendedEventHandler(отправитель объекта, PropertyChangedExtendedEventArgs e);

Выше интерфейс решит мою проблему, однако я не понимаю, как реализовать универсальный интерфейс в моем классе Entity, потому что T будет меняться в зависимости от типа данных свойства.

Может ли кто-нибудь помочь мне в понимании, удастся ли реализовать этот интерфейс с параметром T. Пример реализации этого будет действительно полезным.

Заранее спасибо за помощь.

Umesh

РЕДАКТИРОВАТЬ #1: на основе ответа от Питера, размещение обновленного кода, который может быть полезен для тех, кто хочет захватить старые и новые значения в событии PropertyChanged.

    public class PropertyChangedExtendedEventArgs : PropertyChangedEventArgs
{
    public string OldValue { get; set; }

    public string NewValue { get; set; }

    public PropertyChangedExtendedEventArgs(string propertyName, string oldValue, string newValue) : base(propertyName)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}

    //
// Summary:
//     Notifies clients that a property value has changed.
public interface INotifyPropertyChangedEnhanced
{
    //
    // Summary:
    //     Occurs when a property value changes.
    event PropertyChangedEventHandlerEnhanced PropertyChanged;
}

public delegate void PropertyChangedEventHandlerEnhanced(object sender, PropertyChangedExtendedEventArgs e);

public abstract class BindableBase : INotifyPropertyChangedEnhanced
{
    public event PropertyChangedEventHandlerEnhanced PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] 
    string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        var oldValue = storage;
        storage = value;
        this.OnPropertyChanged(oldValue, value, propertyName);
        return true;
    }
    protected void OnPropertyChanged<T>(T oldValue, T newValue, [CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedExtendedEventArgs(propertyName, oldValue?.ToString(), newValue?.ToString()));
    }
}

2 ответа

Решение

Я не понимаю, как реализовать универсальный интерфейс в моем классе Entity, потому что T будет меняться в зависимости от типа данных свойства

Правильно. Ответ, на который вы смотрите, на самом деле не так уж и хорош, потому что он содержит код, который не компилируется и не имеет смысла.

Основная идея поднятия неуниверсального PropertyChanged событие, передавая универсальный класс args, который наследует неуниверсальный PropertyChangedEventArgs класс сам по себе в порядке. Это должно работать нормально в вашем случае.

Но! Вы не сможете реализовать универсальный интерфейс, предлагаемый в ответе, потому что (как вы уже заметили) интерфейс не работает, когда свойства имеют разные типы. Фактически, ответ, который вы ищете, даже не скомпилируется с этим интерфейсом, потому что их метод сбора событий сам по себе является общим и вызывает неуниверсальный метод. PropertyChanged событие (метод был бы несовместим с универсальным интерфейсом), и они не удосужились бы на самом деле реализовать свой универсальный интерфейс (если бы они попробовали, они бы заметили, что универсальный метод не работает с универсальным интерфейсом... параметр типа метода T отличается от параметра типа интерфейса T).

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

Просто реализуйте свой собственный PropertyChangedEventArgs.

Реализация нового интерфейса не требуется.

Это решение работает. Вот одно из преимуществ полиморфизма

public class PropertyChangedExtendedEventArgs : PropertyChangedEventArgs
{
        public virtual object OldValue { get; private set; }
        public virtual object NewValue { get; private set; }

        public PropertyChangedExtendedEventArgs(string propertyName, object oldValue, 
               object newValue)
               : base(propertyName)
        {
            OldValue = oldValue;
            NewValue = newValue;
        }
    }

public class NotifyObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName, object oldvalue, object newvalue)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedExtendedEventArgs(propertyName, oldvalue, newvalue));
        }
    }



public class Person : NotifyObject
    {
        private int _name;
        public int Name
        {
            get { return _name; }
            set
            {
                var oldValue = _name;
                _name = value;
                NotifyPropertyChanged("Name", oldValue, value);

            }
        }
    }

И ваша модель взгляда

Person person = new Person();
person.PropertyChanged += person_PropertyChanged;

  void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var args = e as PropertyChangedExtendedEventArgs;
            //do whatever you want
        }

Как упоминал @Andy, вы можете реализовать INotifyPropetyChanging, чтобы получить старое значение, но хранение - это большая громоздкая задача, поэтому я создал вспомогательный класс NotifyPropertyChangingCache, который вы можете использовать следующим образом:

class EntityBase : INotifyPropertyChanged, INotifyPropertyChanging
{ /* ... */ }

// initiating the entity
var entity = new EntityBase();

// initiating the cache:
var notifyPropertyChangingCache = new Ice1e0.ComponentModel.NotifyPropertyChangingCache();
entity.PropertyChanging += notifyPropertyChangingCache.OnPropertyChanging;

// your property changed event looks like this
entity.PropertyChanged += (s, e) =>
{
    var oldValue = notifyPropertyChangingCache.GetLastPropertyValue(e.PropertyName);

    // your code goes here ...
};

// when you are done with your entity, don't forget to remove the events (we don't want to create a memory leak ...)
entity.PropertyChanging -= notifyPropertyChangingCache.OnPropertyChanging;
//entity.PropertyChanged -= ... (I assume you use not a in-line method here as I did in this example)

Ты не можешь Реализуйте также INotifyPropetyChanging. Это обеспечит доступ к старому значению. Вы можете найти примеры реализации для обоих интерфейсов в документации.

Другие вопросы по тегам