我们很高兴发布 .NET 6 Preview 4。我们现在大约完成了 .NET 6 发布的一半。现在是一个很好的时机,可以再次查看.NET6的完整范围。许多功能都接近最终形式,而其他功能将很快推出,因为发布的基础构建块已经就绪。预览版 4 为在 11 月交付最终的 .NET 6 构建奠定了坚实的基础,并提供了完整的功能和体验。如果您还没有在您的环境中尝试过 .NET 6,那么您现在可以用拉。
说到最终版本,我们现在有一个日期了! 预计在11 月 9 日至 11 日的.NET Conf 2021。我们将在 9 日发布 .NET 6,其中包含许多深入的演讲和演示,告诉您有关 .NET 6 的所有信息。
您可以下载适用于 Linux、macOS 和 Windows 的 .NET 6 预览版4。
有关 Web 和数据访问方案的新增功能的更多详细信息,请参阅 ASP.NET Core和 EF Core帖子。 还有新的 .NET MAUI帖子描述了新的客户端应用程序体验,还有一个热重载帖子描述了提高开发人员生产力的新方法。
.NET 6 已经过 Visual Studio 16.11 和 Visual Studio for Mac 8.9 的测试。 如果您想在Visual Studio 中试用 .NET 6,我们建议您使用这些版本。
Build 2021
Microsoft Build大会已经举行了。你肯定会想看看这些演讲,其中将包含大量关于 .NET 6 的讨论和演示,向你展示新功能和现在可能实现的功能。
.NET 6 主题
我们于 2020 年底开始在 GitHub 上规划 .NET 6。我们在一系列广泛的主题中确定了八个主题,包括行业场景、支持和教育。这些主题代表了我们发布工作的一半到四分之三。有很多项目没有上升到主题的水平,或者意义重大但不是主题(例如支持 Apple Silicon 设备)。
以下是 .NET 6 主题,每个主题都用一句话总结描述。 它们按照在 themesof.net 中显示的相同顺序列出。
- .NET 对新开发人员和学生具有吸引力--- 在 Visual Studio产品中提供有意简化的体验,包括清晰的文档、更简单的代码模型和更少的文件和概念,以及将工件部署到测试和生产环境的直观路径。
- .NET 拥有出色的客户端应用程序开发体验--- 提供跨平台客户端应用程序基础,可无缝满足桌面、移动和 Web 开发人员的需求,并基于 Blazor 和 Xamarin 等现有应用程序类型进行构建和扩展。
- .NET 被公认为构建云原生应用程序的引人注目的框架------提供主要针对性能和可观察性的基本云原生功能,改进与云原生和容器生态系统的集成,以及一个云原生组件 (yarp),它展示了很多.NET 的价值与关键的云用例。
- 企业和 LTS ---提供更简单、更可预测的模型,以将 .NET 与关键任务应用程序结合使用,并更好地满足大型企业和政府客户的需求。
- 通过提高质量、信心和支持来发展 .NET 生态系统------建立长期的社区合作,旨在将社区开发人员提升到与 Microsoft 类似的水平,并(另一方面)提供新的功能和体验,使其成为企业开发人员更容易依赖社区开源项目中的库,而社区开源项目不一定与大公司有关联或得到大公司的支持。
- 提高 .NET 开发人员的内循环性能------提高开发人员的工作效率,包括提高构建性能、热重启和热重载。
- 使用运行时执行信息 (PGO)提高启动和吞吐量 ---提供基于运行时信息的新模型以提高性能,可用于更快的启动、更高的吞吐量和更小的二进制文件。
- 满足开发人员的期望 ---根据反馈在整个 .NET 产品中进行改进,并启用具有现有功能的新方案。
以下帖子更详细地讨论了其中一些主题:
.NET 平台统一
我们在过去的帖子和会议上讨论了很多关于 .NET 统一的内容,但主题中缺少它。平台统一已经融入我们所做的一切,不需要自己的主题。人们可以将其视为超出所列主题的一个大主题。它贯穿多个主题,是团队前进的基本假设。
内循环性能项目就是一个很好的例子。它假定 .NET 6 应用程序都共享相同的基础,例如使用相同的构建系统和库。如果存在技术差异,例如使用不同的运行时(CoreCLR 或 Mono)或代码生成技术(AOT 或 JIT),我们会考虑这些因素并提供务实和适当的体验,并倾向于没有可观察到的体验差异。EventPipe 项目是另一个类似的例子。
产品信心
我们将很快开始发布生产支持的"上线"版本。我们目前的目标是 8 月。我们的开发模型以启用生产工作负载为导向,即使我们正在完成刚刚提到的所有主题的工作。
产品信心始于 dotnet.microsoft.com 站点。从预览版 1 开始,它在 .NET 6上运行了一半的站点负载。虽然规模不大,但它是我们团队的关键任务站点,我们非常重视它。.NET 6 一直像冠军一样为我们工作。
我们还与在 .NET 预览版上部署生产应用程序的 Microsoft 团队合作。他们这样做是为了尽早利用新的 .NET 功能。这些团队一直在寻找降低云托管成本的机会,并且部署新的 .NET 版本已被证明是最有效和最省力的方法之一。这些团队为我们提供早期反馈,帮助我们确保新功能已准备好供全球生产使用。他们还显着影响最终特征形状,因为他们是我们的第一批生产用户。
所有这些与实际应用程序的早期实战测试让我们相信 .NET 6 将准备好运行您的应用程序。
文章的其余部分专门介绍预览版 4 中的新功能。
工具:使用 Visual Studio 调试器和 dotnet CLI 进行热重载
热重载是一种新体验,让您可以在应用程序运行时对其源代码进行编辑,而无需手动暂停应用程序或点击断点。热重载通过减少重新启动正在运行的应用程序所需的次数来提高开发人员的工作效率。
在此版本中,热重载适用于多种类型的应用程序,例如 WPF、Windows 窗体、WinUI、ASP.NET、控制台应用程序和其他运行在 CoreCLR 运行时之上的框架。 我们也在努力将这项技术引入在 Mono 之上运行的 WebAssembly、iOS 和 Android 应用程序,但这仍然会出现(在稍后的预览版中)。
要开始测试此功能,请安装 Visual Studio 2019 版本 16.11 预览版 1 并使用 Visual Studio 调试器 (F5) 启动您的应用。应用程序运行后,您现在可以使用新的选项来更改代码并使用新的"应用代码更改"按钮应用它们,如下图所示。
热重载也可通过 dotnet监视工具获得。 预览版 4 包含多个改进该体验的修复程序。
如果您想了解有关热重载的更多信息,可以阅读.NET热重载的介绍。
System.Text.Json 对 IAsyncEnumerable 的支持
IAsyncEnumerable
以下示例使用流作为任何异步数据源的表示。源可以是本地机器上的文件,也可以是数据库查询或 Web 服务 API 调用的结果。
流序列化
System.Text.Json 现在支持将 IAsyncEnumerable
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
static async IAsyncEnumerable PrintNumbers(int n)
{
for (int i = 0; i < n; i++) yield return i;
}
using Stream stream = Console.OpenStandardOutput();
var data = new { Data = PrintNumbers(3) };
await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}
IAsyncEnumerable 值仅支持使用异步序列化方法。尝试使用同步方法进行序列化将导致抛出 NotSupportedException。
流反序列化
流式反序列化需要一个返回 IAsyncEnumerable
using System;
using System.IO;
using System.Text;
using System.Text.Json;
var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable(stream))
{
Console.WriteLine(item);
}
此示例将按需反序列化元素,并且在使用特别大的数据流时非常有用。它只支持从根级别的JSON 数组中读取,尽管将来可以根据反馈放宽。
现有的 DeserializeAsync 方法名义上支持 IAsyncEnumerable
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
var result = await JsonSerializer.DeserializeAsync(stream);
await foreach (int item in result.Data)
{
Console.WriteLine(item);
}
public class MyPoco
{
public IAsyncEnumerable Data { get; set; }
}
在此示例中,反序列化器将在返回反序列化对象之前缓冲内存中的所有 IAsyncEnumerable 内容。这是因为反序列化器需要在返回结果之前消耗整个 JSON 值。
System.Text.Json:可写 DOM 功能
可写的 JSON DOM 功能为 System.Text.Json 添加了一个新的简单高效的编程模型。这个新的 API 很有吸引力,因为它避免了序列化的复杂性和仪式以及 DOM 的传统成本。
这个新的 API 有以下好处:
- 在不可能或不希望使用 POCO 类型的情况下,或者当 JSON 模式不固定且必须检查时,序列化的轻量级替代方案。
- 允许对大树的子集进行有效修改。例如,可以高效地导航到大型 JSON 树的子部分并从该子部分读取数组或反序列化 POCO。 LINQ 也可以与它一起使用。
- 允许使用 C# 动态关键字,这允许使用松散类型的、更像脚本的模型。
我们正在寻找有关支持动态的反馈。如果动态支持对您很重要,请给我们您的反馈。
在 dotnet/runtime #6098中提供了更多详细信息。
可写的 DOM API
可写 DOM 公开以下类型。
namespace System.Text.Json.Node
{
public abstract class JsonNode {...};
public sealed class JsonObject : JsonNode, IDictionary {...}
public sealed class JsonArray : JsonNode, IList {...};
public abstract class JsonValue : JsonNode {...};
}
示例代码
以下示例演示了新的编程模型。
// Parse a JSON object
JsonNode jNode = JsonNode.Parse("{"MyProperty":42}");
int value = (int)jNode["MyProperty"];
Debug.Assert(value == 42);
// or
value = jNode["MyProperty"].GetValue();
Debug.Assert(value == 42);
// Parse a JSON array
jNode = JsonNode.Parse("[10,11,12]");
value = (int)jNode[1];
Debug.Assert(value == 11);
// or
value = jNode[1].GetValue();
Debug.Assert(value == 11);
// Create a new JsonObject using object initializers and array params
var jObject = new JsonObject
{
["MyChildObject"] = new JsonObject
{
["MyProperty"] = "Hello",
["MyArray"] = new JsonArray(10, 11, 12)
}
};
// Obtain the JSON from the new JsonObject
string json = jObject.ToJsonString();
Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}}
// Indexers for property names and array elements are supported and can be chained
Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue() == 11);
Microsoft.Extensions.Logging 编译时源代码生成器
.NET 6 引入了 LoggerMessageAttribute 类型。此属性是 Microsoft.Extensions.Logging 命名空间的一部分,使用时,它会生成performant日志API。源代码生成日志支持旨在为现代 .NET 应用程序提供高度可用且高性能的日志解决方案。自动生成的源代码依赖于 ILogger 接口和 LoggerMessage.Define 功能。
当 LoggerMessageAttribute 用于部分日志记录方法时,将触发源生成器。当被触发时,它要么能够自动生成它正在装饰的部分方法的实现,要么产生带有正确使用提示的编译时诊断。编译时日志记录解决方案在运行时通常比现有日志记录方法快得多。它通过最大限度地消除装箱、临时分配和拷贝来实现这一点。
直接手动使用 LoggerMessage.Define API 有以下好处:
- 更短更简单的语法:声明性属性使用而不是代码样板。
- 引导开发者体验:生成器给出警告,帮助开发者做正确的事。
- 支持任意数量的日志参数。 LoggerMessage.Define 最多支持六个。
- 支持动态日志级别。单独使用 LoggerMessage.Define 是不可能的。
- 如果您想跟踪改进和已知问题,请参阅dotnet/runtime\#52549。
基本用法
要使用 LoggerMessageAttribute,消费类和方法需要是部分的。
代码生成器在编译时触发,并生成部分方法的实现。
public static partial class Log
{
[LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
}
在前面的示例中,日志记录方法是静态的,并且在属性定义中指定了日志级别。在静态上下文中使用该属性时,需要 ILogger 实例作为参数。您也可以选择在非静态上下文中使用该属性。有关更多示例和使用场景,请访问编译时日志源生成器的文档。
System.Linq 增强功能
添加了社区请求和贡献的新 System.LINQ API。
对索引和范围参数的可枚举支持
Enumerable.ElementAt 方法现在接受可枚举对象末尾的索引,如下例所示。
Enumerable.Range(1, 10).ElementAt(\^2); // returns 9
添加了接受 Range 参数的 Enumerable.Take 重载。它简化了可枚举序列的切片:
- source.Take(..3) 而不是 source.Take(3)
- source.Take(3..) 而不是 source.Skip(3)
- source.Take(2..7) 而不是 source.Take(7).Skip(2)
- source.Take(^3..) 而不是 source.TakeLast(3)
- source.Take(..^3) 而不是 source.SkipLast(3)
- source.Take(^7..^3) 而不是 source.TakeLast(7).SkipLast(3).
感谢\@dixin 对实施的贡献。
TryGetNonEnumeratedCount
TryGetNonEnumeratedCount 方法尝试在不强制枚举的情况下获取源可枚举的计数。这种方法在枚举之前预分配缓冲区很有用的情况下很有用,如下面的示例所示。
List buffer = source.TryGetNonEnumeratedCount(out int count) ? new List(capacity: count) : new List();
foreach (T item in source)
{
buffer.Add(item);
}
TryGetNonEnumeratedCount 检查实现 ICollection/ICollection
DistinctBy/UnionBy/IntersectBy/ExceptBy
新的变体已添加到 set 操作中,允许使用键选择器函数指定相等性,如下面的示例所示。
Enumerable.Range(1, 20).DistinctBy(x => x % 3); // {1, 2, 3}
var first = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
var second = new (string Name, int Age)[] { ("Claire", 30), ("Pat", 30), ("Drew", 33) };
first.UnionBy(second, person => person.Age); // { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40), ("Drew", 33) }
MaxBy/MinBy
MaxBy 和 MinBy 方法允许使用键选择器查找最大或最小元素,如下例所示。
var people = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
people.MaxBy(person => person.Age); // ("Ashley", 40)
Chunk
Chunk 可用于将可枚举的源分块为固定大小的切片,如下例所示。
IEnumerable chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }
感谢 Robert Andersson 对实施的贡献。
FirstOrDefault/LastOrDefault/SingleOrDefault 重载采用默认参数
如果源枚举为空,现有的 FirstOrDefault/LastOrDefault/SingleOrDefault 方法返回 default(T)。添加了新的重载,接受在这种情况下要返回的默认参数,如下面的示例所示。
Enumerable.Empty().SingleOrDefault(-1); // returns -1
感谢\@Foxtrek64 对实现的贡献。
接受三个枚举的 Zip 重载
Zip方法现在支持组合三个可枚举项,如下例所示。
var xs = Enumerable.Range(1, 10);
var ys = xs.Select(x => x.ToString());
var zs = xs.Select(x => x % 2 == 0);
foreach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs))
{
}
感谢Huo Yaoyuan对实施的贡献。
显着提高了 Windows 上的 FileStream 性能
FileStream 已在 .NET 6 中重新编写,以便在 Windows 上具有更高的性能和可靠性。
重写项目已经分阶段进行了五个 PR:
- 引入 FileStreamStrategy 作为 FileStream 重写的第一步
- FileStream 重写第二部分
- 文件流优化
- FileStream 重写:使用 IValueTaskSource 而不是 TaskCompletionSource
- FileStream 重写:在 AsyncWindowsFileStreamStrategy 中缓存 ValueTaskSource
最终结果是 FileStream 在 Windows 上为异步 IO 创建时永远不会阻塞。这是一个重大的改进。 您可以在基准测试中观察到这一点,我们很快就会看到。
配置
第一个 PR 使 FileStream 能够在运行时选择一个实现。这种模式最明显的好处是可以切换回旧的 .NET 5 实现,您可以使用
runtimeconfig.json 中的以下设置来实现。
{
"configProperties": {
"System.IO.UseNet5CompatFileStream": true
}
}
我们计划接下来添加一个 io_uring 策略,它利用了最近内核中同名的 Linux 功能。
性能基准
让我们使用 BenchmarkDotNet来衡量改进。
public class FileStreamPerf
{
private const int FileSize = 1_000_000; // 1 MB
private Memory _buffer = new byte[8_000]; // 8 kB
[GlobalSetup(Target = nameof(ReadAsync))]
public void SetupRead() => File.WriteAllBytes("file.txt", new byte[FileSize]);
[Benchmark]
public async ValueTask ReadAsync()
{
using FileStream fileStream = new FileStream("file.txt", FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
while (await fileStream.ReadAsync(_buffer) > 0)
{
}
}
[Benchmark]
public async ValueTask WriteAsync()
{
using FileStream fileStream = new FileStream("file.txt", FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true);
for (int i = 0; i < FileSize / _buffer.Length; i++)
{
await fileStream.WriteAsync(_buffer);
}
}
[GlobalCleanup]
public void Cleanup() => File.Delete("file.txt");
}
``ini
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.18363.1500 (1909/November2019Update/19H2)
Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=6.0.100-preview.5.21267.9
[Host] : .NET 5.0.6 (5.0.621.22011), X64 RyuJIT
Job-OIMCTV : .NET 5.0.6 (5.0.621.22011), X64 RyuJIT
Job-CHFNUY : .NET 6.0.0 (6.0.21.26311), X64 RyuJIT
``
环境:Windows 10 与 SSD 驱动器启用 BitLocker
结果:
- 现在读取 1 MB 文件的速度提高了 2 倍,而写入的速度提高了 4 倍。
- 内存分配从 39 KB 下降到 1 KB! 这是 97.5% 的改进!
这些更改将为 Windows 上的 FileStream 用户提供显着的改进。更多详细信息,请访问 dotnet/core #6098。
增强的日期、时间和时区支持
对日期和时间相关的类型进行了以下改进。
新的 DateOnly 和 TimeOnly 结构
添加了仅限日期和时间的结构,具有以下特征:
- 每个代表一个 DateTime 的一半,或者只是日期部分,或者只是时间部分。
- DateOnly 是生日、周年纪念日和工作日的理想选择。 它与 SQL Server 的日期类型一致。
- TimeOnly 是定期会议、闹钟和每周工作时间的理想选择。 它与 SQL Server 的时间类型一致。
- 补充现有的日期/时间类型(DateTime、DateTimeOffset、TimeSpan、TimeZoneInfo)。
- 在 System 命名空间中,在 CoreLib 中提供,就像现有的相关类型一样。
对 DateTime.UtcNow 的性能改进
这种改进有以下好处:
- 修复了在 Windows 上获取系统时间的 2.5 倍性能回归。
- 利用 Windows 闰秒数据的 5 分钟滑动缓存,而不是在每次调用时获取。
支持所有平台上的 Windows 和 IANA 时区
这种改进有以下好处:
- 使用 TimeZoneInfo.FindSystemTimeZoneById 时的隐式转换 (https://github.com/dotnet/runtime/pull/49412)
- 通过 TimeZoneInfo 上的新 API 进行显式转换:TryConvertIanaIdToWindowsId、TryConvertWindowsIdToIanaId
和 HasIanaId (https://github.com/dotnet/runtime/issues/49407) - 改进了使用不同时区类型的系统之间的跨平台支持和互操作。
- 删除需要使用 TimeZoneConverter OSS 库。 该功能现已内置。
改进的时区显示名称
这种改进有以下好处:
- 从 TimeZoneInfo.GetSystemTimeZones 返回的列表中的显示名称中消除歧义。
- 利用 ICU / CLDR 全球化数据。
- 仅适用于 Unix。 Windows 仍然使用注册表数据。 这可能会在以后更改。
其他
- UTC 时区的显示名称和标准名称被硬编码为英语,现在使用与其余时区数据相同的语言(Unix 上的 CurrentUICulture,Windows 上的操作系统默认语言)。
- 由于大小限制,WASM 中的时区显示名称改为使用非本地化的 IANA ID。
- TimeZoneInfo.AdjustmentRule 嵌套类将其 BaseUtcOffsetDelta 内部属性设为公开,并获得一个以 baseUtcOffsetDelta 作为参数的新构造函数。(https://github.com/dotnet/runtime/issues/50256
- TimeZoneInfo.AdjustmentRule 还获得了在 Unix 上加载时区的其他修复(https://github.com/dotnet/run...),(https://github.com/dotnet/run...)
CodeGen
对 RyuJIT 编译器进行了以下改进。
社区贡献
@SingleAccretion在过去几个月中一直忙于进行以下改进。 这是 .NET 6 Preview 3的补充。谢谢!
- dotnet/runtime #50373 --- 如果树是 CSE 候选者,不要折叠双重否定
- dotnet/runtime #50450 --- 处理通过助手完成的转换和值编号中的折叠溢出操作
- dotnet/runtime #50702 --- 删除 GS Cookie 的 must-init 要求
- dotnet/runtime #50703 --- 不要混淆 fgMorphBlocks 中的 fgDispBasicBlocks
动态 PGO
已进行以下改进以支持动态 PGO。
- dotnet/runtime #51664 --- 更新 JIT 以使用从跨代处理的 PGO 数据中看到的新"LikelyClass"记录
- dotnet/runtime #50213 --- 更好地容忍边缘轮廓不一致
- dotnet/runtime #50633 --- 修复了混合 PGO/nonPGO 编译
- dotnet/runtime #50765 --- 修改 fgExpandRunRarelyBlocks
- dotnet/runtime #51593 --- 修改内联规模计算
JIT 循环优化
对循环优化进行了以下改进。
- dotnet/runtime #50982 --- 概括循环反转
- dotnet/runtime #51757 --- 不要在循环克隆期间重新计算 preds 列表
LSRA
对线性扫描寄存器分配(LRSA)进行了以下改进。
- dotnet/runtime #51281 --- 改进 LRSA 统计信息以包括寄存器选择启发式信息
优化
- dotnet/runtime #49930 --- 在值编号级别折叠对 const 字符串的空检查
- dotnet/runtime #50000 --- 针对 ref 类型的初始化静态只读字段折叠空检查
- dotnet/runtime #50112 --- 不要在潜在的 BBJ_THROW 候选中分配字符串文字
- dotnet/runtime #50644 --- 为 VectorX.Create 启用 CSE
- dotnet/runtime #50806 --- 如果后面有意外的块,则放弃尾调用
- dotnet/runtime #50832 --- 更新 Vector
以支持 nint 和 nuint - dotnet/runtime #51409 --- 概括围绕空流优化的分支
.NET 诊断:EventPipe for Mono 和改进的 EventPipe 性能
EventPipe 是 .NET 的跨平台机制,用于输出事件、性能数据和计数器。 从 .NET 6 开始,我们已将实现从 C++ 移至 C。通过此更改,Mono 也将能够使用EventPipe! 这意味着 CoreCLR 和 Mono 将使用相同的事件基础结构,包括 .NET 诊断 CLI 工具!这一变化还伴随着 CoreCLR 的小幅缩减:
我们还进行了一些更改,以提高负载下的 EventPipe 吞吐量。在前几个预览版中,我们进行了一系列更改,使吞吐量提高了 .NET 5 所能达到的2.06 倍:
- 使用 dotnet/diagnostics 中的 EventPipeStress 框架收集的数据。 编写器应用程序在 60 秒内尽可能快地写入事件。 记录成功和丢弃事件的数量。
有关详细信息,请参阅 dotnet/runtime #45518。
IL 剪裁
默认启用警告
剪裁警告告诉您剪裁可能会删除运行时使用的代码的地方。这些警告以前在默认情况下被禁用,因为警告非常嘈杂,主要是由于.NET 平台没有作为第一类方案参与剪裁。
我们对 .NET 库(运行时库,而不是 ASP.NET Core 或 Windows 桌面框架)的大部分进行了注释,以便它们生成准确的剪裁警告。因此,我们认为是时候默认启用剪裁警告了。
您可以通过将
剪裁警告为剪裁过程带来了可预测性,并使开发人员掌握了权力。我们将继续注释更多 .NET 库,包括后续版本中的 ASP.NET Core。我们希望社区也能通过注释更多代码以确保安剪裁全来改善剪裁生态系统。
更多信息:
默认剪裁模式=链接
.NET 6 中新的默认剪裁模式是链接。 链接 TrimMode 不仅可以剪裁未使用的程序集,还可以剪裁未使用的成员,从而显着节省成本。
在 .NET 5 中,默认情况下剪裁会尝试查找和删除未引用的程序集。这更安全,但提供的好处有限。现在默认情况下剪裁警告处于启用状态,开发人员可以对剪裁结果充满信心。
作为示例,让我们通过剪裁其中一个 .NET SDK 工具来看看这种剪裁改进。
我将使用 crossgen,即准备运行的编译器。它可以只用一些剪裁警告来剪裁,crossgen 团队能够解决这些问题。
首先,让我们将 crossgen 发布为一个独立的应用程序,无需剪裁。 它是 80
MB(包括 .NET 运行时和所有库)。
然后我们可以尝试(现在是旧版).NET 5 默认剪裁模式,copyused。 结果下降到 55 MB。
新的 .NET 6 默认剪裁模式链接将独立文件大小进一步降低到 36MB。
我们希望新的链接剪裁模式能更好地满足剪裁的期望:显着节省和可预测的结果。
与 Native AOT 共享模型
我们也为 Native AOT 实验实施了相同的剪裁警告,这应该会以大致相同的方式改善 Native AOT 编译体验。
单文件发布
对单文件应用程序发布进行了以下改进。
静态分析
.NET 5 中添加了用于单文件发布的分析器,以警告 Assembly.Location
和其他一些在单文件包中表现不同的 API。
对于 .NET 6 Preview 4,我们改进了分析以允许自定义警告。如果您的 API 在单文件发布中不起作用,您现在可以使用 [RequiresAssemblyFiles] 属性对其进行标记,如果启用了分析器,则会出现警告。添加该属性还将使方法中与单个文件相关的所有警告静音,因此您可以使用该警告将警告向上传播到您的公共 API。
当 PublishSingleFile 设置为 true 时,分析器会自动为 exe 项目启用,但您也可以通过将 EnableSingleFileAnalysis 设置为 true 为任何项目启用它。如果您想在单个文件包中嵌入库,这可能会有所帮助。
压缩
单文件包现在支持压缩,这可以通过将属性 EnableCompressionInSingleFile 设置为 true 来启用。在运行时,根据需要将文件解压缩到内存中。压缩可以为某些场景提供巨大的空间节省。
让我们看一下与 NuGet 包资源管理器一起使用的带压缩和不带压缩的单个文件发布。
无压缩:172 MB
压缩后:71.6 MB
压缩可以显着增加应用程序的启动时间,尤其是在 Unix 平台上(因为它们具有无法与压缩一起使用的无复制快速启动路径)。您应该在启用压缩后测试您的应用,看看额外的启动成本是否可以接受。
PublishReadyToRun 现在默认使用 crossgen2
Crossgen2 现在在发布 ReadyToRun 图像时默认启用。它还可选地支持生成合成图像。
公开以下设置,使您能够使用准备运行的代码配置发布。这些设置被设为它们的默认值。
PublishReadyToRun 现在默认使用 crossgen2
Crossgen2现在在发布 ReadyToRun 图像时默认启用。 它还可选地支持生成合成图像。
公开以下设置,使您能够使用准备运行的代码配置发布。这些设置被设为它们的默认值。
.NET 6 SDK 可选工作负载的 CLI 安装
.NET 6 将引入可以事后安装在 .NET SDK 之上的 SDK 工作负载的概念,以支持各种场景。预览版 4 中可用的新工作负载是 .NET MAUI 和 Blazor WebAssembly AOT 工作负载。
对于 .NET MAUI 工作负载,我们仍然建议使用预览版 4 的 maui-check
工具,因为它包含 Visual Studio 中尚不可用的其他组件或作为 .NET SDK 工作负载。要尝试 .NET SDK 体验(以 iOS 为例),请运行 dotnet working install microsoft-ios-sdk-full。安装后,您可以运行 dotnet new ios,然后运行 dotnet build 来创建和构建您的项目。
对于 Blazor WebAssembly AOT,请按照 ASP.NET 博客提供的安装说明进行操作。
预览版 4 包括适用于 iOS、Android、tvOS、MacOS 和 MacCatalyst 的 .NET MAUI 工作负载。
请注意,dotnet 工作负载安装会将工作负载从 NuGet.org 复制到您的 SDK 安装中,因此如果 SDK 安装位置受到保护(意味着在管理员/根位置),则需要运行提升 /sudo。
内置SDK版本检查
为了更轻松地跟踪新版本的 SDK 和运行时何时可用,我们向 .NET 6 SDK 添加了一个新命令:dotnet sdk check
这将告诉您在每个功能带内什么是 .NET SDK 和 .NET 运行时的最新可用版本。
CLI 模板 (dotnet new)
预览版 4 引入了新的模板搜索功能。 dotnet new --search 将在 NuGet.org
中搜索匹配模板。在即将进行的预览期间,用于此搜索的数据将更频繁地更新。
CLI 中安装的模板可用于 CLI 和 Visual Studio。安装新版本 SDK 时用户安装的模板丢失的早期问题已得到解决,但是需要重新安装 .NET 6 Preview 4 之前安装的模板。
模板安装的其他改进包括支持 --interactive 开关以支持私有 NuGet 源的授权凭据。
安装 CLI 模板后,您可以通过 --update-check 和 --update-apply 检查更新是否可用。这现在将更快地反映模板更新,支持您定义的 NuGet 源,并支持 --interactive 授权凭据。
在预览版 4 和即将发布的预览版中,将清理 dotnet new 命令的输出,以专注于您最需要的信息。例如, dotnet new --install
为了支持这些以及即将对 dotnet new 进行的更改,我们正在对模板引擎 API 进行重大更改,这可能会影响托管模板引擎的任何人。这些更改将出现在预览版 4 和预览版 5 中。如果您托管模板引擎,请通过https://github.com/dotnet/templating与我们联系,以便我们与您合作以避免或最大限度地减少中断。
支持
.NET 6 将于 2021 年 11 月发布,并将作为长期支持 (LTS) 版本提供三年支持。平台矩阵已显着扩展。
新增内容是:
- 安卓
- IOS
- Mac 和 Mac Catalyst,适用于 x64 和 Apple Silicon(又名"M1")
- Windows Arm64(特别是 Windows 桌面)
.NET 6 Debian 容器映像基于 Debian 11("bullseye"),目前正在测试中。
结束
此时我们已经进入 .NET 6 版本。 虽然 11 月的最终版本似乎还有很长的路要走,但我们已接近完成功能开发。现在是反馈的好时机,因为新功能的形状现在已经确定,而且我们仍处于积极的开发阶段,因此可以很容易地根据反馈采取行动。
说到 11 月,请在 11 月 9 日至 11 日期间预订一些时间观看 .NET Conf 2021。这肯定会令人兴奋和有趣。 我们将在 11月 9 日发布最终的 .NET 6 版本,以及比这一篇更长的博文。
还在寻找更多阅读?您可以查看我们的新对话系列。有很多关于 .NET 6 新功能的详细见解。有任何技术问题,请在Microsoft Q&A 上提问.
我们希望您喜欢试用预览版 4。