乐趣区

关于microsoft:最新-NET-社区工具包-推出MVVM-源代码生成器

咱们很快乐地发表正式推出新的 .NET 社区工具包,当初曾经在 NuGet 上公布了 8.0.0 版本!这是一个重要版本,包含大量新性能、改良、优化、谬误修复和许多反映了全新我的项目构造和组织的重构,这篇博文将详细描述这些内容。

与每个社区工具包版本一样,所有的更改都受到应用该工具包的微软团队和社区其余开发人员反馈的影响。咱们非常感谢所有做出奉献并一直帮忙 .NET 社区工具包变得更好的人!

.NET 社区工具包中有什么?

.NET 社区工具包是一组实用于所有 .NET 开发人员的帮忙程序和 API,独立于任何特定的 UI 平台。该工具包由 Microsoft 保护和公布,是 .NET 基金会的一部分。它也被一些外部我的项目和收件箱应用程序应用,例如 Microsoft Store。从新的 8.0.0 版本开始,该我的项目当初位于 GitHub 上的 CommunityToolkit/dotnet 存储库中,其中包含作为 Toolkit 一部分的所有库。

所有可用的 API 都不依赖于任何特定的运行时或框架,因而所有 .NET 开发人员都能够应用它们。这些库是从 .NET Standard 2.0 到 .NET 6 的多指标库,所以它们既能够反对尽可能多的平台,又能够在与较新的运行时一起应用时进行优化以获得最佳性能。

.NET 社区工具包中的库包含:
CommunityToolkit.Common
CommunityToolkit.Mvvm(又名“微软 MVVM 工具包”)
CommunityToolkit.Diagnostics
CommunityToolkit.HighPerformance

社区工具包历史一览

您可能想晓得为什么 .NET 社区工具包的第一个版本是 8.0.0 版本。好问题!起因是 .NET 社区工具包的所有库最后都是 Windows 社区工具包的一部分,它是帮忙程序、扩大和自定义控件的汇合,和自定义控件,可简化和演示为 Windows 10 和 Windows 11 构建 UWP 和 .NET 应用程序的常见开发人员工作。
随着工夫的推移,仅针对 .NET 且没有任何 Windows 特定依赖项的 API 数量一直减少,咱们决定将它们拆分到一个独自的我的项目中,以便它们能够独立倒退,并且对于不进行任何 Windows 开发的 .NET 开发人员来说也更容易找到。.NET 社区工具包就是这样诞生的。这也使咱们更容易且更好地组织文档,当初每个特定于平台的工具包都有其独自的文档。
因为分支之前的 Windows 社区工具包的最初一个版本是 7.1.x,咱们决定遵循该语义版本号以使现有用户更容易了解转换,这就是 .NET 社区工具包的第一个版本是 8.0.0 的起因. 展望未来,它将与 Windows 社区工具包离开进行版本控制,因为每个我的项目都有本人独立的路线图和公布时间表。
搞清楚这些之后,当初让咱们深刻理解 .NET 社区工具包库的这个新的次要版本中的所有新性能!

MVVM 工具包

正如之前在 7.0 版本中发表的那样,.NET 社区工具包的次要组件之一是 MVVM 工具包:一个古代的、疾速的、平台无关和模块化的 MVVM 库。这与 Microsoft Store、照片应用程序等应用的 MVVM 库雷同!

MVVM 工具包受到 MvvmLight 的启发,并且因为该库已被弃用,MVVM 工具包也就是 MvvmLight 的官网替代品。咱们在开发 MVVM 工具包的同时也与 Laurent Bugnion 单干,他反对 MVVM 工具包作为现有 MvvmLight 用户的降级路线(咱们也有这方面的迁徙文档)。

MVVM 工具包是基于以下几个要害准则构建的:

  • 平台无关:意味着它不依赖于特定的 UI 框架。您能够应用它在 UWP、WinUI 3、MAUI、WPF、Avalonia、Uno 等之间共享代码!
  • 运行时无关:该库反对多指标并反对低至 .NET Standard 2.0 的环境,这意味着您能够在古代运行时(例如 .NET 6)上运行时取得性能改良,并且即便在 .NET 框架上依然能够应用它。
  • 易于上手和应用:对应用的应用程序构造或编码模式没有严格的要求。您能够应用该库来适应您本人的架构和格调。
  • À la carte:所有组件都是独立的,也能够独自应用。没有强制您应用“全副”的办法:如果您只想应用整个库中的一种类型,您能够做得很好,而后依据须要逐步开始应用更多功能。
  • 参考实现:所有可用的 API 都是精简和高性能的,为 .NET 基类库中蕴含的接口提供“参考实现”,但不足间接应用它们的具体类型。例如,您将可能找到 INotifyPropertyChanged 或 ICommand 等接口的“参考实现”。

MVVM 工具包源生成器

MVVM Toolkit 8.0.0 版本中最大的新个性是新的 MVVM 源代码生成器,它旨在大大减少应用 MVVM 设置应用程序所需的样板代码。与咱们在 7.1.0 中公布的预览生成器相比,它们也被齐全重写为增量生成器,这意味着它们的运行速度将比以前更快,并且即便在解决大型项目时也会有助于放弃 IDE 的疾速响应。

您能够在此处找到无关新源生成器的所有文档,如果您更喜爱视频版本,James Montemagno 还制作了几个对于它们的视频。让咱们也回顾一下由源生成器提供反对的次要性能,您能够在 MVVM 工具包中找到这些性能。

命令

创立命令可能是十分反复的,在为每个办法设置一个属性的需要下,咱们心愿以形象的形式向应用程序中用于调用它们的各种 UI 组件 (如按钮) 公开这些办法。
这就是新的 [RelayCommand] 属性发挥作用的中央:这将使 MVVM 工具包主动生成具备正确签名的命令(应用库中蕴含的 RelayCommand 类型),具体取决于带正文的办法。
作为比拟,以下是从前人们通常会如何设置命令的代码:


private IRelayCommand<User> greetUserCommand;

public IRelayCommand<User> GreetUserCommand => greetUserCommand ??= new RelayCommand<User>(GreetUser);

private void GreetUser(User user)
{Console.WriteLine($"Hello {user.Name}!");
}

当初能够简化为:


[RelayCommand]
private void GreetUser(User user)
{Console.WriteLine($"Hello {user.Name}!");
}

源生成器将负责依据带正文的办法创立正确的 GreetUserCommand 属性。此外,您能够指定 CanExecute 办法,还能够管制异步命令的并发级别。还有其余选项能够微调生成的命令的行为,您能够在咱们的文档中理解更多信息。

可察看的属性

编写可察看属性可能十分简短,尤其是当您还必须增加额定的逻辑来解决被告诉的依赖属性时。当初,通过应用 MVVM 工具包中的新属性,并让源生成器在幕后创立可察看的属性,所有这些都能够大大简化。

这些新属性是 [ObservableProperty]、[NotifyPropertyChangedFor] 和 [NotifyCanExecuteChangedFor]、[NotifyDataErrorInfo] 和 [NotifyPropertyChangedRecipients]。接下来让咱们疾速回顾一下所有这些新属性能够做什么。思考一个场景,其中有两个可察看属性,一个依赖属性和下面定义的命令,当两个可察看属性中的任何一个发生变化时,都须要告诉依赖属性和命令。也就是说,每当 FirstName 或 LastName 更改时,也会告诉 FullName 以及 GreetUserCommand。

这就是过来的做法:


private string? firstName;

public string? FirstName
{
    get => firstName;
    set
    {if (SetProperty(ref firstName, value))
        {OnPropertyChanged(nameof(FullName));
            GreetUserCommand.NotifyCanExecuteChanged();}
    }
}

private string? lastName;

public string? LastName
{
    get => lastName;
    set
    {if (SetProperty(ref lastName, value))
        {OnPropertyChanged(nameof(FullName));
            GreetUserCommand.NotifyCanExecuteChanged();}
    }
}

public string? FullName => $"{FirstName} {LastName}";

当初能够全副改写如下:


[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private string? firstName;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private string? lastName;

public string? FullName => $"{FirstName} {LastName}";

MVVM 工具包将解决这些属性的代码生成,包含插入所有逻辑以引发指定的属性更改或执行更改事件。然而等等,还有更多的个性!当应用 [ObservableProperty] 生成可察看属性时,MVVM 工具包
当初还将生成两个没有实现的局部办法:On<PROPERTY_NAME>Changing 和 On<PROPERTY_NAME>Changed。这些办法可用于在更改属性时注入额定的逻辑,而无需回退到应用手动属性。请留神,因为这两个办法是局部的、返回 void 且没有定义,如果未实现它们,C# 编译器将齐全删除它们,这意味着它们在不应用时会隐没并且不会增加到应用程序两头。
这是如何应用它们的示例:


[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private string? firstName;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private string? lastName;

public string? FullName => $"{FirstName} {LastName}";

当然,您也能够只应用这两种办法中的一种,或者不应用任何一种。从下面的代码片段中,源生成器将生成相似于以下的代码:


[ObservableProperty]
private string name;

partial void OnNameChanging(string name)
{Console.WriteLine($"The name is about to change to {name}!");
}

partial void OnNameChanged(string name)
{Console.WriteLine($"The name just changed to {name}!");
}

[ObservableProperty] 属性还反对验证:如果示意属性的任何字段具备一个或多个继承自 ValidationAttribute 的属性,这些属性将主动复制到生成的属性中,因而在应用 ObservableValidator 创立可验证的属性时也齐全反对这种办法。如果您还心愿在设置其值时验证该属性,您还能够增加 [NotifyDataErrorInfo] 以在属性设置器中生成验证代码。[ObservableProperty]还有更多可用的性能,就像命令一样,您能够浏览更多对于它们的信息并在咱们的文档中查看更多示例。

勾销对命令的反对

[RelayCommand] 属性中增加了一个新属性,可用于批示源生成器在原始命令旁边生成勾销命令。此勾销命令可用于勾销异步命令的执行。这也展现了 [RelayCommand] 如何主动适应异步办法和承受参数的办法,并在后盾创立异步命令的实现。这还启用了其余性能,例如易于设置绑定以显示进度指示器等等!

这是如何应用它们的示例:

[RelayCommand(IncludeCancelCommand = true)]private async Task DoWorkAsync(CancellationToken token){// 应用勾销反对做一些长期运行的工作}

从这个小片段中,生成器将生成以下代码:

private AsyncRelayCommand? doWorkCommand;public IAsyncRelayCommand DoWorkCommand => doWorkCommand ??= new AsyncRelayCommand(DoWorkAsync);ICommand? doWorkCancelCommand;public ICommand DoWorkCancelCommand => doWorkCancelCommand ??= IAsyncRelayCommandExtensions.CreateCancelCommand(UpdateSomethingCommand);

生成的代码与 IAsyncRelayCommandExtensions.CreateCancelCommand API 中的逻辑相结合,让您只需一行代码即可生成命令,在工作开始或运行时告诉 UI,并具备主动并发管制(命令是 当它曾经运行时命令是默认禁用的)。每当主命令开始或完结运行时,将告诉独自的勾销命令,并在执行时向传递给主命令包装的办法的令牌收回勾销信号。所有这些,齐全形象进去,只需一个属性即可轻松拜访。

对生成属性的 Broadcast 的更改反对

咱们还增加了一个新的 [NotifyPropertyChangedRecipients] 属性,该属性可用于从继承自 ObservableRecipient(或应用 [ObservableRecipient] 正文的类型)生成的可察看属性。应用它将生成对 Broadcast 办法的调用,以向所有其余订阅组件发送无关刚刚产生的属性更改的音讯。这在视图模型的属性更改还须要告诉应用程序中的其余组件的状况下很有用(假如有一个 IsLoggedIn 布尔属性,当用户登录时更新;这能够告诉并触发应用程序来刷新 Broadcast 音讯)。

它能够按如下形式应用:

[ObservableProperty][NotifyPropertyChangedRecipients]
private string name;

这将产生与此类似的代码:

public string Name{get => name;    set    {        if (!EqualityComparer<string>.Default.Equals(name, value))        {OnNameChanging(value);            
OnPropertyChanging();            
string oldValue = name;            
name = value;            
Broadcast(oldValue, value, nameof(Name));            
OnNameChanged();            
OnPropertyChanged();}
    }}

这是另一个加强生成的属性并确保它们能够在简直所有场景中应用而不会被迫回退到手动属性的性能。

ViewModel 组成

C# 没有多重继承,这有时会成为阻碍。如果有一个必须从特定类型继承的视图模型,但您还想向其中注入 INotifyPropertyChanged 反对,或者让它也从 ObservableRecipient 继承以拜访其 API,该怎么办?MVVM 工具包当初通过引入代码生成属性来解决这个问题,这些属性容许将这些类型的逻辑注入到任意类中。它们是 [INotifyPropertyChanged]、[ObservableObject] 和 [ObservableRecipient]。将它们增加到一个类将导致 MVVM 工具包源代码生成器将该类型的所有逻辑蕴含到该类中,就如同该类也继承自该类型一样。例如:[INotifyPropertyChanged]partial class MyObservableViewModel : DatabaseItem{}此 MyObservableViewModel 将像您所冀望的那样继承自 DatabaseItem,但应用 [INotifyPropertyChanged] 将使其也反对 INotifyPropertyChanged,以及 ObservableObject 本身蕴含的所有帮忙 API。

咱们依然倡议在须要时从根本类型(例如 ObservableObject)继承,因为这也有助于缩小二进制大小,然而在须要的时候以这种形式注入代码的能力能够帮忙在无奈扭转视图模型的根本类型的状况下解决 c# 的限度,就像下面的例子一样。

改良的 Messenger API

MVVM 工具包 中另一个罕用的个性是 IMessenger 接口,它是一种类型合约,可用于在不同对象之间替换音讯。这对于解耦应用程序的不同模块而不用放弃对援用类型的强援用很有用。还能够将音讯发送到特定通道,由令牌惟一标识,并在应用程序的不同局部具备不同的信使。MVVM 工具包 提供了这个接口的两种实现:
WeakReferenceMessenger:它不会固定收件人并容许收集他们。这是通过依赖句柄实现的,这是一种非凡类型的 GC 援用,它容许此信使确保始终容许收集已注册的接收者,即便已注册的处理程序将它们援用回来,但不存在对它们的其余未实现的强援用。
StrongReferenceMessenger:这是一个信使实现,它对已注册的接收者进行根权限化,以确保它们放弃沉闷状态,即便信使是惟一援用它们的对象。上面是一个如何应用这个接口的小例子:

// 申明音讯
public sealed record LoggedInUserChangedMessage(User user);
// 明确注册收件人...
messenger.Register<MyViewModel, LoggedInUserChangedMessage>(this, static (r, m) =>
{
// 在这里解决音讯,r 是接收者,m 是接收者
// 输出音讯。应用作为输出传递的接收者使得    
// ambda 表达式不捕捉“this”,从而进步性能。});

// ... 或者让视图模型实现 IRecipient<TMessage>......
class MyViewModel : IRecipient<LoggedInUserChangedMessage>
{public void Receive(LoggedInUserChangedMessage message)
    {// 在这里解决音讯}
}

// ... 而后通过接口注册(其余 API 也可用)messenger.Register<LoggedInuserChangedMessage>(this);

// 从其余模块发送音讯
messenger.Send(new LoggedInUserChangedMessage(user));

因为新提供的公共 DependentHandle API,这个新版本的 MVVM 工具包中的信使实现在 .NET 6 中失去了高度优化,它容许信使类型变得比以前更快,并提供齐全 零调配 的音讯播送。以下是一些基准,展现了 MVVM 工具包中的信使与其余宽泛应用的 MVVM 库中的其余几种等效类型的比拟:

每个基准测试运行波及向 100 个收件人发送 4 条不同的音讯 1000 次。如您所见,WeakReferenceMessenger 和 StrongReferenceMessenger 都是迄今为止最快的,也是惟一一个在播送音讯时甚至不调配一个字节的。

改良的汇合 API

这个新版本的 MVVM 工具包 还将所有可察看的分组汇合类型从 CommunityToolkit.Common 包挪动到 CommunityToolkit.Mvvm,同时还进行了一些重大更改以改良 API 外表并使其在更多场景中有用。这些 API 在解决分组我的项目时特地有用(例如,显示联系人列表),它们当初还包含扩大以极大地促成常见操作,例如在组内的正确地位插入我的项目(应用默认比拟器 或输出一个,并在须要时创立一个新组)。

这是一个 GIF,展现了来自 MVVM Toolkit 示例应用程序的简略联系人视图:

发表 MVVM 工具包示例应用程序

为了配合新版本,咱们还在 Microsoft Store 中公布了示例应用程序!它包含 MS Docs 上能够找到的所有文档,以及许多可用 API 的交互式示例。它旨在成为 MVVM 工具包的伴侣,咱们心愿它能帮忙人们开始应用这个库,从而更加相熟它!从 Microsoft Store 下载并试用!

改良的诊断 API

CommunityToolkit.Diagnostics 包也取得了一些新的改良,利用了新的 C# 10 内插字符串处理程序和调用者参数表达式性能。一些以前承受字符串的 Guard API 当初也承受自定义处理程序,容许调用站点在没有抛出异样的状况下齐全跳过插值步骤,而且也不再须要手动批示参数名称。这是一个疾速的前后比拟:


// 诊断 7.1
public static void SampleMethod(int[] array, int index, Span<int> span, string text)
{Guard.IsNotNull(array, nameof(array));
    Guard.HasSizeGreaterThanOrEqualTo(array, 10, nameof(array));
    Guard.IsInRangeFor(index, array, nameof(index));
    Guard.HasSizeLessThanOrEqualTo(array, span, nameof(span));
    Guard.IsNotNullOrEmpty(text, nameof(text));
}

// 诊断 8.0
public static void SampleMethod(int[] array, int index, Span<int> span, string text)
{Guard.IsNotNull(array);
    Guard.HasSizeGreaterThanOrEqualTo(array, 10);
    Guard.IsInRangeFor(index, array);
    Guard.HasSizeLessThanOrEqualTo(array, span);
    Guard.IsNotNullOrEmpty(text);
}

.NET 6 反对

这个新版本的 .NET 社区工具包还减少了对 .NET 6 的反对,将其作为所有可用库的新指标。在最新的 .NET 运行时上运行时,带来了一些改良:

  • 当初为所有库启用了修剪反对。为了反对这一点,所有包还为所有 API 提供了残缺的修整正文,以确保所有内容要么对链接器敌对,要么在编译时显式显示正确的正告(例如,MVVM 工具包中的某些验证 API 就是这种状况,它们应用 BCL 中的一些 API,这些 API 实质上须要一些反射能力工作)。
  • High Performance 包中的 Count<T>() 扩大当初也反对 mint 和 nunit。
  • 为 .NET 6 上的所有包引入了其余几项优化。
    当然,所有库都将持续反对 .NET Standard 2.0,因而您也能够持续从具备不同指标框架的我的项目中援用它们。因为 NuGet 包解析的工作原理,如果您应用这些包和较低指标框架(例如 .NET Standard 2.0)编写库,并且消费者从针对较新 .NET 版本的我的项目中援用它(例如.NET 6),他们依然会主动取得可用的 .NET 社区工具包 程序集的最优化版本!

在这个新版本中还有更多内容!您能够在 GitHub 发布页面上查看残缺的变更日志。

您能够在咱们的 GitHub 存储库中找到所有源代码,在 MS Docs 网站上找到一些手写文档,在 .NET API 浏览器网站上找到残缺的 API 参考。如果您想做出奉献,请随时提出问题或分割咱们,让咱们理解您的体验!要关注 Twitter 上的对话,请应用 #CommunityToolkit 标签。您的所有反馈都极大地帮忙了这些库的倒退方向,因而请务必分享它们!

长按辨认二维码
关注微软开发者 MSDN

点击理解.NET 社区工具包~

退出移动版