作者:Kathleen Dollard
排版:Alan Wang
Visual Studio 17.7 Preview 3 和 .NET 8 Preview 6 的发布推进了 C# 12的发展。此预览版包含的功能为将来的性能增强奠定了基础。现在,您能够在库中更方便的使用内联函数。此预览版首次推出了一项实验性功能:拦截器。该功能允许生成器重新路由代码,例如提供特定于上下文的优化。最后,nameof
功能得到增强,您可以在更多的地方使用它。
安装最新的 Visual Studio 预览版或最新版本的 .NET SDK 来使用 C# 12 。将项目的语言版本设置为 preview,就可查看 C# 12 的功能:
csharp
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
由于这是实验性的功能,所以拦截器需要在项目文件中添加一个附加标志。
nameof
访问实例成员
nameof
关键字现在可用于成员名称,如初始化器、静态成员以及属性:
csharp
internal class NameOf
{
public string S { get; } = "";
public static int StaticField;
public string NameOfLength { get; } = nameof(S.Length);
public static void NameOfExamples()
{
Console.WriteLine(nameof(S.Length));
Console.WriteLine(nameof(StaticField.MinValue));
}
[Description($"String {nameof(S.Length)}")]
public int StringLength(string s)
{ return s.Length; }
}
您可以在 C# 12 的新增功能中了解更多信息。
内联数组
InlineArrayAttribute 是在以前的 .NET 8 预览版中引入到运行时的。 这是一项高级功能,主要由编译器、.NET 库和其他一些库使用。 该属性标识了一种可被视为连续基元序列的类型,以实现高效、类型安全、越界安全的可索引/可切分内联数据。 .NET 库使用内联数组提高应用程序和工具的性能。
编译器创建不同的 IL 来访问内联数组。 这会导致一些限制,例如不支持列表模式。 在大多数情况下,您可以像访问其他数组一样访问内联数组。 不同的 IL 可以在不更改代码的情况下提高性能:
csharp
private static void InlineArrayAccess(Buffer10<int> inlineArray)
{
for (int i = 0; i < 10; i++)
{
inlineArray[i] = i * i;
}
foreach (int i in inlineArray)
{
Console.WriteLine(i);
}
}
对于内联数组,大多数人倾向于使用,而不是创建。 但是,了解事物的运作方式大有裨益。 内联数组速度很快,因为它们依赖于指定长度的精确布局。 内联数组是一种具有单个字段的类型,并用指定数组长度的 InlineArrayAttribute
进行标记。 在上一个示例中使用的类型中,由于属性参数,运行时会在 Buffer10<T>
中为10个元素创建存储空间:
csharp
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer10<T>
{
private T _element0;
}
您可以在 C# 12的新增功能中了解更多信息。
拦截器
此预览版引入了一项名为拦截器的实验性功能。它适用于一些高级场景,特别是允许更好的提前编译(AOT)。作为 .NET 8 的实验部分,在未来版本中它可能会被更改或删除。因此,先不要在生产中使用这项功能。
拦截器允许将特定方法调用重新路由到不同的代码。属性指定实际的源代码位置,因此拦截器通常仅适用于源生成器。您可以阅读拦截器提案以了解有关拦截器如何工作的更多信息。
由于拦截器是一项实验性功能,因此您需要在项目文件中显式启用它们:
csharp
<PropertyGroup>
<Features>InterceptorsPreview</Features>
</PropertyGroup>
拦截器可以实现绝佳的代码模式。以下是一些例子:
- 可以拦截编译时已知的调用,例如具有常量模式的
Regex.IsMatch(@"a+b+")
,并使用静态生成的代码进行优化,以便更好地适用于提前编译(AOT)环境 。 - 可以拦截诸如
app.MapGet("/products", handler: (int? page, int? pageLength, MyDb db) => { ... })
之类的 ASP.NET Minimal API 调用来注册一个静态生成的 thunk,该 thunk 会直接调用用户的处理程序,从而跳过分配和间接。 - 在矢量化中,foreach 循环包含对用户方法的调用,编译器可以重写代码以在运行时检查和使用相关的内部函数,但如果这些内部函数不可用,则返回到原始代码。
- 依赖注入的静态依赖图解析,其中
provider.Register<MyService>()
可以被拦截。 - 可以拦截对查询提供程序的调用,以在编译时提供对另一种语言(例如 SQL)的翻译,而不是评估表达式树以在运行时进行翻译。
- 序列化器可以根据具体的调用类型(如
Serialize<MyType>()
)生成特定于类型的(反)序列化,所有这些都在编译时进行。
虽然大多数程序员不会直接使用拦截器,但我们仍希望它能够在开发中发挥重要作用,使您的应用程序更快运行并更易部署。拦截器预计在 C# 12/.NET 8 版本中仍保持实验阶段,并可能包含在 C# 的后续版本中。
总结
您可以在 Microsoft Learn 的 C# 12 新增功能页面上找到有关迄今为止引入的所有功能的更多信息,并在 Roslyn 功能状态页面上跟踪 C# 12 功能的演变。
您可以通过下载最新的 Visual Studio 预览版或最新版本的 .NET SDK 并在您的项目文件中将 LangVersion
设置 preview
来查看最新的 C# 12 功能。
请让我们知道您的想法!