C#/.NET 通过按引用传递 Array
类型来实现可变函数参数(与 C/C++ 不同,C/C++ 只是将所有值直接放在堆栈上,无论好坏).
在 C# 世界中,这有一个很好的优势,即允许您使用“原始”参数或可重用数组实例调用相同的函数:
CultureInfo c = CultureInfo.InvariantCulture;
String formatted0 = String.Format( c, "{0} {1} {2}", 1, 2, 3 );
Int32 third = 3;
String formatted0 = String.Format( c, "{0} {1} {2}", 1, 2, third );
Object[] values = new Object[] { 1, 2, 3 };
String formatted1 = String.Format( c, "{0} {1} {2}", values );
这意味着生成的 CIL 等同于:
String formatted0 = String.Format( c, "{0} {1} {2}", new Object[] { 1, 2, 3 } );
Int32 third = 3;
String formatted0 = String.Format( c, "{0} {1} {2}", new Object[] { 1, 2, third } );
Object[] values = new Object[] { 1, 2, 3 };
String formatted1 = String.Format( c, "{0} {1} {2}", values );
这意味着(在非优化的 JIT 编译器中)每次调用都会分配一个新的 Object[]
实例 - 尽管在第三个示例中您可以将数组存储为字段或其他可重用值,以消除 每次 调用 String.Format
时的新分配。
但在官方的 CLR 运行时和 JIT 中,是否做了任何优化来消除这种分配?或者数组是否经过特殊标记,以便一旦执行离开调用站点的范围,它就会被释放?
或者,也许,因为 C# 或 JIT 编译器知道参数的数量(当使用“原始”时),它可以做与 stackalloc
关键字相同的事情,并将数组放在堆栈上,因此不需要释放它?
最佳答案
是的,每次都会分配一个新的数组。
不,没有进行任何优化。没有你建议的那种“实习”。毕竟,怎么可能呢?接收方法可以对数组做任何事情,包括改变其成员,或重新分配数组条目,或将对数组的引用传递给其他人(然后没有 params
)。
不存在您建议的那种特殊“标记”。这些数组以与其他任何东西相同的方式进行垃圾回收。
补充:当然有一种特殊情况,我们在这里讨论的那种“实习”可能很容易做到,那就是长度为零的数组。 C# 编译器可以调用 Array.Empty<T>()
(每次都返回相同的长度为零的数组)而不是创建 new T[] { }
每当它遇到对 params
的调用时需要长度为零的数组。
这种可能性的原因是长度为零的数组是真正不可变的。
当然可以发现长度为零的数组的“实习”,例如,如果要引入该功能,此类的行为将会改变:
class ParamsArrayKeeper
{
readonly HashSet<object[]> knownArrays = new HashSet<object[]>(); // reference-equals semantics
public void NewCall(params object[] arr)
{
var isNew = knownArrays.Add(arr);
Console.WriteLine("Was this params array seen before: " + !isNew);
Console.WriteLine("Number of instances now kept: " + knownArrays.Count);
}
}
补充:鉴于 .NET 的“奇怪”数组协变不适用于值类型,您确定您的代码:
Int32[] values = new Int32[ 1, 2, 3 ];
String formatted1 = String.Format( CultureInfo.InvariantCulture, "{0} {1} {2}", values );
按预期工作(如果语法更正为 new[] { 1, 2, 3, }
或类似语法,这肯定会导致错误的重载 String.Format
)。
关于c# - C# 中的 `params` 是否总是会导致在每次调用时分配一个新数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38679180/