web-dev-qa-db-ja.com

C#6.0でINotifyPropertyChangedを実装する方法は?

この質問 への回答は、C#6.0では、INotifyPropertyChangedを次のOnPropertyChangedプロシージャで実装できると編集されています。

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

ただし、その回答から、対応するプロパティ定義がどうあるべきかは明確ではありません。この構成が使用されている場合、INotifyPropertyChangedの完全な実装はC#6.0ではどのように見えますか?

22
T.C.

さまざまな変更を組み込んだ後、コードは次のようになります。変更された部分とそれぞれがどのように役立つかをコメントで強調表示しました

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's 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); }
    }
}
32

プロジェクトでも同じロジックを使用しています。私のアプリにはすべてのビューモデルの基本クラスがあります。

_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に入れます。

覚えておくべきもう1つの重要な詳細

フレームワークでは、PropertyChangedとバインドするすべてのプロパティがpublicである必要があります。

15

私はこの質問が古いことを知っていますが、ここに私の実装があります

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); } }
}
5
Aaron. S