6

使用 Source Generators 快速编写 MVVM 代码

 2 years ago
source link: https://segmentfault.com/a/1190000041408437
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

image.png
大家好,我是本期的微软 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 可以大幅减少代码,下面这图直观展示了减少的代码量。
a07a2e9effb6121ed26c72f73990a6bb.png
如果需要查看自动生成的代码,可以在分析器的 CommunityToolkit.Mvvm.SourceGenerators 节点里找到:
b2cea11e5018bd38cabfbb5436b68ea7.png

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...


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK