INotifyPropertyChanged の実装 - もっと良い方法はありますか? 質問する

INotifyPropertyChanged の実装 - もっと良い方法はありますか? 質問する

INotifyPropertyChangedMicrosoft は、自動プロパティのように、単に指定するだけで、に対して何か便利な機能を実装するべきでした{get; set; notify;}。そうすることが非常に理にかなっていると思います。それとも、それを実行するには何か複雑な点がありますか?

私たち自身で、プロパティに「通知」のようなものを実装できますか。クラスに実装するための適切なソリューションはありますか。INotifyPropertyChangedそれとも、各プロパティでイベントを発生させる以外に実装する方法はありませんかPropertyChanged

PropertyChangedそうでない場合、イベントを発生させるコードを自動生成する何かを記述できますか?

ベストアンサー1

postsharp のようなものを使わずに、私が使用する最小限のバージョンでは次のようなものを使用します。

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

各プロパティは次のようになります。

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, "Name"); }
}

これはそれほど大きくないので、必要に応じて基本クラスとして使用することもできます。他のロジックを適用する場合に備えて、boolからの戻り値はSetFieldそれが何も実行されなかったかどうかを示します。


または、C# 5 ではさらに簡単になります。

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

これは次のように呼び出すことができます:

set { SetField(ref name, value); }

コンパイラは"Name"自動的に追加します。


C# 6.0 では実装が簡単になります。

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

...そして今、C#7 では次のようになります。

protected void OnPropertyChanged(string propertyName)
   => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

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 => name;
    set => SetField(ref name, value);
}

C# 8 と Nullable 参照型を使用すると、次のようになります。

public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}

おすすめ記事