我们很高兴地宣布 .NET Community Toolkit 8.1 版正式发布!这个新版本包括呼声很高的新功能、bug 修复和对 MVVM 工具包源代码生成器的大量性能改进,使开发人员在使用它们时的用户体验比以往更好!
就像在我们之前的版本中一样,我们非常感谢 Microsoft 使用该工具包的团队以及社区中的其他开发人员收到的所有反馈,这些反馈已经并将继续对我们如何设计所有新功能和改进并确定其优先级产生巨大影响。我们非常感谢每一位做出贡献并不断帮助 .NET Community Toolkit 变得更好的人!
.NET 社区工具包中有什么?
正如我们在之前的公告帖子中所做的那样,让我们首先简要回顾一下 .NET Community Toolkit 包含的内容。它由几个独立的库组成:
- CommunityToolkit.Common
- CommunityToolkit.Mvvm (又名“Microsoft MVVM Toolkit”)
- CommunityToolkit.Diagnostics
- CommunityToolkit.HighPerformance
这些库还广泛用于 Windows 附带的多个收件箱应用程序,例如 Microsoft Store 和照片应用程序!
有关 .NET Community Toolkit 历史的更多详细信息,请访问我们之前的8.0.0公告帖子的链接。
以下是 .NET Community Toolkit 新 8.1 版中包含的主要更改的部分。
自定义属性 [ObservableProperty]
正如我们在8.1.0预览版1公告博客文章中提到的,对于 MVVM 工具包源代码生成器,我们最需要的功能之一(请参阅#208、#217、#228)是支持对[ObservableProperty]使用自定义属性。有几个提议的设计来支持这一点,我们最终决定利用 C# 中的现有语法 “property”: 来让开发人员标记属性以传播到生成的属性。有几个优势:
- 它利用内置的 C# 语法,使该功能感觉“原生”并且不需要额外的属性
- 它解决了注解属性只能针对属性,不能针对字段的问题
也就是说,使用 MVVM Toolkit 8.1,现在支持以下场景:
[ObservableProperty]
[property: JsonPropertyName("responseName")]
[property: JsonRequired]
private string? _name;
这将在幕后生成以下属性:
[JsonPropertyName("responseName")]
[JsonRequired]
public string? Name
{
get => _name;
set
{
if (!EqualityComparer<string?>.Default.Equals(_name, value))
{
OnPropertyChanging("Name");
OnNameChanging(value);
_name = value;
OnPropertyChanged("Name");
OnNameChanged(value);
}
}
}
partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);
可以看到生成的属性有我们指定的两个属性!这允许完全灵活地为生成的属性添加注释,同时使用内置的 C# 语法并且对该功能支持的属性类型没有限制。
注意:生成的代码略有不同,包括此处未显示的其他性能优化。
您可以在链接找到我们关于新源代码生成器的所有文档。
MVVM 工具包分析器
此版本的 MVVM 工具包也是第一个引入专用分析器的版本。它能够帮助开发人员以最佳方式使用 MVVM 工具包。也就是说,MVVM 工具包将不再只是针对使用错误的功能发出诊断(例如,以报错的方式),它现在还会显示改进代码和避免常见错误的建议!
第一个分析器涵盖了使用[ObservableProperty]属性时的一个常见错误。参照这个例子:
[ObservableProperty]
private string? name;
[RelayCommand]
public async Task LoadUserAsync()
{
User user = await _userService.FetchUserAsync();
name = user.Name; // Whoops! Assigning to the field!
}
开发人员不小心分配给一个字段(而不是生成的属性),然后被卡在 UI 上。UI 上没有显示该更改,也没有明确说明为什么会这样,这一情景已经被多次提出。新的分析器将在这些情况下提供帮助,它将标记支持可观察属性的字段的所有赋值,并显示诊断建议引用生成的属性。你不会再收到莫名其妙的属性丢失的通知。
第二个新分析器旨在帮助减少使用 MVVM 工具包的应用程序中的二进制文件大小。正如我们在8.0.0 公告帖子中提到的,MVVM 工具包包含多个属性(例如[ObservableObject]),这些属性允许生成器将实现 INotifyPropertyChanged 和 INotifyPropertyChanging 接口所需的所有代码(也可选择使用其他帮助程序)注入现有类。
这仅适用于无法继承的情况:如果您不属于这种情况,您应该选择继承 ObservableObject,从而减小的二进制文件大小,因为编译器不必在每个过程中一遍又一遍地复制那些相同的助手类型。请参阅这个例子:
[ObservableObject]
public partial class MyViewModel
{
[ObservableProperty]
private string? name;
}
这里,MyViewModel 不是从任何类型继承而来,因此它应该继承 ObservableObject 而不是使用[ObservableObject]属性,以此改进二进制大小。新的分析器将标记所有与此类似的场景并建议改用继承。这将特别有助于初学者,他们可能不了解两种不同方法的细微差别并且可能不知道如何选择。在这些情况下,现在分析器可以提供帮助。
MVVM 工具包源代码生成器优化
正如我们提到的,这个新版本还包括对 MVVM 工具包的主要性能优化,以进一步改善开发人员的用户体验,尤其是优化了开发人员在处理非常大的解决方案时的体验。我们花了很多时间改进我们所有生成器的架构,并与 Roslyn 团队的工程师交谈,以确保我们正在尽可能地发挥它们的性能。
以下只是我们在这方面所做的一部分改进:
- 为 Roslyn 4.3 添加了多目标(#428,#462):如果支持,MVVM 工具包源代码生成器现在将使用 Roslyn 4.3 目标,这样他们就可以在主机支持的情况下选择加入一些更优化的 API。这一功能在引用 MVVM 工具包时会自动启用。
- 使用 ForAttributeWithMetadataName<T>(#436):我们将生成器切换到新的高级 Roslyn API 以匹配属性,这大大提高了生成器触发特定属性的性能。比如现在[ObservableProperty]就在用这个。
- 将诊断移入诊断分析器(#433,#434):我们将几乎所有诊断都移动到诊断分析器,这些诊断分析器在进程外运行并且独立于源生成器。这样能够显著减少键入时的overhead,因为所有诊断逻辑现在都在单独的进程中运行,并且不会降低 IntelliSense 的速度。
- 停止在增量提供程序中使用符号( #435 ):我们更新了所有增量提供程序以不再传播符号。这可以减少内存使用,因为传播符号会导致 Roslyn 不必要地根编译对象。
- 更多性能优化(#447、#460、#469、#487、#489):我们彻底检查了所有增量模型和增量管道,以显著提高性能并减少内存分配。
IObservable<T> 信使扩展
另一个功能需求,尤其是由那些在应用程序中大量使用响应式 API 开发人员提出,是寻求一种方法来桥接 MVVM 工具包中的功能。界面的新扩展 IObservable<T> extensions 现在支持此功能。它们可以按如下方式使用:
IObservable<MyMessage> observable = Messenger.CreateObservable<MyMessage>();
就像这样!此扩展将创建一个对象 IObservable<T> ,该对象可用于订阅消息并对其做出动态反应。还支持通过单独的重载指定不同的标记。这面这个示例展示了新 API 的端到端使用:
var token =
Messenger
.CreateObservable<MyMessage>()
.Where(...)
.Subscribe(m => Console.WriteLine($"Hello {m.Username}!"));
.NET 7和 C# 11支持
.NET Community Toolkit 工具包的新发布版本还将 .NET 7 TFM 添加到高性能包中,新版本中还包括一些变动,主要对新的 C# 11语言功能进行了改善,比如 ref fields 就是一个典型。
以下类型现在不再处于预览状态,以及更新使用了新的 ref 安全规则:
- Ref<T>
- ReadOnlyRef<T>
- NullableRef<T>
- ReadOnlyNullableRef<T>
下面这种场景可以使用这些规则:
public static bool TryGetElementRef(out NullableRef<T> reference)
{
// Logic here...
}
也就是说,使用 NullableRef<T> 类型规则可以有效地让方法具有 out ref T 参数, 而这在 C# 中是无法通过表达式做到的. 我们还计划扩展这些类型的 API 浅层面,从而允许这些类型通过使用 Unsafe 类型,提供一种易于使用的替代 GC-ref 算法的方法,这在视觉上也更类似于传统的指针算法。
此外,所有不在预览模式中的 ref struct 类型都已被更新,使用内部的 ref 字段实现更好的性能。这些类型包括:
- Span2D<T>
- ReadOnlySpan2D<T>
- RefEnumerable<T>
- ReadOnlyRefEnumerable<T>
- SpanEnumerable<T>
- ReadOnlySpanEnumerable<T>
其他变化
新版本中包含更多内容! 您可以在 GitHub release page 发布页面中查看完整的变更日志。
您可以在 GitHub repo 找到所有的源代码,也能在 MS Learn 上找到一些手写文档, 还能在 .NET API 浏览器网站中找到完整的 API 参考文档。如果您想为新版本的改善贡献自己的力量, 请随时提问题或联系我们,让我们知道您的体验!
如果您想了解有关 MVVM 工具包的更多信息,您还可以观看最近 .NET Conf 2022 中的视频。
新版本中有整套可用的 Toolkit-s 生态系统,其中有大量有用的 API 可用于构建 .NET 应用程序。祝您编码愉快!