params ReadOnlySpan: The Allocation-Free Params

Every time you call a method with params T[], the compiler sneaks in a brand-new array allocation behind your back. One call? No big deal. Thousands of calls on a hot path? You’re basically running a charity for the garbage collector. C# 13 finally lets you swap that T[] for ReadOnlySpan<T>, and the results are chef’s kiss.

The change is laughably simple. Just update your method signature:

// Before: every call allocates an array
public static int Sum(params int[] numbers)
{
var total = 0;
foreach (var n in numbers) total += n;
return total;
}
// After: zero allocations, stack-allocated span
public static int Sum(params ReadOnlySpan<int> numbers)
{
var total = 0;
foreach (var n in numbers) total += n;
return total;
}

Callers don’t change at all. Sum(1, 2, 3) compiles exactly the same way. But under the hood, the compiler now stack-allocates the arguments into an inline array and wraps them in a ReadOnlySpan<T>. No heap. No GC pressure. No drama.

This shines anywhere you have variadic helpers that get called frequently: logging, formatting, math utilities, builder patterns, you name it. And because ReadOnlySpan<T> is a supertype of arrays anyway, existing callers that pass an explicit int[] still work. It’s a free upgrade.

// All of these just work:
Sum(1, 2, 3); // inline span, zero alloc
Sum([10, 20, 30]); // collection expression, zero alloc
Sum(myArray); // existing array, still compiles

The takeaway? If you own a params method that lives anywhere near a hot path, slap ReadOnlySpan<T> on it and let the runtime do the rest. Your GC will send you a thank-you card.