DEV Community

Cover image for C# 13: How params Collections Transform Development
ByteHide
ByteHide

Posted on • Originally published at bytehide.com

C# 13: How params Collections Transform Development

With the arrival of C# 13, the language continues to evolve to provide a smoother, safer, and more efficient development experience. One of the standout features in this new version is the ability to use any type of collection compatible with collection expressions in params parameters. This enhancement, while seemingly small, opens up new possibilities for writing clearer, more optimized, and expressive code.

In this article, we’ll explore how this new feature works, how it relates to the collection expressions introduced in C# 12, and why it should be part of your toolkit as a .NET developer.

What are Collection Expressions?

Collection expressions, introduced in C# 12, provide a simpler and more unified way to create different types of collections. Previously, creating collections required methods and syntax specific to the type of collection being used.

For example, before C# 12, using arrays or Span required more verbose syntax:

// Before C# 12
WriteByteArray(new[] { (byte)1, (byte)2, (byte)3 });
WriteByteSpan(stackalloc[] { (byte)1, (byte)2, (byte)3 });
Enter fullscreen mode Exit fullscreen mode

With collection expressions, this process is significantly simplified, improving readability and reducing code complexity:

// After C# 12
WriteByteArray([1, 2, 3]);
WriteByteSpan([1, 2, 3]);

static void WriteByteArray(byte[] bytes) { }
static void WriteByteSpan(Span bytes) { }
Enter fullscreen mode Exit fullscreen mode

In addition to improving syntax, these expressions automatically infer both the type of the collection and the type of its elements, making them more intuitive and easier to use.


params in C# 13: Beyond Arrays

Since its introduction in C# 1.0, params parameters have allowed developers to declare methods that accept a variable number of arguments, passed as an array. This feature simplifies method calls when the number of arguments is unknown at compile time.

However, until C# 12, params parameters were exclusively limited to arrays. For example:

static void WriteByteArray(params byte[] bytes) { }

// Usage
WriteByteArray(1, 2, 3);// List of values
WriteByteArray();// No values

Enter fullscreen mode Exit fullscreen mode

C# 13 removes this limitation by allowing params parameters to accept any type of collection compatible with collection expressions. This includes types like SpanIReadOnlyList, and even IEnumerable:

static void WriteByteSpan(params Span bytes) { }

// Usage
WriteByteSpan(1, 2, 3);
WriteByteSpan();

Enter fullscreen mode Exit fullscreen mode

This flexibility not only improves code clarity but can also optimize performance in certain scenarios, as we’ll see below.


Benefits of params with Collections

1. Optimized Performance with Span<T>

Using collections like Span<T> instead of traditional arrays allows the compiler to create the collection directly on the stack, avoiding heap allocations. This reduces memory overhead and improves performance:

static void WriteNumbers(params Span numbers) { } 

WriteNumbers(1, 2, 3); // Uses stackalloc internally

Enter fullscreen mode Exit fullscreen mode

2. Clearer Communication of Method Purpose

Specifying more descriptive types like IReadOnlyList<T> or IEnumerable<T> helps method users understand how the data will be handled within the method. For example:

static void ProcessData(params IReadOnlyList data) { } 

Enter fullscreen mode Exit fullscreen mode

Here, the IReadOnlyList<T> type clearly communicates that the collection will not be modified within the method.

3. Greater Flexibility for Method

Users Developers can pass data in a variety of forms: lists of values, arrays, LINQ collections, and more. For example:

static void WriteByteArray(params IEnumerable bytes) { } 
byte[] data = [1, 2, 3, 4]; 
WriteByteArray(data.Where(x => x < 4));// Filters values before passing

Enter fullscreen mode Exit fullscreen mode

4. Compatibility with Preexisting Collections

params parameters now accept any collection implementing IEnumerable, allowing smoother integration with the .NET ecosystem.


Overloading and Method Resolution

Support for collections in params parameters also enhances method overloading capabilities in C#. For example, you can have two methods with the same name but different types of params parameters:

static void WriteNumbers(params IEnumerable values) => Console.WriteLine("IEnumerable");
static void WriteNumbers(params ReadOnlySpan values) => Console.WriteLine("Span");
Enter fullscreen mode Exit fullscreen mode

The compiler selects the most appropriate overload based on context:

  • A list of values or an array is mapped to Span<T>
  • Collections like List<T> or results from LINQ queries are mapped to IEnumerable<T>.
WriteNumbers(1, 2, 3);// Span
WriteNumbers(new[] { 1, 2, 3 });// Span
WriteNumbers(new List { 1 });// IEnumerable
Enter fullscreen mode Exit fullscreen mode

This improves efficiency by leveraging Span whenever possible while maintaining flexibility with other collections.


Considerations When Using params

Although this new feature expands the language’s capabilities, it’s important to maintain consistency in method functionality. If multiple overloads have the same name, they should perform similar tasks, differing only in performance or how they process data.

For example, overloaded methods should delegate to a common implementation to avoid inconsistencies:

private static void WriteNumbers(params IEnumerable values) => WriteCommon(values);
private static void WriteNumbers(params ReadOnlySpan values) => WriteCommon(values.ToArray());

private static void WriteCommon(IEnumerable values) => Console.WriteLine(string.Join(", ", values));
Enter fullscreen mode Exit fullscreen mode

Conclusion

C# 13 transforms the use of params parameters by enabling collections beyond traditional arrays. This change not only simplifies how developers write and consume methods but also introduces significant improvements in terms of performance and flexibility.

With this new capability, code becomes clearer and more adaptive, facilitating the integration of different collection types and optimizing resources. It’s a powerful tool for writing modern, efficient applications, and incorporating this feature into your projects can make a real difference in your development workflows. Now is the perfect time to experiment with these improvements and take full advantage of the new possibilities that C# 13 offers.

Top comments (0)