Как реализовать INotifyPropertyChanged в С# 6.0?

Ответ на этот вопрос был отредактирован, чтобы сказать, что в С# 6.0, INotifyPropertyChanged может быть реализована со следующей процедурой OnPropertyChanged:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Однако из этого ответа неясно, каково должно быть соответствующее определение свойства. Что представляет собой полная реализация INotifyPropertyChanged в С# 6.0 при использовании этой конструкции?

Ответ 1

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

public class Data : INotifyPropertyChanged
{ 
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        //C# 6 null-safe operator. No need to check for event listeners
        //If there are no listeners, this will be a noop
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // C# 5 - CallMemberName means we don't need to pass the property name
    protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) 
            return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    private string name;
    public string Name
    {
        get { return name; }
        //C# 5 no need to pass the property name anymore
        set { SetField(ref name, value); }
    }
}

Ответ 2

Я использую ту же логику в своем проекте. У меня есть базовый класс для всех моделей просмотров в моем приложении:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Каждая модель просмотра наследуется от этого класса. Теперь, в настройщике каждого свойства, мне просто нужно вызвать OnPropertyChanged().

public class EveryViewModel : PropertyChangedBase
{
    private bool initialized;
    public bool Initialized
    {
        get
        {
            return initialized;
        }
        set
        {
            if (initialized != value)
            {
                initialized = value;
                OnPropertyChanged();
            }
        }
    }

Почему это работает?

[CallerMemberName] автоматически заполняется компилятором с именем члена, который вызывает эту функцию. Когда мы вызываем OnPropertyChanged из Initialized, компилятор ставит nameof(Initialized) в качестве параметра OnPropertyChanged

Еще одна важная деталь, о которой нужно помнить

Для фреймворка требуется, чтобы PropertyChanged и все свойства, к которым вы привязываетесь, являются public.

Ответ 3

Я знаю, что этот вопрос старый, но вот моя реализация

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

  • Никакая магическая строка
  • Отсутствие отражения
  • Может быть улучшено для подавления поиска по умолчанию по умолчанию

Код:

    public class Bindable : INotifyPropertyChanged
    {
        private Dictionary<string, object> _properties = new Dictionary<string, object>();

        /// <summary>
        /// Gets the value of a property
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        protected T Get<T>([CallerMemberName] string name = null)
        {
            object value = null;
            if (_properties.TryGetValue(name, out value))
                return value == null ? default(T) : (T)value;
            return default(T);
        }

        /// <summary>
        /// Sets the value of a property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="name"></param>
        protected void Set<T>(T value, [CallerMemberName] string name = null)
        {
            if (Equals(value, Get<T>(name)))
                return;
            _properties[name] = value;
            OnPropertyChanged(name);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

используется как

public class Item : Bindable
{
     public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}