有更好的方法存在吗?

微软应该为INotifyPropertyChanged实现一些动作,就像在自动属性中一样,只需指定{get; set; notify;} {get; set; notify;} {get; set; 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"); }
    }

这不是很大; 如果你愿意,它也可以用作基类。 SetFieldbool返回告诉你它是否是空操作,以防你想应用其他逻辑。


或者更容易使用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:

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

从.Net 4.5开始,最后有一种简单的方法可以做到这一点。

.Net 4.5引入了一个新的主叫信息属性。

private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
     // make sure only to call this if the value actually changes

     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

向函数添加一个比较器也是一个好主意。

EqualityComparer<T>.Default.Equals

这里和这里有更多的例子

另请参阅来电者信息(C#和Visual Basic)


我非常喜欢Marc的解决方案,但我认为它可以稍微改进以避免使用“魔术串”(不支持重构)。 而不是使用属性名称作为字符串,很容易使其成为lambda表达式:

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

只需将以下方法添加到Marc的代码中,它就可以实现这一点:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null)
        throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(selectorExpression);
    return true;
}

顺便说一句,这是受此博客文章更新网址的启发

链接地址: http://www.djcxy.com/p/40743.html

上一篇: does a better way exist?

下一篇: How to Deep clone in javascript