简化MVVM设计中ViewModel属性的实现方式
问题背景
我是MVVM新手,已成功搭建一款MVVM应用,但发现如下自动属性无法触发属性变更通知:
public string Status { get; set; } public bool IsIdle { get; set; } public ObservableCollection<SpecifiedRecord> FilesCollection { get; set; }
必须改写成带私有字段和OnPropertyChanged调用的完整属性才能生效:
private string _status; public string Status { get { return _status; } set { _status = value; OnPropertyChanged(nameof(Status)); } } private bool _isIdle; public bool IsIdle { get { return _isIdle; } set { _isIdle = value; OnPropertyChanged(nameof(IsIdle)); } } private ObservableCollection<SpecifiedRecord> filesColl = new ObservableCollection<SpecifiedRecord>(); public ObservableCollection<SpecifiedRecord> FilesCollection { get { return filesColl; } set { if (value != this.filesColl) filesColl = value; OnPropertyChanged(nameof(FilesCollection)); } }
MVVM诞生至今已有近9年,我疑惑为什么.NET不内置自动触发OnPropertyChanged的机制,每次手动编写这类代码效率极低。另外,是否可以通过继承类实现更简化的写法?
问题解答
为什么.NET不内置自动属性变更通知?
.NET框架设计时保留了自动属性的轻量性与通用性,自动属性本质是编译器生成的私有字段+极简get/set逻辑。若默认加入INotifyPropertyChanged通知,会打破这种通用性——并非所有场景都需要属性变更通知,比如普通数据模型类。此外,这类通知逻辑属于MVVM架构的特定需求,而非基础语言特性,因此.NET将这类能力交给开发者通过框架、扩展或代码生成实现。
通过继承类简化写法
可以封装一个通用的ViewModelBase基类,实现属性变更的核心逻辑,子类继承后即可大幅简化属性编写:
1. 实现ViewModel基类
using System.ComponentModel; using System.Runtime.CompilerServices; public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 通用属性设置方法,自动处理值比较与通知 protected bool SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(backingField, value)) return false; backingField = value; OnPropertyChanged(propertyName); return true; } }
2. 在ViewModel中简化属性编写
public class MainPageViewModel : ViewModelBase { private string _status; public string Status { get => _status; set => SetProperty(ref _status, value); } private bool _isIdle; public bool IsIdle { get => _isIdle; set => SetProperty(ref _isIdle, value); } private ObservableCollection<SpecifiedRecord> _filesCollection = new ObservableCollection<SpecifiedRecord>(); public ObservableCollection<SpecifiedRecord> FilesCollection { get => _filesCollection; set => SetProperty(ref _filesCollection, value); } }
借助SetProperty通用方法自动处理值对比和OnPropertyChanged调用,[CallerMemberName]特性还能自动获取当前属性名,无需手动编写nameof(Property)。
更高效的简化方案
除了自定义基类,还可以使用成熟MVVM框架进一步简化,比如CommunityToolkit.Mvvm(微软官方维护),它提供的ObservableObject基类和[ObservableProperty]特性,只需标记特性就能自动生成带变更通知的属性:
using CommunityToolkit.Mvvm.ComponentModel; public partial class MainPageViewModel : ObservableObject { [ObservableProperty] private string _status; [ObservableProperty] private bool _isIdle; [ObservableProperty] private ObservableCollection<SpecifiedRecord> _filesCollection = new ObservableCollection<SpecifiedRecord>(); }
编译器会自动生成对应的公共属性、私有字段以及变更通知逻辑,完全无需手动编写get/set代码。
内容的提问来源于stack exchange,提问作者McoreD

