.NET 6 攻略大全(四)

点击上方蓝字

关注我们

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

接上篇内容,本篇文章将介绍:DependentHandle 现已公开、RyuJIT、即用型代码/Crossgen 2、.NET 诊断:EventPipe、SDK 的相关攻略。 

d48c2c08d21a21a28284e7756c87d743.png

DependentHandle 现已公开

9ae9306d878b5a05fabc6da162197552.png

该 DependentHandle 类型现在是公共的,具有以下 API 表面:

namespace System.Runtime
{public struct DependentHandle : IDisposable{public DependentHandle(object? target, object? dependent);public bool IsAllocated { get; }public object? Target { get; set; }public object? Dependent { get; set; }public (object? Target, object? Dependent) TargetAndDependent { get; }public void Dispose();}
}

它可用于创建高级系统,例如复杂的缓存系统或 ConditionalWeakTable<TKey, TValue>类型的自定义版本。例如,它将被 MVVM Toolkit 中的 WeakReferenceMessenger 类型使用,以避免在广播消息时分配内存。

▌可移植线程池

.NET 线程池已作为托管实现重新实现,现在用作 .NET 6 中的默认线程池。我们进行此更改以使所有 .NET 应用程序都可以访问同一个线程池,而不管是否正在使用 CoreCLR、Mono 或任何其他运行时。作为此更改的一部分,我们没有观察到或预期任何功能或性能影响。

a6171a3b12d5eaa8de23e8abca981e43.png

RyuJIT

8c77f873f037eb26898b4925b0c46f34.png

该团队在此版本中对 .NET JIT 编译器进行了许多改进,在每个预览帖子中都有记录。这些更改中的大多数都提高了性能。这里介绍了一些 RyuJIT 的亮点。

▌动态 PGO

在 .NET 6 中,我们启用了两种形式的 PGO(配置文件引导优化):

  • 动态 PGO 使用从当前运行中收集的数据来优化当前运行。

  • 静态 PGO 依靠从过去运行中收集的数据来优化未来运行。

动态 PGO 已经在文章前面的性能部分中介绍过。我将提供一个重新上限。

动态 PGO 使 JIT 能够在运行时收集有关实际用于特定应用程序运行的代码路径和类型的信息。然后,JIT 可以根据这些代码路径优化代码,有时会显着提高性能。我们在测试和生产中都看到了两位数的健康改进。有一组经典的编译器技术在没有 PGO 的情况下使用 JIT 或提前编译都无法实现。我们现在能够应用这些技术。热/冷分离是一种这样的技术,而去虚拟化是另一种技术。

要启用动态 PGO,请在应用程序将运行的环境中进行设置DOTNET_TieredPGO=1。

如性能部分所述,动态 PGO 将 TechEmpower JSON“MVC”套件每秒的请求数提高了 26%(510K -> 640K)。这是一个惊人的改进,无需更改代码。

我们的目标是在未来的 .NET 版本中默认启用动态 PGO,希望在 .NET 7 中启用。我们强烈建议您在应用程序中尝试动态 PGO 并向我们提供反馈。

▌完整的 PGO

要充分利用 Dynamic PGO,您可以设置两个额外的环境变量:DOTNET_TC_QuickJitForLoops=1和DOTNET_ReadyToRun=0. 这确保了尽可能多的方法参与分层编译。我们将此变体称为Full PGO。与动态 PGO 相比,完整 PGO 可以提供更大的稳态性能优势,但启动时间会更慢(因为必须在第 0 层运行更多方法)。

您不希望将此选项用于短期运行的无服务器应用程序,但对于长期运行的应用程序可能有意义。

在未来的版本中,我们计划精简和简化这些选项,以便您可以更简单地获得完整 PGO 的好处并用于更广泛的应用程序。

▌静态 PGO

我们目前使用静态 PGO来优化 .NET 库程序集,例如 R2R(Ready To Run)附带的程序集System.Private.CoreLib。

静态 PGO 的好处是,在使用 crossgen 将程序集编译为 R2R 格式时会进行优化。这意味着有运行时的好处而没有运行时成本。这是非常重要的,也是 PGO 对 C++ 很重要的原因。

▌循环对齐

内存对齐是现代计算中各种操作的共同要求。在 .NET 5 中,我们开始在 32 字节边界对齐方法。在 .NET 6 中,我们添加了一项执行自适应循环对齐的功能,该功能在具有循环的方法中添加NOP填充指令,以便循环代码从 mod(16) 或 mod(32) 内存地址开始。这些更改改进并稳定了 .NET 代码的性能。

在下面的冒泡排序图中,数据点 1 表示我们开始在 32 字节边界对齐方法的点。数据点 2 表示我们也开始对齐内部循环的点。如您所见,基准测试的性能和稳定性都有很大提高。

c1bbe724858dba97e4f6fb383eebc6e3.png

▌硬件加速结构

结构是 CLR 类型系统的重要组成部分。近年来,它们经常被用作整个 .NET 库中的性能原语。最近的例子ValueTask是ValueTuple和Span<T>。记录结构是一个新的例子。在 .NET 5 和 .NET 6 中,我们一直在提高结构的性能,部分原因是通过确保结构是局部变量、参数或方法的返回值时可以保存在超快速 CPU 寄存器中)。这对于使用向量计算的 API 特别有用。

▌稳定性能测量

团队中有大量从未出现在博客上的工程系统工作。这对于您使用的任何硬件或软件产品都是如此。JIT 团队开展了一个项目来稳定性能测量,目标是增加我们内部性能实验室自动化自动报告的回归值。这个项目很有趣,因为需要进行深入调查和产品更改才能实现稳定性。它还展示了我们为保持和提高绩效而衡量的规模。

0db8431b1b66962c50c981d68f14363f.png

此图像演示了不稳定的性能测量,其中性能在连续运行中在慢速和快速之间波动。x 轴是测试日期,y 轴是测试时间,以纳秒为单位。到图表末尾(提交这些更改后),您可以看到测量值稳定,结果最好。这张图片展示了一个单一的测试。还有更多测试在dotnet/runtime #43227中被证明具有类似的行为。

d3960767867564a99606edd9ad0a93f3.png

即用型代码/Crossgen 2

5dfd549960ed171cf46cab921c4e6529.png

Crossgen2 是crossgen 工具的替代品。它旨在满足两个结果:

  • 让crossgen开发更高效。

  • 启用一组目前无法通过 crossgen 实现的功能。

这种转换有点类似于本机代码 csc.exe 到托管代码Roslyn 编译器。Crossgen2 是用 C# 编写的,但是它没有像 Roslyn 那样公开一个花哨的 API。

我们可能已经/已经为 .NET 6 和 7 计划了六个项目,这些项目依赖于 crossgen2。矢量指令默认提议是我们希望为 .NET 6 但更可能是 .NET 7 进行的 crossgen2 功能和产品更改的一个很好的例子。版本气泡是另一个很好的例子。

Crossgen2 支持跨操作系统和架构维度的交叉编译(因此称为“crossgen”)。这意味着您将能够使用单个构建机器为所有目标生成本机代码,至少与准备运行的代码相关。但是,运行和测试该代码是另一回事,为此您需要合适的硬件和操作系统。

第一步是用crossgen2编译平台本身。我们使用 .NET 6 完成了所有架构的任务。因此,我们能够在此版本中淘汰旧的 crossgen。请注意,crossgen2 仅适用于 CoreCLR,而不适用于基于 Mono 的应用程序(它们具有一组单独的代码生成工具)。

这个项目——至少一开始——并不以性能为导向。目标是启用更好的架构来托管 RyuJIT(或任何其他)编译器以离线方式生成代码(不需要或启动运行时)。

你可能会说“嘿……如果是用 C# 编写的,难道你不需要启动运行时来运行 crossgen2 吗?” 是的,但这不是本文中“离线”的含义。当 crossgen2 运行时,我们不使用运行 crossgen2 的运行时附带的 JIT 来生成准备运行 (R2R) 代码. 那是行不通的,至少对于我们的目标来说是行不通的。想象一下 crossgen2 在 x64 机器上运行,我们需要为 Arm64 生成代码。Crossgen2 将 Arm64 RyuJIT(针对 x64 编译)加载为原生插件,然后使用它生成 Arm64 R2R 代码。机器指令只是保存到文件中的字节流。它也可以在相反的方向工作。在 Arm64 上,crossgen2 可以使用编译为 Arm64 的 x64 RyuJIT 生成 x64 代码。我们使用相同的方法来针对 x64 机器上的 x64 代码。Crossgen2 会加载一个 RyuJIT,它是为任何需要的配置而构建的。这可能看起来很复杂,但如果您想启用无缝的交叉定位模型,它就是您需要的那种系统,而这正是我们想要的。

我们希望只在一个版本中使用术语“crossgen2”,之后它将替换现有的 crossgen,然后我们将回到使用术语“crossgen”来表示“crossgen2”。

5f29e1bb201200f448f9996b365d7991.png

.NET 诊断:EventPipe

29a49d837ee712aef360ac503a5acf25.png

EventPipe 是我们用于在进程内或进程外输出事件、性能数据和计数器的跨平台机制。从 .NET 6 开始,我们已将实现从 C++ 移至 C。通过此更改,Mono 也使用 EventPipe。这意味着 CoreCLR 和 Mono 都使用相同的事件基础设施,包括 .NET 诊断 CLI 工具。

这一变化还伴随着 CoreCLR 的小幅减小:


大小之后-大小之前
差异
libcoreclr.so7037856 – 7049408-11552

我们还进行了一些更改,以提高 EventPipe 在负载下的吞吐量。在最初的几个预览版中,我们进行了一系列更改,从而使吞吐量提高了 .NET 5 的 2.06 倍:

13e645ec3507c6581ddbaf8552c6c0aa.png

对于这个基准,越高越好。.NET 6 是橙色线,.NET 5 是蓝色线。

6af1751d806e1c9ad3933d59fd6a9957.png

SDK

96665833b9cbeec8a1b2f19e8b5aae6f.png

对 .NET SDK 进行了以下改进。

▌.NET 6 SDK 可选工作负载的 CLI 安装

.NET 6 引入了SDK 工作负载的概念。工作负载是可选组件,可以安装在 .NET SDK 之上以启用各种场景。.NET 6 中的新工作负载是:.NET MAUI 和 Blazor WebAssembly AOT 工作负载。我们可能会在 .NET 7 中创建新的工作负载(可能来自现有的 SDK)。工作负载的最大好处是减少大小和可选性。我们希望随着时间的推移使 SDK 变得更小,并且只安装您需要的组件。这个模型对开发者机器有好处,对 CI 来说甚至更好。

Visual Studio 用户并不真正需要担心工作负载。工作负载功能经过专门设计,以便像 Visual Studio 这样的安装协调器可以为您安装工作负载。可以通过 CLI 直接管理工作负载。

工作负载功能公开了用于管理工作负载的多个动词,包括以下几个:

  • dotnet workload restore— 安装给定项目所需的工作负载。

  • dotnet workload install— 安装命名工作负载。

  • dotnet workload list— 列出您已安装的工作负载。

  • dotnet workload update— 将所有已安装的工作负载更新到最新的可用版本。

update 动词查询更新 nuget.org的工作负载清单、更新本地清单、下载已安装工作负载的新版本,然后删除所有旧版本的工作负载。这类似于 apt update && apt upgrade -y(用于基于 Debian 的 Linux 发行版)。将工作负载视为 SDK 的私有包管理器是合理的。它是私有的,因为它仅适用于 SDK 组件。我们将来可能会重新考虑这一点。这些 dotnet workload 命令在给定 SDK 的上下文中运行。假设您同时安装了 .NET 6 和 .NET 7。工作负载命令将为每个 SDK 提供不同的结果,因为工作负载将不同(至少相同工作负载的不同版本)。

请注意,将 NuGet.org 中的工作负载复制到您的 SDK 安装中,因此如果 SDK 安装位置受到保护(即在管理员/根位置),dotnet workload install则需要运行提升或使用sudo。

▌内置 SDK 版本检查

为了更容易跟踪 SDK 和运行时的新版本何时可用,我们向 .NET 6 SDK 添加了一个新命令。

dotnet sdk check

它会告诉您是否有可用于您已安装的任何 .NET SDK、运行时或工作负载的更新版本。您可以在下图中看到新体验。

4207e0f1445c5606cf4d1f33db92c99a.png

dotnet new

您现在可以在 NuGet.org 中搜索带有.dotnet new --search

模板安装的其他改进包括支持切换以支持私 有NuGet 源的授权凭据。--interactive

安装 CLI 模板后,您可以通过和检查更新是否可用。--update-check--update-apply

▌NuGet 包验证

包验证工具使 NuGet 库开发人员能够验证他们的包是否一致且格式正确。

这包括:

  • 验证版本之间没有重大更改。

  • 验证包对于所有特定于运行时的实现是否具有相同的公共 API 集。

  • 确定任何目标框架或运行时适用性差距。

该工具是 SDK 的一部分。使用它的最简单方法是在项目文件中设置一个新属性。

<EnablePackageValidation> true </EnablePackageValidation>

▌更多 Roslyn 分析仪

在 .NET 5 中,我们提供了大约 250 个带有 .NET SDK 的分析器。其中许多已经存在,但作为 NuGet 包在带外发送。我们为 .NET 6 添加了更多分析器。

默认情况下,大多数新分析器都在信息级别启用。您可以通过如下配置分析模式在警告级别启用这些分析器:<AnalysisMode>All</AnalysisMode>

我们为 .NET 6 发布了我们想要的一组分析器(加上一些附加功能),然后将它们中的大多数做成了可供抓取的。社区添加了几个实现,包括这些。

贡献者问题标题
纽厄尔·克拉克dotnet/运行时 #33777使用基于跨度的string.Concat
纽厄尔·克拉克dotnet/运行时 #33784解析时优先string.AsSpan()string.Substring()
纽厄尔·克拉克dotnet/运行时 #33789覆盖Stream.ReadAsync/WriteAsync
纽厄尔·克拉克dotnet/运行时 #35343替换为Dictionary<,>.Keys.ContainsContainsKey
纽厄尔·克拉克dotnet/运行时 #45552使用代替String.EqualsString.Compare
梅克特雷尔dotnet/运行时 #47180使用代替String.Contains(char)String.Contains(String)

感谢 Meik Tranel 和 Newell Clark。

▌为 Platform Compatibility Analyzer 启用自定义防护

CA1416 平台兼容性分析器已经使用 OperatingSystem 和 RuntimeInformation 中的方法识别平台防护,例如 OperatingSystem.IsWindows 和OperatingSystem.IsWindowsVersionAtLeast。但是,分析器无法识别任何其他保护可能性,例如缓存在字段或属性中的平台检查结果,或者在辅助方法中定义了复杂的平台检查逻辑。

为了允许自定义守卫的可能性,我们添加了新属性 SupportedOSPlatformGuard并UnsupportedOSPlatformGuard 使用相应的平台名称和/或版本注释自定义守卫成员。此注释被平台兼容性分析器的流分析逻辑识别和尊重。

▌用法

[UnsupportedOSPlatformGuard("browser")] // The platform guard attribute
#if TARGET_BROWSERinternal bool IsSupported => false;
#elseinternal bool IsSupported => true;
#endif[UnsupportedOSPlatform("browser")]void ApiNotSupportedOnBrowser() { }void M1(){ApiNotSupportedOnBrowser();  // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser'if (IsSupported){ApiNotSupportedOnBrowser();  // Not warn}}[SupportedOSPlatform("Windows")][SupportedOSPlatform("Linux")]void ApiOnlyWorkOnWindowsLinux() { }[SupportedOSPlatformGuard("Linux")][SupportedOSPlatformGuard("Windows")]private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows();void M2(){ApiOnlyWorkOnWindowsLinux();  // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'.if (_isWindowOrLinux){ApiOnlyWorkOnWindowsLinux();  // Not warn}}
}

5eb68ce3ffcd62b104b9bc3a7dfc9744.png

结束

0eec7085bf59e629506817570cb14eee.png

欢迎使用 .NET 6。它是另一个巨大的 .NET 版本,在性能、功能、可用性和安全性方面都有很多的改进。我们希望您能找到许多改进,最终使您在日常开发中更有效率和能力,并提高性能或降低生产中应用程序的成本。我们已经开始从那些已经开始使用 .NET 6 的人那里听到好消息。

在 Microsoft,我们还处于 .NET 6 部署的早期阶段,一些关键应用程序已经投入生产,未来几周和几个月内还会有更多应用程序推出。

.NET 6 是我们最新的 LTS 版本。我们鼓励每个人都转向它,特别是如果您使用的是 .NET 5。我们期待它成为有史以来采用速度最快的 .NET 版本。

此版本是至少 1000 人(但可能更多)的结果。这包括来自 Microsoft 的 .NET 团队以及社区中的更多人。我试图在这篇文章中包含许多社区贡献的功能。感谢您抽出宝贵时间创建这些内容并完成我们的流程。我希望这次经历是一次美好的经历,并且更多的人会做出贡献。

感谢您成为 .NET 开发人员。

往期精彩回顾

▎.NET 6 攻略大全(一)
▎.NET 6 攻略大全(二)

c4705b7cd2d6debf235322ed169a65dd.gif

 了解更多.NET 

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

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

相关文章

[No000022]他们说:得诺贝尔奖到底有多难?

转载于:https://www.cnblogs.com/Chary/p/No000022.html

java操作redis简单学习3

2019独角兽企业重金招聘Python工程师标准>>> package com.hanchao.testredis;import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Set;import redis.clients.jedis.Jedis;import com.alibaba.fastjson.JSON; import com.al…

Xamarin效果第三篇之手机底部弹窗

前面一篇文章把基本的大框架搞定了,这不再逐个去实现里面的细节;今天主要是分享点击了CollectionView内的点点点然后从手机底部弹出一个可以交互的窗口;直接看看最终实现的效果:作为初来乍到的小萌新只能求助万能的群友让大佬们给指条光明通畅的大道,不然容易跑偏;最终给的方向…

自定义sql_一个简单易用的开源BI软件,专为SQL用户设计的开源库

poli一个易于使用的SQL报告应用程序&#xff0c;专为SQL爱好者而设计。SQL中的电源数据分析&#xff0c;可获得更快的业务洞察力。特性⚡️ 自托管和轻松设置平台独立的Web应用程序 单个JAR文件单个SQLite DB文件。在5分钟内启动并运行。连接任何支持JDBC驱动程序的数据库Postg…

echarty轴自定义显示不全_表格打印不全怎么办?这招超简单!

私信回复关键词【福利】~获取丰富办公资源&#xff0c;助你高效办公早下班&#xff01;大家好&#xff0c;我是小E~最近&#xff0c;秋叶 Excel 学习班花生 同学&#xff0c;从同事那里收到了一份开会要用的表格&#xff0c;需要马上就要打印出来。会议主题是&#xff0c;从五个…

Xcode命令行生成Bitcode静态库

近两三年一直在做静态库开发 1、在Xcode工程中创建静态库的Target&#xff0c;最初是手动Build出真机的.a文件&#xff0c;模拟器的.a文件&#xff0c;然后再用命令行合并 2、后来一些特殊的开发者&#xff0c;要求加入特殊的功能&#xff0c;或者开放特殊的接口和属性&#xf…

如何优雅地处理 EF Core 异常

前言当我们在使用 EF Core 保存数据时&#xff0c;可能会出现问题&#xff0c;比如插入重复的值&#xff0c;这时我们会收到DbUpdateException&#xff1a;如果把内部异常信息直接返回给客户&#xff0c;这样不太友好&#xff0c;同时也会暴露数据库细节。如何更优雅地处理这些…

利用 VMware vRealize - 构建和优化云管理

2019独角兽企业重金招聘Python工程师标准>>> 利用 VMware vRealize - 构建和优化云管理 VMware vCloud Suite 5.8是新一代基于软件定义数据中心架构构建与管理vSphere私有云的一体化产品&#xff0c;可支持各企业提升效率、控制性与灵活性&#xff0c;实现关键性的I…

NOIP2012模拟试题【奶牛晒衣服】

1&#xff0e;奶牛晒衣服(dry) 【问题描述】 在熊大妈英明的带领下&#xff0c;时针和它的同伴生下了许多牛宝宝。熊大妈决定给每个宝宝都穿上可爱的婴儿装。于是&#xff0c;为牛宝宝洗晒衣服就成了很不爽的事情。 圣人王担负起了这个重任。洗完衣服后&#xff0c;你就要弄干衣…

PYTHON REQUESTS的安装与简单运用

PYTHON REQUESTS的安装与简单运用 2013.07.09强烈推荐&#xff01;requests官方文档已有了中文版&#xff0c;请见http://cn.python-requests.org/zh_CN/latest/ 。 requests是python的一个HTTP客户端库&#xff0c;跟urllib&#xff0c;urllib2类似&#xff0c;那为什么要用re…

7. 堪比JMeter的.Net压测工具 - Crank 总结篇 - crank带来了什么

1. 前言通过上面的学习&#xff0c;我们已经了解清楚crank的职责以及作用&#xff0c;那么我们重新回来思考一下&#xff0c;crank能为我们带来什么&#xff1f;2. Crank带来了什么&#xff1f;为分布式压测提供了解决方案、可以模拟更高强度的压测单机压测最多提供65535&#…

里rust怎么找蓝图_Rust错误处理

错误处理是程序开发中必不可少的一个环节&#xff0c;在Rust中&#xff0c;错误分成两个类别&#xff1a;可恢复错误和不可恢复错误。 可恢复错误&#xff1a;比如说未找到文件&#xff0c;Rust中用Result<T,E>来实现 不可恢复错误&#xff1a;比如数组访问越界&#xff…

转载牛X文章

2019独角兽企业重金招聘Python工程师标准>>> Java中有关Null的9件事&#xff1a; http://www.importnew.com/14229.html 深入理解java异常处理机制&#xff1a; http://blog.csdn.net/hguisu/article/details/6155636 Android JNI知识简介&#xff1a; http://blog.…

internet访问局域网内部方法之----------路由器端口映射

很多人每天都问为什么要端口映射&#xff1f;例如:通过路由器上网的&#xff0c;网站自己可以访问&#xff0c;但是别人就不能&#xff1b;输入127.0.0.1可以访问&#xff0c;别人还是看不到&#xff1b;输入localhost可以看到&#xff0c;但是别人就是看不到&#xff0c;气人啊…

Xamarin效果第四篇之CollectionView子项右侧布局

前面基于PopupPage然后实现了手机底部弹窗;今天再次基于他从手机右侧弹出一个可以交互的窗口;直接看看最终实现的效果:右侧弹窗不是重点主角是排行榜:我还是直接使用CollectionView然后对于头像那一块还是CollectionView然后通过修改他的子项和Header模板然后旋转让子项从右侧布…

java读取csv文件

2019独角兽企业重金招聘Python工程师标准>>> CSV其实就是COMMA SEPARATED VALUE的缩写。在开发中用Java操作csv文件有专门的的API叫javacsv.jar javacsv.jar下载地址&#xff1a; http://sourceforge.net/project/showfiles.php?group_id33066 package com.syc.tes…

两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试)

本篇文章主要介绍了"两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试)"&#xff0c;主要涉及到两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试)方面的内容&#xff0c;对于两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试)感兴趣的同学可…

子窗体菜单合并到父窗体菜单的解决办法

系统中既有父窗体又有子窗体&#xff0c;子窗体添加下拉菜单&#xff0c;运行时子窗体的菜单会合并到父窗体的菜单中&#xff0c;解决办法是&#xff0c;将子窗体菜单控件的AllowMerge属性设为false。

Android笔记:Activity

1.Android可视化界面结构: 一个Android应用的可视化界面最底层是Activity,在他之上是一个Window对象,在window之上通常是布局容器,再上面才是用户直接交互的组件(按钮,文本框)交互组件(UI控件)-->布局容器(layout)--->Window对象--->Activity2.Activity生命周期 …

ASP.NET Core使用编译时依赖关系注入(DI)

前言依赖关系注入(DI)&#xff0c;是一种在类及其依赖项之间实现控制反转(IoC)的技术。在ASP.NET Core中&#xff0c;依赖关系注入是“一等公民”&#xff0c;被大量使用。通常&#xff0c;使用接口作为依赖关系实现抽象化&#xff0c;并且在服务容器中注册依赖关系&#xff0c…