当值发生改变时,我应该在set被调用时还是在INotifyPropertyChanged中引发?

5
当实现`INotifyPropertyChanged`时,`PropertyChanged`应该只在`n != value`时被调用,还是因为调用了`set`而被调用呢?
我在这里寻找的是一个行业标准建议(如果存在的话),哪种实现方式更好以及原因。
示例1-展示了一种“愚蠢”的事件触发策略。
class Person : INotifyPropertyChanged
{
    private string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get
        {
            return name;
        }

        set
        {
            name = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
        }
    }
}

使用案例示例1

Person p = new Person();

p.Name = "John"; // ProperyChanged fired
p.Name = "John"; // ProperyChanged fired

示例2 - 展示了一种“聪明”的事件触发策略

class Person : INotifyPropertyChanged
{
    private string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get
        {
            return name;
        }

        set
        {
            if(name != value)
            {
                name = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
            }
        }
    }
}

使用案例示例2

Person p = new Person();

p.Name = "John"; // ProperyChanged fired
p.Name = "John"; // ProperyChanged ignored as name == value

请注意示例2中的if(name != value),它只允许值的更改,并在传入的值与现有值不匹配时触发ProperyChanged事件。


这就是编程的伟大之处:你可以让它做任何你想让它做的事情。 - Jeroen Vannevel
我认为这可能是那种“情况因人而异”的答案之一。 - user6845125
我只是想知道这两种方法之间是否有“推荐”的方法,以及为什么? - Matthew Layton
你已经列出了两种选项和第二个版本的优点。这完全取决于你的用例。很可能你想要获取每个事件,就像你可能只想要真实的变化一样。 - Jeroen Vannevel
@JeroenVannevel 我明白这有点“因情况而异”的答案。我只是想从性能的角度来看,如果对象经常被访问,示例1似乎会变得有点昂贵。 - Matthew Layton
我在“基于观点”的情况下有些不同意闭包,OP正在询问是否存在任何标准以及随之而来的利弊。尽管它没有像命名空间或变量命名约定(例如:https://dev59.com/-3NA5IYBdhLWcg3wjOhS)那样严格定义,但了解哪些标准被定义以及什么被认为是最佳实践是有帮助的。 - Stefan
3个回答

4
正如接口名称INotifyPropertyChanged所示,根据msdn的说明:

INotifyPropertyChanged接口用于通知客户端(通常是绑定客户端)属性值已更改。

最好只在值改变时触发事件。

https://msdn.microsoft.com/zh-cn/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx

典型的实现模式应该像这样:
public string Foo
{
    get { return _foo; }
    set
    {    
        if (string.Equals(_foo, value)) return;

        _foo= value;
        OnPropertyChanged();
    }
}

顺便提一下,这也是ReSharper的惯例,可以在此处找到:https://www.jetbrains.com/help/resharper/2016.1/Coding_Assistance__INotifyPropertyChanged_Support.html 作为一个附注:仅在值更改时触发将保护您免受更新从其他值计算而来的值时出现循环依赖的影响。
例如:考虑以下代码:
public string DependentFoo
{
    get { return _foo; }
    set
    {    
        if (string.Equals(_foo, value)) return;

        _foo= value;

        //if some condition:
        DependentBar = "";
        OnPropertyChanged();
    }
}

public string DependentBar
{
    get { return _bar; }
    set
    {    
        if (string.Equals(_bar, value)) return;

        _bar = value;

        //if some condition:
        DependentFoo = "";
        OnPropertyChanged();
    }
}

虽然这不是一个很好的例子,但我认为你会明白要点。

2
INotifyPropertyChanged”文档中表示该接口的目的是通知客户端属性值已发生更改。这显然是指“属性值”的更改。文档页面上的示例也使用了您的第二个示例中的模式。

-2

没有一种“正确”的方法。这完全取决于你需要它做什么。 例如:

如果你只需要更新GUI,那么我会说当属性实际上没有改变时调用PropertyChanged是不必要的。然而,如果你想将事件用于其他目的(如日志记录或类似的东西),那么它绝对可以成为一种解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接