性能和互操作性
C# 9.0提供了三个新功能来改善对本地交互和需要高性能的低级库的支持:原生大小的整数、函数指针和省略 localsinit 标志。本文将详细介绍这些新功能。
# 原生大小的整数
原生大小的整数(nint和nuint)是整数类型,由底层类型 System.IntPtr 和 System.UIntPtr 表示。编译器为这些类型提供了附加的转换和操作,作为本机整数的表面。原生大小的整数定义了 MaxValue 或 MinValue 属性。这些值不能作为编译时常量表示,因为它们取决于目标机器上整数的本机大小。这些值在运行时是只读的。您可以使用 nint 的常量值在 [int.MinValue .. int.MaxValue] 范围内。您可以使用 nuint 的常量值在 [uint.MinValue .. uint.MaxValue] 范围内。编译器对使用 System.Int32 和 System.UInt32 类型的所有一元和二元运算符执行常量折叠。如果结果不符合 32 位,则在运行时执行操作,且不被视为常量。在需要大量使用整数运算且需要尽可能快的性能的情况下,原生大小的整数可以提高性能。更多信息请参见nint和nuint类型 (opens new window) .
以下是一个示例,展示如何使用原生大小的整数类型:
using System;
class Program
{
static void Main(string[] args)
{
// nint and nuint example
nint ptr = new IntPtr(42);
Console.WriteLine(ptr);
nuint uptr = new UIntPtr(42);
Console.WriteLine(uptr);
}
}
# 函数指针
函数指针提供了一个易于使用的语法来访问 IL 操作码 ldftn 和 calli。您可以使用新的 delegate* 语法声明函数指针。delegate* 类型是一个指针类型。调用 delegate* 类型使用 calli,与使用调用 Invoke() 方法的委托不同。从语法上讲,这两种调用是相同的。函数指针调用使用托管调用约定。您可以在 delegate* 声明上使用 unmanaged 关键字来声明您需要非托管调用约定。其他调用约定可以使用委托*声明上的属性来指定。更多信息请参见Unsafe code and pointer types (opens new window) 。
以下是一个示例,展示如何使用函数指针类型:
using System;
unsafe delegate*<int,int,int> AddDelegate;
class Program
{
static int Add(int a, int b)
{
return a + b;
}
static void Main(string[] args)
{
// function pointer example
AddDelegate add = &Add;
int result = add(1, 2);
Console.WriteLine(result);
}
}
# 省略 localsinit 标志
您可以添加 System.Runtime.CompilerServices.SkipLocalsInitAttribute 来指示编译器不发出 localsinit 标志。该标志指示 CLR 对所有局部变量进行零初始化。自 C# 1.0 以来,localsinit 标志已经成为默认行为。但是,在某些情况下,额外的零初始化可能会产生可测量的性能影响,特别是当您使用 stackalloc 时。在这些情况下,您可以添加 SkipLocalsInitAttribute。您可以将其添加到单个方法或属性,或添加到类、结构、接口或甚至模块中。该属性不会影响抽象方法;它影响生成的实现代码。更多信息请参见SkipLocalsInit attribute (opens new window)。
以下是一个示例,展示如何使用 SkipLocalsInitAttribute:
using System;
using System.Runtime.CompilerServices;
class Program
{
[SkipLocalsInit]
static void DoSomething()
{
// Do something without initializing locals
}
static void Main(string[] args)
{
DoSomething();
}
}
# 性能和交互操作注意事项
这些功能可以在某些情况下提高性能。但是,只有在仔细比较采用前后的性能之后,才应该使用它们。涉及原生大小整数的代码必须在具有不同整数大小的多个目标平台上进行测试。其他功能需要使用不安全代码。
# 结论
C# 9.0为本地交互和需要高性能的低级库提供了三个新功能:原生大小的整数、函数指针和省略 localsinit 标志。这些功能可以在某些情况下提高性能,但是使用它们之前必须进行仔细的基准测试。涉及原生大小整数的代码必须在具有不同整数大小的多个目标平台上进行测试。其他功能需要使用不安全代码。