今天我们发布了 .NET 7 预览版 5。.NET 7的预览版包括对泛型数学的改进,使API作者的生活更轻松,一个新的文本分类API用于 ML.NET,它为自然语言处理添加了最先进的深度学习技术,对源代码生成器的各种改进和一个新的Roslyn分析器和修复器,用于RegexGenerator,并在CodeGen领域进行了多项性能改进, 可观察性,JSON序列化/反序列化和使用流。
您可以下载适用于 Windows、macOS 和 Linux 的 .NET 7 Preview 5。
.NET 7 Preview 5 已使用 Visual Studio 17.3 Preview 2 进行了测试。如果要将 .NET 7 与 Visual Studio 系列产品配合使用,我们建议您使用预览版频道版本。如果您使用的是 macOS,我们建议您使用最新的 Visual Studio 2022 for Mac preview。现在,让我们进入此版本中的一些最新更新。
可观察性
可观察性的目标是帮助您更好地了解应用程序的状态,因为规模和技术复杂性增加。
公开高性能活动事件和活动链接标记枚举器方法
https://github.com/dotnet/runtime/issues/68056
公开的方法可用于性能关键型方案,以枚举 Tag 对象,而无需任何额外分配,并具有快速项目访问权限。
var tags = new List<KeyValuePair<string, object?>>()
{
new KeyValuePair<string, object?>("tag1", "value1"),
new KeyValuePair<string, object?>("tag2", "value2"),
};
ActivityLink link = new ActivityLink(default, new ActivityTagsCollection(tags));
foreach (ref readonly KeyValuePair<string, object?> tag in link.EnumerateTagObjects())
{
// Consume the link tags without any extra allocations or value copying.
}
ActivityEvent e = new ActivityEvent("SomeEvent", tags: new ActivityTagsCollection(tags));
foreach (ref readonly KeyValuePair<string, object?> tag in e.EnumerateTagObjects())
{
// Consume the event's tags without any extra allocations or value copying.
}
System.Text.Json
多态性
https://github.com/dotnet/runtime/issues/63747
System.Text.Json 现在支持使用属性批注序列化和反序列化多态类型层次结构:
[JsonDerivedType(typeof(Derived))]
public class Base
{
public int X { get; set; }
}
public class Derived : Base
{
public int Y { get; set; }
}
此配置启用 的多态序列化,特别是当运行时类型为:Base
Derived
Base value = new Derived();
JsonSerializer.Serialize<Base>(value); // { "X" : 0, "Y" : 0 }
请注意,这不会启用多态反序列化,因为有效负载将舍入为:Base
Base value = JsonSerializer.Deserialize<Base>(@"{ ""X"" : 0, ""Y"" : 0 }");
value is Derived; // false
使用类型鉴别器
若要启用多态反序列化,用户需要为派生类指定类型鉴别器:
[JsonDerivedType(typeof(Base), typeDiscriminator: "base")]
[JsonDerivedType(typeof(Derived), typeDiscriminator: "derived")]
public class Base
{
public int X { get; set; }
}
public class Derived : Base
{
public int Y { get; set; }
}
现在,它将发出 JSON 以及类型鉴别器元数据:
Base value = new Derived();
JsonSerializer.Serialize<Base>(value); // { "$type" : "derived", "X" : 0, "Y" : 0 }
这可用于以多态方式反序列化值:
Base value = JsonSerializer.Deserialize<Base>(@"{ ""$type"" : ""derived"", ""X"" : 0, ""Y"" : 0 }");
value is Derived; // true
类型鉴别器标识符也可以是整数,因此以下形式有效:
[JsonDerivedType(typeof(Derived1), 0)]
[JsonDerivedType(typeof(Derived2), 1)]
[JsonDerivedType(typeof(Derived3), 2)]
public class Base { }
JsonSerializer.Serialize<Base>(new Derived2()); // { "$type" : 1, ... }
Utf8JsonReader.CopyString
https://github.com/dotnet/runtime/issues/54410
直到今天,Utf8JsonReader。GetString()
是用户使用解码的 JSON 字符串的唯一方式。这将始终分配一个新字符串,该字符串可能不适合某些对性能敏感的应用程序。新包含的方法允许将未转义的 UTF-8 或 UTF-16 字符串复制到用户拥有的缓冲区:CopyString
int valueLength = reader.HasReadOnlySequence ? checked((int)ValueSequence.Length) : ValueSpan.Length;
char[] buffer = ArrayPool<char>.Shared.Rent(valueLength);
int charsRead = reader.CopyString(buffer);
ReadOnlySpan<char> source = buffer.Slice(0, charsRead);
ParseUnescapedString(source); // handle the unescaped JSON string
ArrayPool<char>.Shared.Return(buffer);
或者,如果处理 UTF-8 更可取:
ReadOnlySpan<byte> source = stackalloc byte[0];
if (!reader.HasReadOnlySequence && !reader.ValueIsEscaped)
{
source = reader.ValueSpan; // No need to copy to an intermediate buffer if value is span without escape sequences
}
else
{
int valueLength = reader.HasReadOnlySequence ? checked((int)ValueSequence.Length) : ValueSpan.Length;
Span<byte> buffer = valueLength <= 256 ? stackalloc byte[256] : new byte[valueLength];
int bytesRead = reader.CopyString(buffer);
source = buffer.Slice(0, bytesRead);
}
ParseUnescapedBytes(source);
源生成改进
添加了对 (https://github.com/dotnet/runtime/issues/59268)、(https://github.com/dotnet/runtime/issues/59954) 和 /(https://github.com/dotnet/runtime/issues/53539) 类型的源生成支持。IAsyncEnumerable<T>JsonDocumentDateOnlyTimeOnly
例如:
[JsonSerializable(typeof(typeof(MyPoco))]
public class MyContext : JsonSerializerContext {}
public class MyPoco
{
// Use of IAsyncEnumerable that previously resulted
// in JsonSerializer.Serialize() throwing NotSupportedException
public IAsyncEnumerable<int> Data { get; set; }
}
// It now works and no longer throws NotSupportedException
JsonSerializer.Serialize(new MyPoco { Data = ... }, MyContext.MyPoco);
系统.IO.流
ReadExactly 和 ReadAt至少
https://github.com/dotnet/runtime/issues/16598
使用时最常见的错误之一是,返回的数据可能少于其中可用的数据,而返回的数据可能比传入的缓冲区少。即使对于意识到这一点的程序员来说,每次他们想要从中读取时都必须编写相同的循环也很烦人。Stream.Read()
Read()
Stream
Stream
为了帮助这种情况,我们向基类添加了新方法:System.IO.Stream
namespace System.IO;
public partial class Stream
{
public void ReadExactly(Span<byte> buffer);
public void ReadExactly(byte[] buffer, int offset, int count);
public ValueTask ReadExactlyAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
public ValueTask ReadExactlyAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default);
public int ReadAtLeast(Span<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true);
public ValueTask<int> ReadAtLeastAsync(Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true, CancellationToken cancellationToken = default);
}
新方法保证准确读取请求的字节数。如果流在读取请求的字节之前结束,则抛出 。ReadExactly
EndOfStreamException
using FileStream f = File.Open("readme.md");
byte[] buffer = new byte[100];
f.ReadExactly(buffer); // guaranteed to read 100 bytes from the file
新方法将至少读取请求的字节数。如果有更多的数据随时可用,它可以读取更多内容,直至缓冲区的大小。如果 Stream 在读取请求的字节之前结束(在高级情况下,当您想要的好处,但您还希望自己处理流结束方案时,您可以选择不引发异常)。ReadAtLeast
EndOfStreamException
ReadAtLest
using FileStream f = File.Open("readme.md");
byte[] buffer = new byte[100];
int bytesRead = f.ReadAtLeast(buffer, 10);
// 10 <= bytesRead <= 100
用于正则表达式生成器的新型 Roslyn 分析仪和固定器
https://github.com/dotnet/runtime/pull/69872
在 .NET 7 中的正则表达式改进中,Stephen Toub 介绍了新的正则表达式生成器,它允许您在编译时静态生成正则表达式,从而提高性能。要利用这一点,首先您必须在代码中找到可以使用它的位置,然后对每个代码进行更改。这听起来像是 Roslyn 分析仪和修复器的完美工作,因此我们在预览版 5 中添加了一个。
分析器
新的分析器包含在 .NET 7 中,它将搜索可转换为使用正则表达式生成器源生成器的用法。分析器将检测构造函数的使用情况,以及满足以下条件的静态方法的使用情况:Regex
Regex
Regex
- 提供的参数在编译时具有已知值。源生成器的输出取决于这些值,因此必须在编译时知道这些值。
- 它们是面向 .NET 7 的应用的一部分。新的分析器在 .NET 7 目标包中提供,只有面向 .NET 7 的应用才有资格使用此分析器。
- 高于 。目前,正则表达式源生成器需要设置为 。
LangVersion10LangVersionpreview
以下是Visual Studio中运行的新分析器:
代码修复程序
代码修复程序也包含在 .NET 7 中,它执行两项操作。首先,它建议使用正则表达式生成器方法,并为您提供重写默认名称的选项。然后,它将原始代码替换为对新方法的调用。
以下是Visual Studio中运行的新代码修复程序:
泛型数学
在 .NET 6 中,我们预览了一项名为 Generic Math 的功能,该功能允许 .NET 开发人员从泛型代码中利用静态 API,包括运算符。此功能将直接使 API 作者受益,他们可以简化其代码库。其他开发人员将间接受益,因为他们使用的API将开始支持更多类型,而无需每种数字类型都获得显式支持。
在 .NET 7 中,我们对实现进行了改进,并响应了社区的反馈。
系统.调用成员时的反射性能改进
https://github.com/dotnet/runtime/pull/67917
当对同一成员多次执行调用时,使用反射调用成员(无论是方法、构造函数还是属性 gettersetter)的开销已大大减少。典型增益速度提高 3-4 倍。
使用软件包:BenchmarkDotNet
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Reflection;
namespace ReflectionBenchmarks
{
internal class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<InvokeTest>();
}
}
public class InvokeTest
{
private MethodInfo? _method;
private object[] _args = new object[1] { 42 };
[GlobalSetup]
public void Setup()
{
_method = typeof(InvokeTest).GetMethod(nameof(InvokeMe), BindingFlags.Public | BindingFlags.Static)!;
}
[Benchmark]
// *** This went from ~116ns to ~39ns or 3x (66%) faster.***
public void InvokeSimpleMethod() => _method!.Invoke(obj: null, new object[] { 42 });
[Benchmark]
// *** This went from ~106ns to ~26ns or 4x (75%) faster. ***
public void InvokeSimpleMethodWithCachedArgs() => _method!.Invoke(obj: null, _args);
public static int InvokeMe(int i) => i;
}
}
ML.NET 文本分类 API
文本分类是将标签或类别应用于文本的过程。
常见用例包括:
- 将电子邮件分类为垃圾邮件或非垃圾邮件
- 分析客户评论中的正面或负面情绪
- 将标签应用于支持票证
文本分类是分类的一个子集,因此今天您可以使用 ML.NET 中的现有分类算法来解决文本分类问题。但是,这些算法并不能解决文本分类以及现代深度学习技术的常见挑战。
我们很高兴地推出 ML.NET 文本分类 API,该 API 使你能够更轻松地训练自定义文本分类模型,并将用于自然语言处理的最新最先进的深度学习技术引入 ML.NET。
CodeGen
非常感谢社区贡献者。
@singleaccretion预览版 5 期间贡献了 23 项 PR 贡献,亮点是:
- 改进冗余分支优化以处理更多副作用 https://github.com/dotnet/runtime/pull/68447
- PUTARG_STK/x86: 标记推送 [mem] 候选注册可选 https://github.com/dotnet/runtime/pull/68641
- 复制传播到 LCL_FLDs https://github.com/dotnet/runtime/pull/68592
@Sandreenko完成,允许StoreLclVar src成为IND / FLD https://github.com/dotnet/runtime/pull/59315。@hez2010 https://github.com/dotnet/runtime/pull/68475 中修复了 CircleInConvex 测试。
来自@anthonycanino,@aromaa和@ta264的更多贡献包含在以下部分中。
手臂64
https://github.com/dotnet/runtime/pull/68363 合并的“msub”(将两个寄存器值相乘,从第三个寄存器值中减去乘积)和“madd”(将两个寄存器值相乘,添加第三个寄存器值)逻辑。
Arm64:让 CpBlkUnroll 和 InitBlkUnroll 使用 SIMD 寄存器初始化复制小于 128 字节的内存块
循环优化
https://github.com/dotnet/runtime/pull/67930 处理循环克隆的更多方案现在支持以 > 1 为增量向后或向前移动的循环。
https://github.com/dotnet/runtime/pull/68588 提升“this”对象的空检查,将对象上的空检查移动到循环之外。
x86/x64 优化
- https://github.com/dotnet/runtime/pull/67182 在 x64 上将 shlx、sarx、shrx 的 mov+shl、sar 或 shr 发出到 x64 上的 shlx、sarx 或 shrx。
- https://github.com/dotnet/runtime/pull/68091 为 x64 启用了 UMOD 优化。
- @anthonycanino https://github.com/dotnet/runtime/pull/68677 中添加了 X86Serialize 硬件内部函数。
- @aromaa优化的bswap+mov到 https://github.com/dotnet/runtime/pull/66965 中的movbe。
- @ta264 https://github.com/dotnet/runtime/pull/68046 中修复了 clr.alljits 子集的 linux-x86 编译。
常规优化
- https://github.com/dotnet/runtime/pull/68105 启用了多个嵌套的“无 GC”区域请求。
- https://github.com/dotnet/runtime/pull/69034 删除了“提升参数”尾声限制。
实现 JIT 现代化
随着社区对JIT代码库的贡献增加,重组和现代化我们的代码库变得非常重要,以使我们的贡献者能够轻松提升和快速开发代码。
在预览版 5 中,我们在内部做了大量工作,清理了 JIT 的中间表示形式,并删除了过去设计决策带来的限制。在许多情况下,这项工作导致JIT本身的内存使用量减少和吞吐量增加,而在其他情况下,它导致更好的代码质量。以下是一些亮点:
- 删除 CLS_VAR https://github.com/dotnet/runtime/pull/68524
- 删除 GT_ARGPLACE https://github.com/dotnet/runtime/pull/68140
- 删除 GT_PUTARG_TYPE https://github.com/dotnet/runtime/pull/68748
上述内容允许我们在使用字节/sbyte/short/ushort类型的参数内联函数时删除JIT内联器中的旧限制,从而提高代码质量(允许内联器替换小参数 https://github.com/dotnet/runtime/pull/69068 )
需要改进的一个领域是更好地理解涉及结构和结构字段的读取和写入的不安全代码。@SingleAccretion通过将JIT的内部模型切换到更通用的“物理”模型,在这方面做出了巨大的变化。这为JIT使用结构重新解释等功能更好地推理不安全代码铺平了道路:
- 物理值编号 https://github.com/dotnet/runtime/pull/68712
- 为VNF_BitCast https://github.com/dotnet/runtime/pull/68979 实施恒定折叠
还进行了其他小的清理以简化JIT IR:
- 移除 GTF_LATE_ARG https://github.com/dotnet/runtime/pull/68617
- 替换内联候选参数中的GT_RET_EXPR https://github.com/dotnet/runtime/pull/69117
- 删除存储作为 LIR https://github.com/dotnet/runtime/pull/68460 中调用的操作数
启用库修剪
如前所述,修整允许 SDK 从自包含的应用中删除未使用的代码,以使其更小。但是,修剪警告可能指示应用与修剪不兼容。要使应用程序兼容,它们的所有引用也必须兼容。
为此,我们还需要库采用修剪。在预览版 5 中,我们努力使用 Roslyn 分析器更轻松地查找和修复库中的修剪警告。若要打开库的修剪警告,请添加到项目文件中。修复警告后,使用库修剪的应用程序将变小,并且与修剪兼容。<IsTrimmable>true</IsTrimmable>
贡献者聚焦:史蒂夫·邓恩
Steve 记录了他对 .NET 的贡献的开始,我们非常感谢所有这些贡献。我们想借此机会特别指出Steve在Microsoft.Extensions.Configuration上的工作。他处理了需要大量技术知识和领域专业知识的高影响力问题,一个很好的例子是使用配置绑定支持不可变类型。
史蒂夫不仅能够应对技术挑战,而且他也很善解人意,耐心和周到。在他最近的公关中,史蒂夫收到了来自六条评论的123条评论,涉及41次提交。史蒂夫对反馈表示欢迎,并主动达成每个人都满意的解决方案。史蒂夫是高质量社区贡献者的完美典范。
用史蒂夫自己的话说:
我目前在.NET中为企业客户编写服务。过去,我发布过打包产品,包括阿拉伯主题,这是“Windows 95 Plus”的阿拉伯语转换!
我在80年代中期开始编写软件,从BBC Basic开始,然后是Commodore 64游戏,包括Better dead than Alien,Call me psycho,Space relief,Galaxia 7,Thunder Hawk和Zone Z。
我经常写关于 https://dunnhq.com 的文章,我目前正在研究治愈对Vogen的原始痴迷。
贡献者聚焦:安东尼·卡尼诺
Anthony 最近根据 API 提案添加了 X86Serialize 硬件内部组件,该提案公开了 xarch 序列化指令,引用了英特尔 64 和 IA-32 架构软件开发人员手册、第 3A 卷和英特尔架构指令集扩展和未来功能的几章。
这允许开发人员在有条件之前发出,例如:X86Serialize.Serialize()
X86Serialize.Serialize();
if (someCondition)
{
// ...
}
感谢 Anthony 对 .NET 的多次贡献!
用安东尼自己的话说:
我的名字是 Anthony Canino,我是英特尔的一名软件工程师,总部位于华盛顿州西雅图,对编译器开发和语言设计充满热情。作为新地点的PNW居民,我喜欢利用该地区所有美妙的徒步旅行和美丽的自然风光,以及我的两只西伯利亚哈士奇。
我们在英特尔的团队信奉开发人员体验第一,我们支持 .NET 生态系统,并希望继续看到该平台蓬勃发展,并成为当今以开发人员为中心的核心。我对 .NET 的一些初步贡献是 xarch 代码发射器中的一些窥视孔优化,这些优化确实帮助我理解了代码生成的一些较低级别的细节。专门针对 .NET的RyuJIT,我发现代码和文档的质量大大降低了在具有挑战性的领域做出贡献的门槛。
我喜欢为 .NET 做出贡献的原因是 Microsoft 致力于使开源开发人员能够通过生产级资源(文档和测试基础结构和管道)以及专业的开发人员社区支持和规划来参与该项目。这种处理方式确实让外部贡献者感受到项目整体方向的一部分,并鼓励对生态系统做出进一步的贡献。
定位 .NET 7
若要面向 .NET 7,需要在项目文件中使用 .NET 7 目标框架名字对象 (TFM)。例如:
<TargetFramework>net7.0</TargetFramework>
下面是全套 .NET 7 TFM,包括特定于操作的 TFM。
net7.0
net7.0-android
net7.0-ios
net7.0-maccatalyst
net7.0-macos
net7.0-tvos
net7.0-windows
我们希望从 .NET 6 升级到 .NET 7 应该很简单。请报告在使用 .NET 7 测试现有应用的过程中发现的任何重大更改。
支持
.NET 7 是一个短期支持 (STS) 版本,这意味着它将从发布日期起 18 个月内获得免费支持和修补程序。请务必注意,所有版本的质量都是相同的。唯一的区别是支撑的长度。
我们最近将“当前”名称更改为“短期支持(STS)”。我们正在推出这一变化。
重大更改
通过阅读 .NET 7 中的重大更改文档,可以找到 .NET 7 中重大更改的最新列表。它按区域和版本列出了重大更改,并带有指向详细说明的链接。