C# · 12月 26, 2021

C#中的`params`总是会在每次调用时分配一个新的数组?

通过传递一个Array类型的引用(而不是C/C++,只是将所有的值直接放在堆栈上,更好和更糟),C#/ .NET具有可变参数.

在C#世界中,这有一个很好的优势,允许您使用“raw”参数或可重用的数组实例调用相同的函数:

CultureInfo c = CultureInfo.InvariantCulture;String formatted0 = String.Format( c,”{0} {1} {2}”,1,2,3 );Int32 third = 3;String formatted0 = String.Format( c,third );Int32[] values = new Int32[ 1,3 ];String formatted1 = String.Format( c,values );

这意味着生成的CIL等效于:

String formatted0 = String.Format( c,new Object[] { 1,3 } );Int32 third = 3;String formatted0 = String.Format( c,third } );Int32[] values = new Int32[ 1,values );

这意味着(在非优化的JIT编译器中)每个调用将分配一个新的Object []实例 – 尽管在第三个示例中,您可以将数组存储为字段或其他可重用的值,以消除每个调用String.Format.

但是在官方的CLR运行时和JIT中,为了消除这种分配而进行的任何优化?或者也许是特别标记的数组,一旦执行离开调用站点的范围,它将被释放?

或者也许是因为C#或JIT编译器知道参数的数量(当使用“raw”)时,它可以像stackalloc关键字那样做,并将数组放在堆栈上,因此不需要解除分配?

解决方法

But in the official CLR runtime and JIT are any optimizations done to eliminate this allocation?

你必须要求作者.但是考虑到需要付出多少努力,我怀疑.声明方法必须能够访问数组,并使用数组语法检索成员.所以任何优化都必然要重写方法逻辑来将数组访问转换为直接参数访问.

此外,优化将必须在全球进行,同时考虑到该方法的所有呼叫者.而且它必须检测方法是否将数组传递给其他任何东西.

这似乎不是一个可行的优化,特别是考虑到它对运行时性能的增加有多少价值.

Or perhaps is the array tagged specially so that it will be deallocated as soon as execution leaves the scope of the call-site?

没有必要特别标记数组,因为垃圾回收器已经能够自动处理这种情况了.事实上,一旦它在声明方法中不再使用,数组就可以被垃圾收集.无需等待方法返回.