1. ホーム
  2. c#

[解決済み] INotifyPropertyChangedの実装 - より良い方法は存在するか?

2022-03-14 15:32:19

質問

マイクロソフトは INotifyPropertyChanged を指定するだけで、自動生成のプロパティのように {get; set; notify;} そうすると、とても理にかなっていると思うんです。それとも、何か複雑な問題があるのでしょうか?

私たち自身が、プロパティに「notify」のようなものを実装することは可能でしょうか。を実装するための優雅な解決策はあるのでしょうか? INotifyPropertyChanged を発生させるしかないのでしょうか? PropertyChanged イベントを各プロパティで実行します。

を発生させるコードを自動生成するようなものを書けないでしょうか? PropertyChanged イベント

解決方法は?

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 は、他のロジックを適用したい場合に備えて、それがno-opであったかを教えてくれます。


または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);
}