最新 .NET 社区工具包, 推出MVVM 源代码生成器!

点击上方蓝字

关注我们

(本文阅读时间:10分钟)

我们很高兴地宣布正式推出新的 .NET 社区工具包,现在已经在NuGet上发布了8.0.0版本!这是一个重要版本,包括大量新功能、改进、优化、错误修复,许多反映了全新项目结构和组织的重构,这篇文章将详细描述这些内容。

87f05be5e1a45a4408347f72baee650f.png

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

d6747c7a375dcc6485ab3212c8cff9be.png

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

04f7b5e18a54c3d07f633587fe38b211.png

.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

  • CommunityToolkit/dotnet:

    https://github.com/CommunityToolkit/dotnet

545f16da101d96e6629c3fdad034a720.png

社区工具包历史一览 

5411f4745d38e13bd160f707e0bcd71f.png

您可能想知道为什么 .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 社区工具包库的这个新的主要版本中的所有新功能!

  • Windows 社区工具包

    https://github.com/CommunityToolkit/WindowsCommunityToolkit

  • 文档

    https://docs.microsoft.com/zh-cn/dotnet/communitytoolkit/?ocid=AID3052907

376982e8cba41e9781c3d84c5091c269.png

MVVM 工具包

0c56ed0d910d9d71f407bd3215520e8d.png

正如之前在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 等接口的“参考实现”。

  • 7.0 版本:

    https://blogs.windows.com/windowsdeveloper/2021/03/16/announcing-windows-community-toolkit-v7-0/

  • MvvmLight:

    https://www.nuget.org/packages/MvvmLight

  • Laurent Bugnion:

    https://twitter.com/LBugnion

  • 迁移文档:

    https://docs.microsoft.com/zh-cn/dotnet/communitytoolkit/mvvm/migratingfrommvvmlight?ocid=AID3052907

2d2cbaf27c50354b4558f4c017635f36.png

MVVM 工具包源生成器

0a54eabc499608095776b2287d835790.png

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

您可以在此处找到有关新源生成器的所有文档,如果您更喜欢视频版本,James Montemagno 还制作了几个关于它们的视频。让我们也回顾一下由源生成器提供支持的主要功能,您可以在 MVVM 工具包中找到这些功能。

  • MVVM 源代码生成器:

    https://docs.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/overview?ocid=AID3052907

  • 在 7.1.0 中发布的预览生成器:

    https://devblogs.microsoft.com/ifdef-windows/windows-community-toolkit-7-1-preview-release/?ocid=AID3052907

  • 增量生成器:

    https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md

  • 视频:

    https://www.youtube.com/watch?v=AXpTeiWtbC8&t=600s

78c11ab553d8f056fec49ff0e39844d2.png

命令

bf08b940e948e228d8c895ae662abbb9.png

创建命令可能是非常重复的,在为每个方法设置一个属性的需求下,我们希望以抽象的方式向应用程序中用于调用它们的各种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 方法,还可以控制异步命令的并发级别。还有其他选项可以微调生成的命令的行为,您可以在我们的文档中了解更多信息。

  • 在我们的文档中

    https://docs.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/?ocid=AID3052907

7af483ff4907d684e0585d847f54ac72.png

可观察的属性

da2b05f2c56368493ec1c7c5a446a3b4.png

编写可观察属性可能非常冗长,尤其是当您还必须添加额外的逻辑来处理被通知的依赖属性时。现在,通过使用 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]
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}!");
}

当然,您也可以只使用这两种方法中的一种,或者不使用任何一种。

从上面的代码片段中,源生成器将生成类似于以下的代码:

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

[ObservableProperty] 属性还支持验证:如果表示属性的任何字段具有一个或多个继承自 ValidationAttribute 的属性,这些属性将自动复制到生成的属性中,因此在使用 ObservableValidator 创建可验证的属性时也完全支持这种方法。如果您还希望在设置其值时验证该属性,您还可以添加 [NotifyDataErrorInfo] 以在属性设置器中生成验证代码。

[ObservableProperty]还有更多可用的功能,就像命令一样,您可以关于它们的信息并在我们的文档中查看更多示例。

  • 在我们的文档中

    https://docs.microsoft.com/zh-cn/dotnet/communitytoolkit/mvvm/generators/overview?ocid=AID3052907

96e740b1ee2b841a4acff8d0041f83e1.png

取消对命令的支持

c147582d90938d57a324e7df7cbae3ed.png

[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,并具有自动并发控制(命令是 当它已经运行时命令是默认禁用的)。每当主命令开始或结束运行时,将通知单独的取消命令,并在执行时向传递给主命令包装的方法的令牌发出取消信号。所有这些,完全抽象出来,只需一个属性即可轻松访问。

aa232439b3647c97279211434bd4e797.png

对生成属性的 Broadcast 的更改支持

f92a9346ac5357607c0480dc19c84810.png

我们还添加了一个新的 [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();}}
}

这是另一个增强生成的属性并确保它们可以在几乎所有场景中使用而不会被迫回退到手动属性的功能。

7de513c0bce53aed5c9826ca118331e1.png

ViewModel 组成

33e1141835c442d7cd89a19edd2ee808.png

C# 没有多重继承,这有时会成为障碍。

如果有一个必须从特定类型继承的视图模型,但您还想向其中注入 INotifyPropertyChanged 支持,或者让它也从 ObservableRecipient 继承以访问其 API,该怎么办?

MVVM 工具包现在通过引入代码生成属性来解决这个问题,这些属性允许将这些类型的逻辑注入到任意类中。它们是 [INotifyPropertyChanged]、[ObservableObject] 和 [ObservableRecipient]。

将它们添加到一个类将导致 MVVM 工具包源代码生成器将该类型的所有逻辑包含到该类中,就好像该类也继承自该类型一样。例如:

[INotifyPropertyChanged]
partial class MyObservableViewModel : DatabaseItem
{
}

此 MyObservableViewModel 将像您所期望的那样继承自 DatabaseItem,但使用 [INotifyPropertyChanged] 将使其也支持 INotifyPropertyChanged,以及 ObservableObject 自身包含的所有帮助 API。

我们仍然建议在需要时从基本类型(例如 ObservableObject)继承,因为这也有助于减少二进制大小,但是在需要的时候以这种方式注入代码的能力可以帮助在无法改变视图模型的基本类型的情况下解决c#的限制,就像上面的例子一样。

444450dbca8b809ae2184aa90be92e91.png

改进的 Messenger API 

214819e1f478b98edb9b82067b562d71.png

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 库中的其他几种等效类型的比较:

方法中位数错误标准差比率比率标准差第0代第一代已分配
MVVMToolkitStrong4.025 ms0.0177 ms0.0147 ms10
MVVMToolkitWeak7.549 ms0.0815 ms0.0762 ms1.870.02
MvvmCrossStrong11.483 ms0.0226 ms0.0177 ms2.850.019687.541,824,022 B
MvvmCrossWeak13.941 ms0.1865 ms0.1744 ms3.470.049687.541,824,007 B
MVVMLight52.929 ms0.1295 ms0.1011 ms13.140.06760033,120,010 B
Stylet91.540 ms0.6362 ms0.4967 ms22.730.1735500153,152,352 B
MvvmGen141.743 ms2.7249 ms2.7983 ms35.310.71925083,328,348 B
Catel148.867 ms2.6825 ms2.5093 ms36.940.64525022,736,316 B
Prism150.077 ms0.5359 ms0.4184 ms37.260.131750025076,096,900 B
CaliburnMicro280.740 ms3.7625 ms3.1418 ms69.740.82880002000381,859,608 B
MauiMessagingCenter673.656 ms1.7619 ms1.3755 ms167.260.63800035,588,776 B

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

  • 公共 DependentHandle API:

    https://github.com/dotnet/runtime/pull/54246

bb3df97969963309daca1cbffc73d04a.png

改进的集合 API

2ff625229f07de6dfceb7ed7025ed7af.png

这个新版本的 MVVM 工具包 还将所有可观察的分组集合类型从 CommunityToolkit.Common 包移动到 CommunityToolkit.Mvvm,同时还进行了一些重大更改以改进 API 表面并使其在更多场景中有用。这些 API 在处理分组项目时特别有用(例如,显示联系人列表),它们现在还包括扩展以极大地促进常见操作,例如在组内的正确位置插入项目(使用默认比较器 或输入一个,并在需要时创建一个新组)。

这个视频,展示了来自 MVVM Toolkit 示例应用程序的简单联系人视图:

bba205045e166f75b63ac945263cf85e.png

宣布 MVVM 工具包示例应用程序

55606658698866df704b0cb27b6240a4.png

为了配合新版本,我们还在 Microsoft Store 中发布了示例应用程序!它包括 MS Docs 上可以找到的所有文档,以及许多可用 API 的交互式示例。它旨在成为 MVVM 工具包的伴侣,我们希望它能帮助人们开始使用这个库,从而更加熟悉它!

从 Microsoft Store 下载并试用!

33ada13abb4f40c0609bf201473c39cc.png

  • Microsoft Store:

    https://apps.microsoft.com/store/detail/9NKLCF1LVZ5H?hl=zh-cn&gl=CN

e7c1795182a7208ff614baf626c52204.png

改进的诊断 API

9ec233d2d5289e9d9d43da629dd4b11d.png

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);
}
  • C# 10 内插字符串处理程序

    https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/tutorials/interpolated-string-handler?ocid=AID3052907

  • 调用者参数表达式

    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/caller-argument-expression?ocid=AID3052907

2dc0802e423659e0c09b57e6ef7645d9.png

.NET 6 支持

ce963439d8b706198408f19581f4d52e.png

这个新版本的 .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 社区工具包 程序集的最优化版本!

b9d65b1df032f8dded5f9faee6520fc6.gif

在这个新版本中还有更多内容!

您可以在 GitHub 发布页面上查看完整的变更日志。

您可以在我们的 GitHub 存储库中找到所有源代码,在MS Docs 网站上找到一些手写文档,在 .NET API 浏览器网站上找到完整的 API 参考。如果您想做出贡献,请随时提出问题或联系我们,让我们了解您的体验!要关注 Twitter 上的对话,请使用 #CommunityToolkit 标签。您的所有反馈都极大地帮助了这些库的发展方向,因此请务必分享它们!

  • GitHub 存储库

    https://github.com/CommunityToolkit/dotnet

  • MS Docs 网站

    https://docs.microsoft.com/en-us/dotnet/communitytoolkit/?ocid=AID3052907

b654e86cdf40d0d9a69fa90bb8501fcc.png

谢谢你读完了本文!欢迎在评论区留言分享你的想法,并且转发到朋友圈

fbc1a28980b0bef6cb03299bba3bff06.jpeg

长按识别二维码

关注微软开发者MSDN

c5fbe89fa8f5d767ebd973b3f505e363.gif

点击「阅读原文」了解.NET社区工具包~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java并发编程:Executor、Executors、ExecutorService

Executors 在Java 5之后&#xff0c;并发编程引入了一堆新的启动、调度和管理线程的API。Executor框架便是Java 5中引入的&#xff0c;其内部使用了线程池机制&#xff0c;它在java.util.cocurrent 包下&#xff0c;通过该框架来控制线程的启动、执行和关闭&#xff0c;可以简化…

IOTCS+Ekuiper搭建物联网边缘计算平台

背景介绍IOTCS 是专为物联网平台而设计的工业智能网关。自从 2020 年 10 月以来&#xff0c;我们从需求调研&#xff0c;设计&#xff0c;定型&#xff0c;研发&#xff0c;测试经过漫长的沉淀与孵化&#xff0c;最终顺利实现工业智能网关最初的设想。我们凭借创新设计理念、快…

JMX 使用指南一 Java Management Extensions

1. 什么是 JMX JMX&#xff0c;全称 Java Management Extensions&#xff0c;是在 J2SE 5.0 版本中引入的一个功能。提供了一种在运行时动态管理资源的框架&#xff0c;主要用于企业应用程序中实现可配置或动态获取应用程序的状态。JMX 提供了一种简单、标准的监控和管理资源的…

多种方法实现自适应布局

最近切了几个手机端的网页&#xff0c;第一次切的是美团的首页&#xff0c;为了自适应不同的手机分辨率&#xff0c;需要用到自适应布局&#xff0c;切图的时候是用的第一中方法&#xff0c;用到了定位&#xff0c;后来查找了一些其他方法&#xff0c;现在就介绍几种自适应布局…

hivesql优化的深入解析

转载&#xff1a;https://www.csdn.net/article/2015-01-13/2823530 一个Hive查询生成多个Map Reduce Job&#xff0c;一个Map Reduce Job又有Map&#xff0c;Reduce&#xff0c;Spill&#xff0c;Shuffle&#xff0c;Sort等多个阶段&#xff0c;所以针对Hive查询的优化可以大致…

如何用一行 CSS 实现 10 种现代布局

现代 CSS 布局使开发人员只需按几下键就可以编写十分有意义且强大的样式规则。上面的讨论和接下来的帖文研究了 10 种强大的 CSS 布局&#xff0c;它们实现了一些非凡的工作。 01. 超级居中&#xff1a;place-items: center 对于第一个“单行”布局&#xff0c;让我们解决所有 …

在.NET 6.0中使用不同的托管模型

本章是《定制ASP NET 6.0框架系列文章》的第六篇。在本章中&#xff0c;我们将讨论如何在ASP NET 6.0中自定义托管宿主。比如&#xff0c;托管选项和不同类型的托管&#xff0c;并了解一下IIS上的托管。限于篇幅&#xff0c;本章只是一个抛砖迎玉。本章涵盖主题包括&#xff1a…

TypeScript 与 JavaScript 的区别

TypeScript 是 JavaScript 的一个超集&#xff0c;支持 ECMAScript 6 标准&#xff08;ES6 教程&#xff09;。TypeScript 由微软开发的自由和开源的编程语言。TypeScript 设计目标是开发大型应用&#xff0c;它可以编译成纯 JavaScript&#xff0c;编译出来的 JavaScript 可以…

IO 和NIO的区别

1.IO和NIO的区别 NIO就是New IO在JDK1.4中引入。 IO和NIO有相同的作用和目的&#xff0c;但实现方式不同&#xff0c;NIO主要用到的是块&#xff0c;所以NIO的效率要比IO快不少。 在Java API中提供了两套NIO&#xff0c;一套针对标准输入输出NIO&#xff0c;另一套就是网络编程…

PerfView专题 (第四篇):如何寻找 C# 中程序集泄漏

一&#xff1a;背景 前两篇我们都聊到了非托管内存泄漏&#xff0c;一个是 HeapAlloc &#xff0c;一个是 VirtualAlloc&#xff0c;除了这两种泄漏之外还存在其他渠道的内存泄漏&#xff0c;比如程序集泄漏&#xff0c;这一篇我们就来聊一聊。二&#xff1a;程序集也会泄漏&am…

站立会议第九天

1.站立会议内容 昨天我们成功的将图片插进去了&#xff0c;在这里&#xff0c;图片是使用的png格式&#xff0c;长知识了。我们今天要继续把界面再优化一下。 照片&#xff1a; 2.任务展板 3.燃尽图 转载于:https://www.cnblogs.com/bk1246788/p/6852935.html

学习nginx 下面只是简单的配置文件

2019独角兽企业重金招聘Python工程师标准>>> #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } …

实现城市治理一网统管,必须这 4 个关键技术

导读&#xff1a;要实现城市治理一网统管&#xff0c;必须具备以下四个关键技术&#xff1a;城市状态一网感知、城市数据一网共享、信息流转三屏联动、虚实映射数字孪生。 作者&#xff1a;郑宇 来源&#xff1a;大数据DT&#xff08;ID&#xff1a;hzdashuju&#xff09; 01…

实现生成订单30分钟未支付,则自动取消

目录 了解需求 方案 1&#xff1a;数据库轮询 思路 实现 优点 缺点 方案 2&#xff1a;JDK 的延迟队列 思路 实现 优点 缺点 方案 3&#xff1a;时间轮算法 思路 实现 优点 缺点 方案 4&#xff1a;redis 缓存 思路一 实现一 解决方案 思路二 实现二 优…

CA周记-.NET MAUI in GCR 月报(2022年8月)

.NET MAUI 正式版本发布已经三个月了&#xff0c;有小伙伴希望我们有一些关于 .NET MAUI 相关的本地化内容以及开源项目介绍&#xff0c;接下来从8月开始&#xff0c;我希望用月报的形式和大家分享 .NET MAUI 在中国的活动&#xff0c;学习资源&#xff0c;优秀的开源项目&…

一文读懂研发效能洞察的五大流动指标

作者 | 张乐 目录 1 数字化时代&#xff0c;软件研发本身也要数字化 2 流框架及五大流动指标 1. 流动速率 2. 流动时间 3. 流动负载 4. 流动效率 5. 流动分布 3 研发过程中的常见瓶颈及解决思路 1. 稀缺的专家或资源&#xff0c;导致流动受阻 2. 缺乏自动化或工程能…

RabbitMQ队列

RabbitMQ是什么&#xff1f; RabbitMQ是一个在AMQP基础上完整的&#xff0c;可复用的企业消息系统。他遵循Mozilla Public License开源协议。 MQ全称为Message Queue, 消息队列&#xff08;MQ&#xff09;是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息&…

《ASP.NET Core 6框架揭秘实例》演示[14]:日志的进阶用法

为了对各种日志框架进行整合&#xff0c;微软创建了一个用来提供统一的日志编程模式的日志框架。《ASP.NET Core 6框架揭秘》实例演示[13]&#xff1a;日志的基本编程模式》以实例演示的方式介绍了日志的基本编程模式&#xff0c;现在我们来补充几种“进阶”用法。[本文节选《A…

什么是云原生,云原生技术为什么这么火?

文章目录 一、开篇浅谈二、云计算是什么三、云原生是什么四、云计算的四个层次 4.1 IaaS&#xff08;基础架构即服务&#xff09;4.2 PaaS&#xff08;平台即服务&#xff09;4.3 SaaS&#xff08;软件即服务&#xff09;4.4 DaaS&#xff08;数据即服务&#xff09;五、云原生…

PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏

一&#xff1a;背景 前几篇我们聊的都是 非托管内存泄漏&#xff0c;这一篇我们再看下如何用 PerfView 来排查 托管内存泄漏 &#xff0c;其实 托管内存泄漏 比较好排查&#xff0c;尤其是用 WinDbg&#xff0c;毕竟C#是带有丰富的元数据&#xff0c;不像C下去就是二进制。二&a…