C#/.NET has variadic function parameters by passing an Array
type by-reference (as opposed to C/C++ which just places all of the values directly on the stack, for better and for worse).
In the C# world this has a neat advantage of allowing you to call the same function with either 'raw' arguments or a reusable array instance:
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 );
This means that the generated CIL is equivalent to:
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 );
Which means that (in a non-optimizing JIT compiler) every call will allocate a new Object[]
instance - though in the third example you're able to store the array as a field or other reusable value to eliminate the new allocation on every call to String.Format
.
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?
Or, perhaps, because the C# or JIT compiler knows the number of arguments (when used "raw") could it do the same thing as the stackalloc
keyword and place the array on the stack, and thus not need to deallocate it?
Yes, a new array is allocated every time.
No, no optimizations are done. There is no "interning" of the kind you suggest. After all, how could there be? The receiving method can do anything with the array, including mutating its members, or reassigning array entries, or passing a reference to the array on to others (without params
then).
No special "tagging" of the kind you suggest, exists. These arrays are garbage collected in the same way as anything else.
Addition: There is of course one special case where "interning" of the kind we discuss here, could be easy to do, and that is for length-zero arrays. The C# compiler could call Array.Empty<T>()
(which returns the same length-zero array each time) instead of creating a new T[] { }
whenever it encountered a call to params
where a length-zero array is needed.
The reason for this possibility is that length-zero arrays are truly immutable.
Of course "interning" of length-zero arrays would be discoverable, for example the behavior of this class would change if the feature were to be introduced:
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);
}
}
Addition: Given that the "strange" array covariance of .NET does not apply to value types, are you sure your code:
Int32[] values = new Int32[ 1, 2, 3 ];
String formatted1 = String.Format( CultureInfo.InvariantCulture, "{0} {1} {2}", values );
works as intended (if the syntax is corrected to new[] { 1, 2, 3, }
or similar, this will go to the wrong overload of String.Format
, for sure).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With