在属性设置器中调用方法是否正确?

时间:2019-05-24 11:26:30

标签: c# wpf

在我正在使用的WPF应用程序中,我遇到了以下代码片段;

public int SomeInteger
{
    get { return _someInteger; }
    set
    {
        _someInteger= value;
        OnPropertyChanged("SomeInteger");
        SomeIntegerChanged();
    }
}

如您所见,在属性设置器中调用了一个方法。这种方法是正确的还是在MVVM模式WPF中有更好的方法?

上面的代码段是一个示例,实际上,当属性更改时,属性设置器可能会加载DataTable,这可能很耗时。我尝试使用asyncawait使应用程序异步,但是由于asyncawait没有异步属性概念,因此上述情况会导致问题。

我还找到了一种将事件绑定到INotifyPropertyChanged的方法,并通过检查哪个属性触发了该事件并调用了该方法。但是正在寻找其他选择。

4 个答案:

答案 0 :(得分:5)

  

这种方法是正确的还是在MVVM模式WPF中有更好的方法吗?

不幸的是,这是跨所有C#的通用和公认的MVVM模式。我说“不幸”是因为这种模式确实遇到了问题。它基于事件-本身就存在问题-并且还会导致立即更新,这在许多情况下是不希望的。

例如,有时希望具有两个相互依赖的属性-当用户更改一个属性时,另一个属性相对于它改变-这会引起问题,因为无法将“用户更改”与“代码更改”。

再举一个例子,有时您可能会得到一整套依赖属性的树,并且所有更改都需要花费一些时间才能传播并停止相互干扰。

更现代的MVVM方法(如原子更新的单一真理源单向数据流(由Redux推广))避免了上述问题。有一个C#Redux实现;我不知道使用起来有多容易。在我自己的CalculatedProperties library中,我建立了自己的“无效队列”来解决此问题,defers all PropertyChanged updates直到计算出整个系统的新稳态之后。

  

我试图使用async和await使应用程序异步,但是由于async和await没有异步属性概念,因此上述情况会导致问题。

是的。特别是,您不能“异步获取”属性。这在MVVM世界中是有意义的。当WPF更新屏幕时,它要求您的VM提供数据值,那么您的VM无法说“保持,我将在一分钟内得到它”。 WPF需要知道该值 now ,以便它可以更新屏幕 now

连接一个异步事件处理程序是在值更改时启动异步工作的一种方法。这种方法很好。通常,SomeIntegerChanged的存在意味着该属性专门存在一个“已更改”事件,因此使用字符串比较插入PropertyChanged可能是更困难的方法

有关MVVM的异步属性的更多信息,请参见我的article on the subject

答案 1 :(得分:2)

在这种情况下,可以在Setter中调用方法。在WPF中将INotifyPropertyChanged用于属性更改通知是正确的方法,将调用添加到Setter是实现它的默认方法。

SomeIntegerChanged();的调用在某种程度上取决于该方法的功能(如果已经实现了INotifyPropertyChanged,甚至需要调用该方法)。

将属性用作消费者应该具有尽可能小的副作用,并且通常应该是简单的操作。 MSDN上有关于该主题的不错的指南。

您还应避免使用属性名称字符串作为参数,在这种情况下,对OnPropertyChanged的调用应如下所示:OnPropertyChanged(nameof(SomeInteger));

答案 2 :(得分:2)

属性设置器不应该启动长期运行的操作。它应该简单地设置后备字段的值,并可能调用几乎立即返回的快速同步方法。有关更多信息,请参阅@Stephen Cleary的blog post

如果每次设置SomeInteger时都需要调用异步方法,则可以例如为async事件挂接一个PropertyChanged事件处理程序,例如:

this.PropertyChanged += async (s, e) =>
{
    if (e.PropertyName == nameof(SomeInteger))
    {
        await SomeAsyncMethod();
    }
};

答案 3 :(得分:0)

我会提防过早的优化。

不要使您的解决方案复杂化。

如果更改是立即进行的,那么将您喜欢的内容放入设置器中就没有问题。

依赖的属性更改只是偶尔的要求,但通常也没什么大碍。

如果您需要漫长的过程才能开始,那么听起来好像应该是离散的。您应该在单独的线程上启动该处理,或者解耦该处理。您可能只是开火而忘记线程(不是很优雅)。

您可以使用服务调用,消息队列(例如msmq或RabbitMQ或pub sub)来分离处理。有很多pub子方法-一个简单的实现是mvvmlight的Messenger或Prism的eventaggregator。更复杂的将是NServiceBus甚至Windows Workflow。如果涉及事件,那么弱事件是可取的。