乐趣区

关于mvvm:使用-Source-Generators-快速编写-MVVM-代码


大家好,我是本期的微软 MVP 实验室研究员——陈锦华。
微软 MVP(Windows Development 方向),专一于 .NET 开发,有十多年的客户端开发教训。当初热衷于撰写博客,分享 WPF、UWP 和 Azure DevOps 相干的教训。

0.1. 对于 MVVM Toolkit

.NET Community Toolkit 是以用于所有 .NET 开发人员的帮忙类和 API 的合集,并且与任何特定 UI 平台无关。最它公布了 8.0.0 preview1 版本,它蕴含了从 Windows Community Toolkit 迁徙过去的以下组件:
• CommunityToolkit.Common
• CommunityToolkit.Mvvm
• CommunityToolkit.Diagnostics
• CommunityToolkit.HighPerformance
其中 CommunityToolkit.Mvvm 又名 MVVM Toolkit,它是一个现代化、疾速以及模块化的 MVVM 库。它蕴含一个 Source Generators 组件:MVVM Toolkit source generators。这篇文章将介绍它如何帮忙开发着疾速编写 MVVM 代码。

0.2. MVVM Toolkit source generators

Source Generators 是一项 C# 编译器性能,使 C# 开发人员可能在编译用户代码时进行查看,并动静生成新的 C# 源文件,以增加到用户的编译中。通过这种形式,你的代码能够在编译过程中运行并查看你的程序以生成与其余代码一起编译的其余源文件。
对 MVVM 平台的开发者来说,Source Generators 是一个期待已久的新性能,毕竟 MVVM 模式中,开发者须要写很多命令和属性的额定的模板代码。到目前为止,为了加重这些额定代码的累赘,微软和其它开发者想出过很多不同的计划,而 Source Generators 看起来会是更好的一个。MVVM Toolkit 已开始了这方面的工作,8.0 版本又失去进一步的增强,当初 MVVM Toolkit source generators 是一个增量生成器,性能将会晋升很多。
上面将简略解说如何应用 MVVM Toolkit source generators 命令和属性的代码。

0.3. 如何生成命令的代码

在 MVVM 模式中,命令的写法让人有点懊恼。这是 MVVM Toolkit 中的通常写法:

private IRelayCommand _displayCommand;

IRelayCommand DisplayCommand => _displayCommand ??= new RelayCommand(new Action(Display), () => HasName);

private void Display()
{}

首先,代码就不少。另外,_displayCommand 和 DisplayCommand、Display() 是写在一起好呢,还是按字段、属性、函数的排序别离放在代码里的不同地位呢?又或者索性用 Partial 类别离放在不同的文件?
用 source generators 就没这些懊恼了,命令的定义能够简化成这样:

[ICommand(CanExecute = nameof(HasName))]
private void Display()
{}

通过增加 ICommandAttribute,source generators 能够依据 Display() 这个函数名正确地生成 DisplayCommand 及对应的初始化代码。此外,还能够通过它的 CanExecute 属性指定将 ICommand 的 CanExecute 关联到对应的属性。

0.4. 如何生成属性的代码

属性也有和命令一样的懊恼,通常来说 MVVM 模式中的属性的写法如下:

private string name;

public string Name
{
    get => name;
    set => SetProperty(ref name, value);
}

其实还好,不会太多。但如果是这样呢:

 private string _surname;

 public string Surname
 {
     get
     {return _surname;}
     set
     {
         if (!EqualityComparer<string>.Default.Equals(_surnam
         {
             _surname = value;
             OnPropertyChanged();
             OnPropertyChanged(nameof(FullName));
             OnPropertyChanged(nameof(HasName));
             DisplayCommand.NotifyCanExecuteChanged();}
     }
 }

 public string FullName => $"{Name} {Surname}";

 public bool HasName => !string.IsNullOrWhiteSpace(FullName);

这时候 source generators 的作用就能够很显著,因为它只须要上面的代码就能够主动产生与下面等价的代码:

[ObservableProperty]
[AlsoNotifyChangeFor(nameof(FullName), nameof(HasName))]
[AlsoNotifyCanExecuteFor(nameof(DisplayCommand))]
private string _surname;

public string FullName => $"{Name} {Surname}";

public bool HasName => !string.IsNullOrWhiteSpace(FullName);

从这段代码能够看到有三个 Attribute 起了作用:
ObservableProperty:主动为 _name 属性生成对应的属性。AlsoNotifyChangeFor:属性值批改时同时触发 FullName 和 HasName 这两个属性的 PropertyChanged 事件。AlsoNotifyCanExecuteFor:属性值批改时同时告诉 DisplayCommand 执行它的 NotifyCanExecuteChanged()。

0.5. 如何注入到现有类

一般来说,MVVM Toolkit source generators 须要在 ObservableObject 的派生类中应用,例如:

public partial class TestModel: ObservableObject

但如果你的类曾经继承了其它类,MVVM Toolk source generators 也容许你应用它的性能,办法是增加上 INotifyPropertyChangedAttribute,代码如下:

[INotifyPropertyChanged]
public partial class TestModel: Behaviour

INotifyPropertyChangedAttribute 会主动生成实现 INotifyPropertyChanged 的代码,而无需更改基类。不过遗憾的是,INotifyPropertyChangedAttribute 目前只能在未实现 INotifyPropertyChanged 接口的类中应用,即上面这种代码不能编译通过:

[INotifyPropertyChanged]
public partial class TestModel: ObservableObject

0.6. 应用 source generators 后的成绩

应用了 source generators 能够大幅缩小代码,上面这图直观展现了缩小的代码量。

如果须要查看主动生成的代码,能够在分析器的 CommunityToolkit.Mvvm.SourceGenerators 节点里找到:

0.7. 一些小问题

MVVM Toolkit source generators 能够重构你的代码,但代价是什么?
首先,尽管 MVVM Toolkit source generators 反对 .NET Standard 2.0,但局部性能须要 C# 8.0 以上,所以编译时可能会看到这条谬误:

The source generator features from the MVVM Toolkit require consuming projects to set the C# language version to at least C# 8.0 

解决办法是在我的项目文件的 PropertyGroup 节点里增加这段指明 C# 的版本:
<LangVersion>9.0</LangVersion>
另外,MVVM Toolkit source generators 还须要 Visual Studio 2022 才能够应用。
还有一点,我还没找到为生成的属性增加正文的办法,这对一些难以了解的属性来说非常致命,只好用回传统办法来解决这种属性。
最初,没有 CodeLens,没法直观看到属性的援用、批改等信息,用起来不是很棘手。

0.8. 总结

总的来说,MVVM Toolkit source generators 能够帮忙客户缩小大量代码工作,而且无论从代码量、可维护性、可浏览性来看,source generators 都有微小的劣势。但在现阶段,它用起来还是有不少小问题,不能齐全代替原生写法。不过这是个很合乎 80/20 准则的工具:它能够让用户用 20% 的投入解决了 80% 的问题。所以开发者能够在新的我的项目中尝试这个工具,以进步开发效率。
.NET Community Toolkit 及 MVVM Toolkit 都是开源我的项目,所以您也能够在它的存储库中提出您的反馈和代码。
其它更多的内容,请参考 Github 或其它文档:
https://github.com/CommunityT…
https://github.com/CommunityT…
https://docs.microsoft.com/zh…

退出移动版