How to Use params with Collections in C# 13
I remember the days when params
in C# could only be arrays. Every method that needed flexibility had to accept params T[]
, and every call packed arguments into an array—allocating on the heap.
That’s all changed in C# 13.
Now, params
can target any collection type supported by collection expressions, Span<T>
, ReadOnlySpan<T>
, IEnumerable<T>
, and more. It’s a quiet change, but it opens up clarity, flexibility, and performance benefits.
What’s Changed with params
in C# 13
Before, params
meant one thing: an array. With C# 13, you can write:
static void DoWork(params ReadOnlySpan<int> numbers) { /*...*/ }
DoWork(1, 2, 3);
or
static void LogMessages(params IEnumerable<string> messages) { /*...*/ }
LogMessages("start", "process", "done");
That means you can now:
- Accept
Span<T>
orReadOnlySpan<T>
via params - Use
IEnumerable<T>
,IReadOnlyList<T>
,ICollection<T>
, etc. - Let the compiler generate the collection from a literal or inline value
Why It Matters?
1. Zero-Allocation Performance
params
Span<T>
lets the compiler allocate data on the stack, not the heap—no GC pressure.- Compared to
params T[]
, it’s much faster and leaner.
2. Sharper API Intent
- If a method takes params ReadOnlySpan
, callers know it won’t modify the data. - params IEnumerable
signals read-only and allows many input types—including LINQ results.
3. Cleaner Overloads
You can define overloads and rely on the compiler to pick the most efficient option:
static void WriteNumbers(params ReadOnlySpan<int> nums) { … }
static void WriteNumbers(params IEnumerable<int> nums) { … }
Passing literal values or arrays picks the Span overload. Passing a List<T>
uses the IEnumerable overload. No guessing needed.
Examples in Action
Example: Summing Integers with params Span
static int Sum(params ReadOnlySpan<int> values)
{
int total = 0;
foreach (var v in values)
total += v;
return total;
}
// Usage:
int result = Sum(1, 2, 3, 4, 5);
No array is allocated. All values are on the stack.
Example: Logging Strings with params IEnumerable<string>
static void Log(params IEnumerable<string> messages)
{
foreach (var m in messages)
Console.WriteLine(m);
}
// Works with:
Log("a", "b", "c");
Log(new[] { "x", "y" });
Log(myList.Where(m => m != null));
Handles arrays, LINQ results, lists—any IEnumerable seamlessly.
When to Use Which params
Type?
Method Signature | Best For | Allocation Behavior |
---|---|---|
params ReadOnlySpan<T> | Performance‑critical, small items | Stack‑alloc, zero heap allocation |
params Span<T> | When mutability is needed inside method | Stack or heap based on size |
params IEnumerable<T> / ReadOnlyList | General purpose flex‑API | Synthesized collection if needed |
Things to Keep in Mind
Overload Ambiguity
Don’t mix params T[]
and params Span<T>
without care. Compiler resolves overloads based on types. Carefully design overloads to avoid confusion.
Invocation Rules
- Only one params parameter per method, and it must be last.
- You can pass a collection directly:
- DoWork(new[] {1, 2, 3})
- Or value list: DoWork(1, 2, 3)
Performance Trade-offs
- Use Span
when you want maximum speed and no allocations. - Use IEnumerable
for flexibility—but it may allocate internally if values come from value list.
Why Sharp Engineers Should Care
- Cleaner API: Less boilerplate, clearer intent.
- Better performance: You avoid unnecessary memory allocation.
- More flexibility: Accept input in many forms, without multiple overloads. This feature builds on the C# 12 collection expressions groundwork and brings it forward into APIs. It’s part of a bigger evolution toward expressive and efficient code.
Final Thoughts
C# 13’s enhanced params collections bring a subtle—but meaningful—step forward. If you’re writing performance-sensitive utilities or designing APIs for clarity, this feature gives you both.
Here’s how to get started:
- Use params ReadOnlySpan
where performance matters. - Opt for params IEnumerable
when you want flexibility. - Add overloads thoughtfully to guide the compiler toward the best match.
- Compile against .NET 9+ and enable C# 13 preview if needed.
This is a feature that’s particularly valuable if you care about clean APIs and optimized performance—both traits of seasoned engineers. If you’d like a companion blog post or code examples to practice with, just say the word!
Want more posts like this on software engineering, .NET, C#, Web Development and SaaS? Subscribe for weekly insights.