我们很高兴地宣布 .NET 社区工具包 8.1 版本正式发布!这个新版本包括人们强烈要求的新功能、错误修复以及对 MVVM Toolkit 源生成器的大规模性能改进,使开发人员在使用它们时的用户体验比以往任何时候都更好!
就像我们之前的版本一样,我们非常感谢使用该工具包的 Microsoft 团队以及社区中的其他开发人员收到的所有反馈,这些反馈已经并将继续对我们的设计和优先级产生巨大影响所有新功能和改进。我们非常感谢所有做出贡献并不断帮助 .NET 社区工具包变得更好的人!
.NET 社区工具包中有什么?
就像我们在之前的公告帖子中所做的那样,让我们首先简要回顾一下 .NET 社区工具包包含的内容。它由几个独立的库组成:
CommunityToolkit.Common
CommunityToolkit.Mvvm
(又名“微软 MVVM 工具包”)CommunityToolkit.Diagnostics
CommunityToolkit.HighPerformance
这些库还广泛用于 Windows 附带的多个收件箱应用程序,例如 Microsoft Store 和照片应用程序!
有关 .NET 社区工具包历史的更多详细信息,请参阅我们之前的 8.0.0 公告帖子的链接。
以下是 .NET 社区工具包新 8.1 版本中包含的主要更改的详细信息。
自定义属性[ObservableProperty]
正如我们在8.1.0 预览版 1 公告博客文章中提到的,我们最需要的 MVVM Toolkit 源生成器功能之一(请参阅#208、#217、#228)是支持使用[ObservableProperty]
. 有几个提议的设计来支持这一点,我们最终决定利用property:
C# 中的现有语法来让开发人员标记属性以传播到生成的属性。这给我们带来了几个优势:
- 它利用内置的 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# 语法,并且对此功能支持的属性类型没有任何限制
注意:生成的代码略有不同,并且包括此处未显示的其他性能优化。
您可以在此处找到有关新源生成器的所有文档,如果您喜欢视频版本,James Montemagno还制作了几个关于它们的视频,例如这个。
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 未反映更改的情况,并且没有明确说明为什么会出现这种情况,已经多次提出这个问题。新的分析器将在这些情况下提供帮助,它将标记支持可观察属性的字段的所有分配,显示诊断以建议引用生成的属性。不再有神秘的丢失财产通知!
第二个新分析器旨在帮助使用 MVVM Toolkit 减少应用程序中的二进制大小。正如我们在8.0.0 公告帖子中提到的,MVVM 工具包包含多个属性(例如[ObservableObject]
),允许生成器将实现INotifyPropertyChanged
和INotifyPropertyChanging
接口(也可以选择使用其他帮助程序)所需的所有代码注入到现有类中。这在类已经继承自不同类型的情况下特别有用ObservableObject
:您可以使用该属性,并且仍然可以访问相同的帮助器,而无需自己重新实现逻辑。
这仅适用于不可能继承的情况:如果不是这种情况,您应该只是继承ObservableObject
并受益于减少的二进制大小,因为编译器不必在每个中一遍又一遍地复制这些相同的帮助器类型。考虑这个例子:
[ObservableObject]
public partial class MyViewModel
{
[ObservableProperty]
private string? name;
}
这里,MyViewModel
不继承任何类型,因此它应该继承ObservableObject
而不是使用[ObservableObject]
属性,以从二进制大小的改进中受益。新的分析器将标记所有像这样的场景,建议改用继承。这将特别帮助初学者,他们可能不理解两种不同方法的细微差别,并且可能不知道如何选择。在这些情况下,分析仪现在将提供帮助。
MVVM Toolkit 源生成器优化
正如我们提到的,这个新版本还包括对 MVVM 工具包的主要性能优化,以进一步改善开发人员的用户体验,特别是在处理非常大的解决方案时。我们花了很多时间改进所有生成器的架构,并与 Roslyn 团队的工程师交谈,以确保我们尽一切可能从它们中榨取尽可能多的性能。
以下是这方面的一些改进:
- 添加了 Roslyn 4.3 的多目标(#428、#462):MVVM Toolkit 源生成器现在将使用 Roslyn 4.3 目标(如果支持),以便在主机支持的情况下它们可以选择加入一些更优化的 API。引用 MVVM Toolkit 时会自动启用此功能。
- 使用
ForAttributeWithMetadataName<T>
(#436):我们将生成器切换到新的高级 Roslyn API 来匹配属性,这极大地提高了生成器触发特定属性的性能。比如[ObservableProperty]
现在正在用这个。 - 将诊断移至诊断分析器(#433、#434):我们将几乎所有诊断移至诊断分析器,这些分析器在进程外运行且独立于源生成器。这显着减少了键入时的开销,因为所有诊断逻辑现在都在单独的进程中运行,并且不会减慢 IntelliSense 的速度。
- 停止在增量提供程序中使用符号( #435 ):我们更新了所有增量提供程序以不再传播符号。这可以减少内存使用,因为传播符号可能会导致 Roslyn 不必要地根编译对象。
- 更多性能优化(#447、#460、#469、#487、#489):我们彻底检查了所有增量模型和增量管道,以显着提高性能并减少内存分配。
IObservable<T>
信使扩展
另一个要求的功能,尤其是在应用程序中大量使用响应式 API 的开发人员,是有一种方法来桥接MVVM 工具包中的信使 API公开的功能。由于界面的新IObservable<T>扩展,现在支持这一点IMessenger
。它们可以按如下方式使用:
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 社区工具包还将 .NET 7 TFM 添加到了 HighPerformance 包中,并包含一些更改以受益于新的 C# 11 语言功能(特别是ref字段)。
以下类型现在不再处于预览状态,并且已更新以利用新的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 接口,以允许这些类型为使用类型的 GC-ref 算术提供一种易于使用的替代方案Unsafe
,这在视觉上也可以更类似于传统的指针算术。
此外,所有ref struct
尚未预览的类型都已更新为ref
在内部使用字段,以受益于更好的性能。这些包括:
Span2D<T>
ReadOnlySpan2D<T>
RefEnumerable<T>
ReadOnlyRefEnumerable<T>
SpanEnumerable<T>
ReadOnlySpanEnumerable<T>
其他变化
这个新版本中包含更多内容!
您可以在GitHub 发布页面中查看完整的变更日志。
今天就开始吧!
您可以在我们的GitHub 存储库中找到所有源代码,在MS learn上找到一些手写文档,并在 .NET API 浏览器网站中找到完整的 API 参考。如果您想做出贡献,请随时提出问题或联系我们,让我们了解您的体验!要关注 Twitter 上的对话,请使用 #CommunityToolkit 主题标签。您的所有反馈都极大地有助于塑造这些库的方向,因此请务必分享它们!
文章作者 | Sergio Pedri(塞尔吉奥·佩德里)