我很想知道这两个课程选项之间的差异和/或好处。我在堆栈溢出时看到了几个视图模型的例子,下面的两个类例子经常出现。我不确定区别是什么。希望有人可以解释原因吗?何时使用其中一个?...谢谢。
类选项#1
private ObservableCollection<Family> families;
public ObservableCollection<Family> Families
{
get { return families ?? (families = new ObservableCollection<Families>()); }
}
类选项#2
private ObservableCollection<Family> families;
public ObservableCollection<Family> Families
{
get { return families; }
set
{
families = value;
NotifyPropertyChanged("Families");
}
}
应该合并吗?
public ObservableCollection<Family> Families
{
get { return families ?? (families = new ObservableCollection<Family>()); }
set
{
families = value;
NotifyPropertyChanged("Families");
}
}
答案 0 :(得分:3)
第一个选项:
private ObservableCollection<TabItem> families;
public ObservableCollection<TabItem> Families
{
get { return families ?? (families = new ObservableCollection<Families>()); }
}
当您需要懒惰地实例化 readonly 属性的支持字段时,使用。由于此处没有setter,因此您永远不能为该属性赋值,您只能读取该值。
此方法可让您在需要时在最后一刻创建支持字段。如果这是数据绑定的,那么懒惰的实例化将被浪费,因为视图将在创建期间查询属性值,这意味着后备字段将始终立即被实例化。
第二个选项
private ObservableCollection<Family> families;
public ObservableCollection<Family> Families
{
get { return families; }
set
{
families = value;
NotifyPropertyChanged("Families");
}
}
允许读取和写入到属性。由于属性可以为其分配新值,因此必须调用NotifyPropertyChanged
以便可以告知View已更改该值。一旦视图知道值已更改,它将通过从属性中获取新值来刷新自身。
在数据绑定属性的场景中,将两者结合起来实际上只是一个浪费的IMO。分配DataContext时,视图将数据绑定到属性,并将背景字段作为实例。
对于不是数据绑定的属性,懒惰实例化的行为取决于您。创建集合需要大量开销吗?在您提供的示例中,没有。但是如果你懒得从其他数据源创建它,那可能有点贵,那么是的。
这是你要解决的问题。
答案 1 :(得分:1)
我不确定我是否完全理解这个问题,对我而言,两种选择之间的差异是不言而喻的。但是以面值来表达问题:
选项#1有两个重要特征:
选项#2是INotifyPropertyChanged
的典型实现。这适用于WPF的属性绑定系统,但不能充分利用系统。
差异非常明显,我认为:由于第一个选项是只读的,因此不需要属性更改通知,因此当然不实现。另一方面,第二个选项需要外部初始化属性;它不仅不像第一个那样懒惰,它甚至根本不会初始化属性,而是将默认值保留为null
。
如果你想要一个具有两者特性的视图模型(延迟初始化和可写性/ INotifyPropertyChanged
实现),那么是的......你实际上可以结合两个例子中的技术。
就个人而言,我更愿意尽可能地将我的视图模型类实现为DependencyObject
子类(几乎所有时间)。这需要更多的输入,因为声明DependencyProperty
对象的冗长,但它与WPF绑定系统完全集成,可以更高效,并且只在使用{{时启用不可用的绑定功能1}}(例如价值强制)。
INotifyPropertyChanged
属性看起来不像您发布的两个示例中的任何一个。相反,它可能看起来像这样:
DependencyObject
那么,何时应该实施class ViewModel : DependencyObject
{
public static readonly DependencyProperty FamiliesProperty =
DependencyProperty.Register("Families", typeof(ObservableCollection<Families>),
typeof(ViewModel), new PropertyMetadata(new ObservableCollection<Families>()));
public ObservableCollection<Family> Families
{
get { return (ObservableCollection<Family>)GetValue(FamiliesProperty); }
set { SetValue(FamiliesProperty, value); }
}
}
,何时应该继承INotifyPropertyChanged
。坦率地说,我发现这可以归结为个人偏好,就像其他任何东西一样。实际的权衡是相关的,这是不寻常的。但需要权衡利弊。
最全面的讨论之一可以在这里找到:
INotifyPropertyChanged vs. DependencyProperty in ViewModel
为了充分理解,请务必阅读整个讨论,即所有答案,而不仅仅是已接受的答案。
简要总结一些亮点:
DependencyObject
和DependencyObject
的优点包括:DependencyProperty
对象属性作为源进行绑定,但绑定的目标属性必须是依赖属性。对于简单的场景,这不是一个问题,但有时您可能希望链接属性绑定和/或使用源绑定在XAML中声明视图模型(即使只是为了初始化它),也只是依赖属性将允许这一点。INotifyPropertyChanged
时相比,属性值的更新将更快地发生。很多时候,这不重要。计算机速度快,用户速度慢,并且与绑定相比,API的其他区域有很多开销。但在某些情况下,它可能很重要。INotifyPropertyChanged
的优点包括:INotifyPropertyChanged
还是实现的辅助基类DependencyObject
)。但这是理论上的可能性。INotifyPropertyChanged
本身不可序列化。DependencyObject
和Equals()
。虽然在实践中,这可能是你不想在视图模型上做的事情,因为它们本质上是可变的,所以如果你依赖于相等比较,你需要非常小心不要触摸视图 - 您使用其相等特征后的模型值(例如,将对象存储在哈希集或字典中)。如果您确实希望进行相等比较,您也可以为您的视图模型类实施GetHashCode()
,无论它是否为IEqualityComparer<T>
。DependencyObject
要求在拥有它的线程上访问该对象)。同样,在实践中,这并不是一个问题,因为视图模型的本质是绑定到也具有线程亲和力的其他对象。即如果你在一些非UI线程上改变你的视图模型,它将导致调用一个需要UI线程的对象并且无论如何都会失败。相反,DependencyObject
本身仍然可以在非UI线程中使用...你只是不能对它做任何会影响依赖属性的事情,或者在其中创建需要UI线程的其他对象(例如其他依赖对象)。有时,这种限制可能很重要,但在大多数情况下它不会。换句话说,所有这些都是两种技术之间真正的实际差异,但在实践中,这两种技术的假设优势都不是真的那么重要。