Ever used the params
keyword in C#? If you write C# code regularly, you probably reach for it whenever you need to pass a variable number of arguments to a method. It’s super handy, letting you skip the tedious step of creating arrays first.
But until now, there’s been a limitation: params
only worked with arrays. This meant every call created memory allocations with potential performance costs. The good news? C# 14 is changing the game by extending params
to work with modern collections like IEnumerable<T>
, Span<T>
, and more.
How params Has Worked Until Now
Let’s look at how we’ve all been using params
up to this point:
// Traditional params with arrays
public void DisplayNames(params string[] names)
{
foreach (var name in names)
{
Console.WriteLine(name);
}
}
// Usage
DisplayNames("Alice", "Bob", "Charlie");
// Behind the scenes: compiler creates a new string[] array
This pattern works, but brings some notable drawbacks:
- Each call allocates a new array on the heap (memory overhead)
- Frequent calls increase garbage collection pressure
- Arrays offer less flexibility than other collection types
The New params: Supporting Collections
C# 14 broadens the params
keyword to work with various collection types:
IEnumerable<T>
ReadOnlySpan<T>
Span<T>
- Other collection interfaces
Here’s what this looks like in real code:
// With IEnumerable<T>
public void ShowItems(params IEnumerable<string> items)
{
foreach (var item in items)
{
Console.WriteLine(item);
}
}
// With Span<T>
public void ProcessValues(params Span<int> values)
{
for (int i = 0; i < values.Length; i++)
{
values[i] *= 2; // Can modify values with Span<T>
}
}
Performance Implications
Let’s talk about the real-world performance benefits this change brings:
Reduced Allocations
With Span<T>
or ReadOnlySpan<T>
, you can skip heap allocations completely since these types can work with stack-allocated memory:
// No heap allocations needed
CalculateSum(params Span<int> values)
{
int sum = 0;
foreach (var value in values)
{
sum += value;
}
return sum;
}
var result = CalculateSum(1, 2, 3, 4, 5); // Uses the stack, not the heap
Better Fit with Modern APIs
Many newer .NET APIs use Span<T>
for better performance. Now your code can fit right in with these patterns:
// Much cleaner than creating arrays just to pass them
public bool TryParseValues(params Span<char> characters)
{
// Work directly with the span without extra memory use
return ProcessSpanDirectly(characters);
}
Memory Usage: Before and After
Compare these two approaches:
// Old way
public void ProcessTraditional(params int[] values)
{
// Process array values
}
// New way
public void ProcessModern(params ReadOnlySpan<int> values)
{
// Process span values
}
// Old: Creates a new array on the heap
ProcessTraditional(1, 2, 3, 4, 5);
// New: No heap allocation needed
ProcessModern(1, 2, 3, 4, 5);
Cleaner LINQ Code
LINQ operations become more straightforward:
// Old way with array
public IEnumerable<T> CombineSources<T>(params IEnumerable<T>[] sources)
{
return sources.SelectMany(source => source);
}
// New way, more direct
public IEnumerable<T> CombineSources<T>(params IEnumerable<T> sources)
{
return sources.SelectMany(source => source);
}
Frequently Asked Questions
What’s new about params in C# 14?
How does params for collections improve performance?
Can I use params with any collection type?
Will my existing code with params arrays still work?
What’s the advantage of using params Span over params T[]?
Can I mix and match different collection types with params?
Related Posts
- IEquatable
in C#: Why Every .NET Developer Should Master Custom Equality - Why Async Can Be Slower in Real Projects?
- Avoiding Boxing with Struct Dictionary Keys in C#: Performance and Best Practices
- High-Volume File Processing in C#: Efficient Patterns for Handling Thousands of Files
- 5 Essential Benefits of Immutability in C# Programming