如果给setter分配相同的值,它是否应立即返回?

14

在实现了INotifyPropertyChanged接口的类中,我经常看到以下模式:

    public string FirstName
    {
        get { return _customer.FirstName; }
        set
        {
            if (value == _customer.FirstName)
                return;

            _customer.FirstName = value;

            base.OnPropertyChanged("FirstName");
        }
    }

准确地说,这些行

            if (value == _customer.FirstName)
                return;

这给我带来了困扰。虽然我经常这样做,但我不确定它是否必要或者好。毕竟,如果调用者分配了完全相同的值,我不想重新分配字段,尤其是当语义上没有改变时,通知我的订阅者属性已更改。

除了通过释放UI更新一些在屏幕/任何媒介上看起来可能相同的东西来节省一些CPU / RAM等资源外,我们还能得到什么?

有些人可以通过为属性重新分配相同的值(虽然这不是一个好的实践)来强制执行刷新吗?

1. 我们应该这样做还是不应该?

2. 为什么?

6个回答

10

当消费者设置一个已经持久化的属性值时,您应该立即返回。

首先,在属性的setter中没有任何理由浪费时间或资源 - 值已经设置,因此不需要进一步操作。如果属性的后备字段存储的值没有改变,则永远不应调用 OnPropertyChanged - 该方法旨在在值发生更改时引发,而不是在调用属性的setter时引发。

尽管如此 - 如果setter没有调用 OnPropertyChanged,我就不会麻烦事先检查值。对于一个只设置后备字段值并且没有其他逻辑的简单setter,始终设置值实际上比先检查然后再设置值要快。只有当属性的setter具有额外逻辑时才使用此模式,这些逻辑既不应该触发也可能会导致不必要的性能损失。


2
我认为你的第二点并不正确,这取决于正在做什么。比较两个字符串要比将引用设置为一个字符串昂贵得多。此外,即使它们不同,你也添加了处理过程。 - galford13x
补充我之前的陈述,根据意图仍可能希望调用OnPropertyChanged。即使值没有改变,有时您可能仍希望进入OnPropertyChanged事件,因为该值已被输入。 - galford13x
我不同意第一点。你称之为资源浪费,但这可能会导致缺少通知。当调用setter时,值确实已更改。它可能是相同的值,但它已经被更改了。 - Sinatr

8

或者您可以这样做:

   set
    {
        if (value != _customer.FirstName)
       {

           _customer.FirstName = value;

          base.OnPropertyChanged("FirstName");
       }
    }

不需要多个返回路径。

进一步回答您的问题,如果属性被同一个值覆盖,我不会强制更新属性。因为这样做没有任何意义,您可能不会从中获得任何好处。(我可以想象有时您希望跟踪每次尝试更新值的情况。)


无论如何,我认为他所问的不是关于确切的代码路径,而是是否可以避免在重复设置时执行setter代码。(对于Web开发人员来说,它像PUT而不是POST。) - Esteban Küber
4
不需要增加嵌套。 :) - ANeves
实际上,很多人认为在if语句中不应该省略{},例如:if(..){return;}。仍然存在嵌套,并且有多个返回路径。 - kemiller2002
作为个人偏好,我不喜欢为单语句块添加花括号... - Andrei Rînea
@Andrei:始终使用大括号的原因是为了最小化出错的可能性。我也不喜欢它们,所以我转向使用Python :) - Esteban Küber

2

我所能想到的反对该模式(即如果值未更改,则返回)的唯一论点是纯粹主义者的观点,即每个函数应该只有一个出口。但我不是一个纯粹主义者,我不同意这种观点。如果值没有改变,避免通知更新,我认为中断是没有问题的。


3
我从未被“每个函数/方法只有一条出口路径”的观点所说服。 - Esteban Küber
我个人遇到过太多这样的情况,即不可行将每个函数限制为单个退出路径,而不必将其分成多个函数,即使这样做也不总是可避免的,否则会使您的代码变得晦涩难懂(如果超过一半的函数位于“else”块内,则在我看来,您正在错误地进行操作)。 - monkey0506

1
唯一不应该使用它的情况是当您知道您的类可能存在脏数据时,例如在ORM层对象上,由于另一个用户的修改,它可能具有过时的值与底层数据库相比。
如果这种情况不影响您,那么就可以缓存!

编辑

我误解了你的问题,因为你谈论的是设置器而不是获取器

类似的观点适用。如果设置操作是昂贵的,并且不应该有任何副作用(它不应该!设置器上的副作用是<blink>邪恶的</blink>!!1),那么这是一个有效的优化。


1
不立即返回的一个原因,我想是为了那些加入晚了的订阅者。他们可能不知道对象的当前状态,如果你立即返回,他们将错过setter通知。

1
无论是后来加入的人在加入时使用了当前值,还是这个值不相关:如果他们使用了当前值,则不需要更新;如果他们没有使用,则选择忽略它,并且不需要通知他们未发生的更改。无论哪种方式,引发事件都是无关紧要的。 - ANeves
@sr,你强调的是。我更注重触发该值的事件(并不是说值不重要,但它次于事件本身)。有一些完全有效的用例,其中某些其他对象只需要知道它发生时的值,并不一定想知道在它订阅之前发生了什么。如果你想不到这样的用例,请告诉我,我会给你几个例子。 - Anurag
我很感激你提出的观点,这对某些应用程序非常相关。最近,我不得不重新设计一个面向方面的解决方案,以应用属性更改通知,因为该方面总是立即返回,如果值相等。这是我大多数时候想要的,但我遇到了一种情况,对事件本身而不是实际值感兴趣。现在我有了可以执行任何操作的方面。我可以看到使用单独的事件来观察而不是“更改”事件的论点。 - DannyMeister

0

大多数情况下,这些属性用于绑定。但是,一旦您开始自己使用NotifyPropertyChanged事件(在自己的标记扩展、行为、VM之间的MVVM等),您就会意识到您希望此事件始终发生。

对我来说,这种检查(通知保护)有点过早优化。我经常使用""作为propertyName引发事件,只是为了刷新绑定或强制执行某些自定义内容,这本身就更加昂贵。

我不认为需要保护每个属性。为什么?从哪种操作?动画正在运行依赖属性上。用户更新(当视图绑定正在更新源时)无论如何都很慢。对于从代码后台引发的任何更新,您几乎需要引发事件。

对我来说,这看起来像是一个没有太多理由而盲目遵循的模式。当然,在某些情况下,您需要防止在某些条件下运行属性设置器代码。如果您添加此类检查以解决某些性能问题,那么这是可以的。但不要提前这样做。


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