我们很高兴地发布 .NET 6 Preview 4。现在,我们到 .NET 6 正式发布尚有一半。现在是再次浏览.NET 6 全部内容的好时机,就像第一篇预览文章一样。许多功能处于最终形式,而其他功能也将很快发布,因为已经为发布提供了基础构建块。Preview 4 为在 11月交付最终的 .NET 6 版本奠定了坚实的基础,并提供了完整的功能和体验。如果您尚未在环境中尝试过.NET 6,它也已准备好进行实际测试。
说到最终版本,我们现在有个会议!为 .NET Conf 2021预订11月9日至11日。我们将在9日发布.NET 6,其中将进行许多深入的演讲和演示,向您介绍有关 .NET 6 的所有知识。
您可以下载适用于Linux,macOS和Windows的 .NET 6 Preview 4。
安装程序和二进制文件
容器镜像
Linux软件包
发布说明
已知的问题
GitHub问题追踪
有关 Web和数据访问方案的新增功能,请参阅ASP.NET Core和EF Core帖子以获取更多详细信息。还有一个新的 .NET MAUI 帖子,描述了新的客户端应用程序体验,以及一个 Hot reload 帖子,描述了开发人员生产力的新方法。
.NET 6 已通过 Visual Studio 16.11 和Visual Studio for Mac 8.9进行了测试。如果您想在Visual Studio尝试.NET 6,我们建议您使用这些版本。
微软BUILD大会就在这个星期。它是免费的,并且可以在网络上观看直播。现在注册还为时不晚。
您一定想要了解这些介绍,其中包括有关.NET 6的大量讨论和演示,这些演示向您展示了新功能和现在的可能。
.NET进行现代应用程序开发的未来
.NET 6深入探讨;有什么新东西,还将有哪些新东西
.NET团队 “专家提问”
我们从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开发人员的 inner-loop 性能-改善开发人员的工作效率,包括提高构建性能,热重启和热重载。
使用运行时执行信息(PGO)改善启动和吞吐量—提供一种新模型来基于可用于更快启动,更高吞吐量和更小的二进制文件的运行时信息提高性能。
满足开发人员的期望-根据反馈在整个.NET产品中进行改进,并启用具有现有功能的新方案。
以下帖子将更详细地讨论其中一些主题:
.NET 6预览版1
.NET 6预览版2
关于 PGO 的对话
在过去的帖子和会议上,我们已经讨论了很多有关.NET统一的话题,但是主题中却缺少它。平台统一已融入到我们所做的所有事情中,并且不需要自己的主题。可以将其视为超出所列主题之外的一个大型主题。它贯穿多个主题,是团队前进的基本假设。
Inner-loop 性能项目是一个很好的例子。假定.NET 6应用程序都共享相同的基础设施,例如使用相同的构建系统和库。在存在技术差异的地方,如使用不同的运行时(CoreCLR 或 Mono)或代码生成技术(AOT或JIT),我们将这些因素考虑在内并提供务实且适当的体验,并且偏向于几乎不可察觉的体验差异。EventPipe项目就是另一个类似的例子。
我们将很快开始发布在生产中受支持的“上线”版本。我们目前正为此目标。我们的开发模型围绕实现生产工作负载而进行,即使我们正在完成上述所有主题的工作。
生产信心始于 dotnet.microsoft.com 网站。从 Preview 1 开始,它已经在 .NET 6 上运行了一半的站点负载。虽然规模不大,但对于我们的团队来说,这是一个重要的站点,我们非常重视它。.NET 6一直像冠军一样为我们工作。
我们还与微软使用.NET预览版部署其生产应用程序团队合作。他们这样做是为了尽早使用新的 .NET 功能。这些团队一直在寻找减少其云托管成本的机会,事实证明,部署新的.NET版本是最有效,最省力的方法之一。这些团队为我们提供了早期反馈,可帮助我们确保新功能可用于全球生产。由于它们是我们的第一批生产用户,因此它们也极大地影响最终特性的面貌。
所有这些对现实应用程序的早期测试都建立了我们对.NET 6将为运行您的应用程序做好准备的信心。
文章的其余部分专门介绍预览版4中的新增功能。
dotnet
CLI进行热重装Hot Reload是一种新体验,使您可以在运行时对应用程序的源代码进行编辑,而无需手动暂停应用程序或遇到断点。Hot Reload通过减少重新启动正在运行的应用程序所需的次数来提高开发人员的效率。
在此版本中,热重加载可用于许多类型的应用程序,例如WPF,Windows窗体,WinUI,ASP.NET,控制台应用程序以及在CoreCLR运行时之上运行的其他框架。我们也正在努力将该技术引入在Mono之上运行的WebAssembly,iOS和Android应用程序中,但这种技术仍将继续开发(在以后的Preview中)。
若要开始测试此功能,请安装Visual Studio 2019版本16.11 Preview 1,然后使用Visual Studio调试器(F5)启动您的应用程序。应用程序运行后,您现在可以使用新选项进行代码更改,并使用新的“应用代码更改”按钮应用代码,如下所示。
也可以通过该dotnet watch
工具进行热重装。预览版4提供了很多修复可以改善这种体验。
如果您想了解有关热重载的更多信息,请阅读.NET热重载简介。
IAsyncEnumerable
的支持IAsyncEnumerable
是.NET Core 3.0和C#8中添加的一项重要功能。使用 IAsyncEnumerable
实现了对 System.Text.Json
的(反)序列化新的增强功能。
以下示例将流用作任何异步数据源的示例。数据源可以是本地计算机上的文件,也可以是数据库查询或Web服务API调用的结果。
如下面的示例所示,System.Text.Json
现在支持将IAsyncEnumerable
值序列化为JSON数组。
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
的新API 。我们为此添加了JsonSerializer.DeserializeAsyncEnumerable
方法,如下面的示例所示。
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。
在写JSON DOM功能增加了一个新的简单和高性能的编程模型。这种新的API具有吸引力,因为它避免了序列化的复杂性和仪式以及DOM的传统成本。System.Text.Json
这个新的API具有以下优点:
在无法或不希望使用POCO类型的情况下,或者在JSON模式不固定且必须进行检查的情况下,可以使用轻型替代序列化。
启用对大树子集的有效修改。例如,可以有效地导航到大JSON树的子部分,并从该子部分读取数组或反序列化POCO。LINQ也可以与此一起使用。
启用使用C#dynamic
关键字,该关键字允许使用松散类型的,更类似于脚本的模型。
我们正在寻找有关对支持dynamic
的反馈。如果dynamic
支持对您很重要,请给我们您的反馈。
更多详细信息,请访问dotnet / runtime#6098。
可写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);
.NET 6引入了这种LoggerMessageAttribute
类型。此属性是命名空间Microsoft.Extensions.Logging
的一部分,使用时,它会生成性能记录API的源代码。源代码日志支持旨在为现代.NET应用程序提供高度可用和高性能的日志记录解决方案。自动生成的源代码依赖于ILogger
接口和LoggerMessage.Define
功能。
当LoggerMessageAttribute
用于partial
日志记录方法时,将触发源代码生成器。触发后,它既可以自动生成partial
其修饰方法的实现,也可以生成带有有关正确用法提示的编译时诊断。编译时日志记录解决方案在运行时通常比现有的日志记录方法快得多。它通过消除装箱,临时分配和副本来最大程度地实现了这一目标。
与直接手动使用 LoggerMessage.Define
API相比,有很多好处:
语法更短,更简单:声明性属性的使用而不是进行模板式编码。
指导开发人员的体验:生成器会给出警告,以帮助开发人员做正确的事。
支持任意数量的日志记录参数。LoggerMessage.Define
最多支持六个。
支持动态日志级别。这单靠LoggerMessage.Define
是不可能的。
如果您想跟踪改进和已知问题,请参阅dotnet / runtime#52549。
要使用LoggerMessageAttribute
,需要使用的类和方法partial
。代码生成器在编译时触发,并生成该partial
方法的实现。
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);
}
在前面的示例中,日志记录方法为static
,并且日志级别在属性定义中指定。在静态上下文中使用属性时,必须将ILogger
实例作为参数。您也可以选择在非静态上下文中使用该属性。有关更多示例和使用场景,请访问有关编译时日志记录源生成器的文档。
已添加新的System.LINQ API,这些API已由社区提出请求并做出了贡献。
Index
和Range
参数的支持该方法现在从可枚举的末尾接受索引,如下面的示例所示。Enumerable.ElementAt
Enumerable.Range(1, 10).ElementAt(^2); // returns 9
已添加一个接受参数的重载。它简化了可枚举序列的切片:Enumerable.Take``Range
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
或利用Linq所采用的一些内部优化。
DistinctBy
/ UnionBy
/ IntersectBy
/ExceptBy
如下面的示例所示,已将新的变体添加到集合操作中,这些变体允许使用通过键选择器函数指定相等性。
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
接受三个枚举重载该方法现在支持组合三个枚举接口,你可以在下面的例子中看到。
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对此实现的贡献。
FileStream
已在.NET 6中重新编写,以在Windows上具有更高的性能和可靠性。
在重新编写项目已逐步在五个大PR:
引入FileStreamStrategy作为FileStream重写的第一步
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
Method | Runtime | Mean | Ratio | Allocated |
---|---|---|---|---|
ReadAsync | .NET 5.0 | 3.785毫秒 | 1.00 | 39 KB |
ReadAsync | .NET 6.0 | 1.762毫秒 | 0.47 | 1 KB |
WriteAsync | .NET 5.0 | 12.573毫秒 | 1.00 | 39 KB |
WriteAsync | .NET 6.0 | 3.200毫秒 | 0.25 | 1 KB |
**环境:**具有启用了BitLocker的SSD驱动器的Windows 10
结果:
现在,读取1 MB文件的速度提高了2倍,而写入速度则提高了4倍。
内存分配从39 KB减少到1 KB!这是97.5%的改善!
这些更改将为FileStream
Windows用户提供显着的改进。更多详细信息,请访问dotnet / core#6098。
迄今为止,与日期和时间相关的类型已进行了以下改进。
DateOnly
和TimeOnly
结构添加了仅日期和时间结构,具有以下特征:
每个代表的一个DateTime
的一半,要么仅是日期部分,要么仅是时间部分。
DateOnly
是日期,周年纪念日和工作日的理想选择。它与SQL Server的date
类型一致。
TimeOnly
是定期会议,闹钟和每周工作时间的理想选择。它与SQL Server的time
类型一致。
补充了现有的日期/时间类型(DateTime
,DateTimeOffset
,TimeSpan
,TimeZoneInfo
)。
在System
命名空间中,与现有的相关类型一样,在CoreLib中提供。
DateTime.UtcNow
此改进具有以下好处:
修复了2.5倍的性能回归,以获取Windows上的系统时间。
利用5分钟的Windows滑动缓存跳过第二个数据,而不是每次调用都提取数据。
此改进具有以下优点:
使用时的隐式转换(https://github.com/dotnet/runtime/pull/49412)TimeZoneInfo.FindSystemTimeZoneById
通过新的API显式转换上TimeZoneInfo
:TryConvertIanaIdToWindowsId
,TryConvertWindowsIdToIanaId
,和HasIanaId
(https://github.com/dotnet/runtime/issues/49407)
改进跨平台的支持和使用不同时区类型的系统之间的互操作。
无需使用TimeZoneConverter
开源库。该功能现已内置。
此改进具有以下好处:
从TimeZoneInfo.GetSystemTimeZones
中返回的列表中的显示名称中消除歧义。
利用ICU / CLDR全球化数据。
仅限Unix。Windows仍使用注册表数据。以后可能会更改。
UTC时区的显示名称和标准名称已硬编码为英语,并且现在使用与其余时区数据相同的语言(CurrentUICulture
在Unix上,在Windows为OS默认语言)。
由于大小限制,WASM中的时区显示名称改用非本地化的IANA ID。
TimeZoneInfo.AdjustmentRule
嵌套类将其BaseUtcOffsetDelta
内部属性公开,并获取一个新的构造函数baseUtcOffsetDelta
作为参数。(https://github.com/dotnet/runtime/issues/50256)
TimeZoneInfo.AdjustmentRule
还获得了在Unix(https://github.com/dotnet/runtime/pull/49733)上加载时区的多项修复程序(https://github.com/dotnet/runtime/pull/50131)
RyuJIT编译器进行了以下改进。
在过去几个月中,@ SingleAccretion一直在忙于以下改进。这是对.NET 6 Preview 3的补充。谢谢!
dotnet / runtime#50373 —如果树是CSE候选者,不要弃用双重否定
dotnet / runtime#50450 —处理通过帮助程序进行的强制转换并在值编号中折叠溢出操作
dotnet / runtime#50702 —删除了GS Cookies的必须初始化的要求
dotnet / runtime#50703 —不要混淆fgMorphBlocks中的fgDispBasicBlocks
为了支持动态PGO,进行了以下改进。
dotnet / runtime#51664 —更新JIT以使用从交叉基因处理的PGO数据中看到的新“ LikelyClass”记录
dotnet / runtime#50213 —更好地容忍边缘轮廓不一致
dotnet / runtime#50633 —修复了混合的PGO / nonPGO编译
dotnet / runtime#50765 —修改fgExpandRunRarelyBlocks
dotnet / runtime#51593 —修改内联刻度计算
对循环优化进行了以下改进。
dotnet / runtime#50982 —概括循环反演
dotnet / runtime#51757 —不要在循环克隆期间重新计算preds列表
对线性扫描寄存器分配(LRSA)进行了以下改进。
dotnet / runtime#51281 —改进LRSA统计信息以包括寄存器选择启发式信息
dotnet / runtime#49930 —在值编号级别折叠空检查const字符串
dotnet / runtime#50000 —对引用类型的初始化的静态只读字段进行折叠空检查
dotnet / runtime#50112 —不要在潜在的BBJ_THROW候选对象中分配字符串文字
dotnet / runtime#50644 —为VectorX启用CSE。
dotnet / runtime#50806 —如果后面没有意外阻止,请放弃尾部调用
dotnet / runtime#50832 —更新Vector
以支持nint
和修改nuint
dotnet / runtime#51409-概括围绕分支空流程的优化
EventPipe是.NET的跨平台机制,用于输出事件,性能数据和计数器。
从.NET 6开始,我们将实现从C ++移到了C。通过这一更改,Mono也将能够使用EventPipe
!这意味着CoreCLR和Mono将使用相同的事件基础结构,包括.NET Diagnostics CLI工具!此更改还使CoreCLR的尺寸有所减小:
LIB | 之后大小–之前大小 | 差异 |
---|---|---|
libcoreclr.so | 7037856 – 7049408 | -11552 |
我们还进行了一些更改,以提升高负载下的EventPipe吞吐量。在最初的几个预览中,我们进行了一系列更改,使吞吐量提高了.NET 5所能达到的2.06倍
使用 dotnet/diagnostics 中 EventPipeStress 框架收集的数据。应用程序以最快的速度写入事件,持续60秒。记录了成功和删除事件的数量。
有关更多信息,请参见dotnet / runtime#45518。
修剪警告会告诉您修剪可能会删除运行时使用的代码的地方。以前默认情况下会禁用这些警告,因为这些警告非常嘈杂,这在很大程度上是由于.NET平台未作为一流方案参与修剪。
我们已经为.NET库的大部分(运行时库,而不是ASP.NET Core或Windows Desktop框架)添加了注解,以便它们产生准确的修剪警告。因此我们认为是时候默认启用修整警告了。
您可以通过将设置
为来禁用警告true
。在早期版本中,您可以设置相同的属性为false
以查看修剪警告。
修剪警告为修剪过程带来了可预测性,并增强了开发人员的控制能力。我们将继续在以后的版本中注解更多的 .NET 库,包括 ASP.NET Core。我们希望社区也将通过注解更多代码以保护修剪安全,从而改善修剪生态系统。
更多信息:
.NET 6中的修剪警告
准备用于修剪的.NET库
.NET 6中新的默认修剪模式为link
。link
TrimMode可以通过修剪不仅未使用的程序集还有不用的成员来显著的修剪效果。
在.NET 5中,修剪默认情况下尝试查找和删除未引用的程序集。这样比较安全,但收益有限。现在默认情况下,修剪警告处于打开状态,开发人员可以对修剪的结果充满信心。
让我们来看一下通过修整.NET SDK工具之一进行的修整改进。我将使用crossgen,Ready To Run 编译器。Crossgen团队能够解决的问题仅需修剪一些警告即可进行修剪。
首先,让我们看一下如何将Crossgen发布为一个自包含的应用程序,而无需进行修整。它是80 MB(包括.NET运行时和所有库)。
然后,我们可以尝试使用(现在是旧版的).NET 5默认修剪模式copyused
。结果下降到55 MB。
新的.NET 6默认修剪模式link
将自包含文件的大小进一步减小到36MB。
我们希望新的link
修整模式能更好地满足修整的期望:节省大量成本并获得可预测的结果。
我们还为Native AOT实验实施了相同的修剪警告,这将以几乎相同的方式改善Native AOT编译体验。
对单文件应用程序发布进行了以下改进。
.NET 5中添加了用于单文件发布的分析器,以警告Assembly.Location
和其他一些在单文件包中表现不同的API。
对于.NET 6 Preview 4,我们改进了分析以允许自定义警告。如果您有在单文件发布中不起作用的API,则可以使用属性将其标记[RequiresAssemblyFiles]
,如果启用了分析器,则会出现警告。添加该属性还将使方法中与单个文件相关的所有警告均消除,因此您可以使用该警告将警告向上传播到公共API。
PublishSingleFile
设置为true
时,会为exe项目自动启用分析器,但也可以通过设置EnableSingleFileAnalysis
为true
来为任何项目启用分析器。如果要将库嵌入单个文件包中,这可能会有所帮助。
现在单文件包支持压缩,这可以通过设置属性EnableCompressionInSingleFile
到true
来启用。在运行时,根据需要将文件解压缩到内存中。在某些情况下,压缩可以节省大量空间。
让我们看一下NuGet Package Explorer一起使用的压缩和不压缩的单个文件发布。
不压缩:172 MB
压缩后:71.6 MB
压缩会大大增加应用程序的启动时间,尤其是在Unix平台上(因为它们具有无法与压缩一起使用的无副本快速启动路径)。启用压缩后,应该测试您的应用程序,以查看是否可以接受额外的启动成本。
PublishReadyToRun
现在默认使用crossgen2默认情况下,发布 ReadyToRun 时会启用 Crossgen2。它还可选地支持生成组合配置。
公开了以下设置,使您可以配置发布并准备运行代码。下面的设置被设置为其默认值。
false
true
false
.NET 6将介绍SDK工作负载的概念,可以在.NET SDK之上安装之后,以启用各种方案。预览4中提供的新工作负载是.NET MAUI和Blazor WebAssembly AOT工作负载。
对于.NET MAUI工作负载,我们仍建议对预览4使用maui-check工具,因为它包含Visual Studio中尚不可用的其他组件或作为.NET SDK工作负载。要尝试试用.NET SDK的经验(以iOS为例),请运行dotnet workload install microsoft-ios-sdk-full。安装后,您可以运行,然后dotnet new ios创建和dotnet build构建您的项目。
对于Blazor WebAssembly AOT,请按照ASP.NET博客提供的安装说明进行操作。
预览4包括适用于iOS,Android,tvOS,MacOS和MacCatalyst的.NET MAUI工作负载。
请注意,dotnet workload install
将工作负载从NuGet.org复制到您的SDK安装中,因此,如果SDK安装位置受到保护(意味着在admin / root位置),则需要以高权限/ sudo运行。
为了更容易跟踪何时有新版本的SDK和运行时,我们向.NET 6 SDK添加了新命令:dotnet sdk check
这将告诉您在每个功能范围内什么是.NET SDK和.NET Runtime的最新可用版本。
sdkcheckdotnet new
)预览4引入了模板的新搜索功能。将在NuGet.org中搜索匹配的模板。在即将到来的预览期间,用于此搜索的数据将更频繁地更新。dotnet new --search
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进行重大更改,这可能会影响托管模板引擎的任何人。这些更改将显示在Preview 4和Preview 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的新功能,有很多详细的见解。
我们希望您享受尝试 Preview 4。