Bewerken

Delen via


C# overload resolution prefers params span-type overloads

C# 13 added support for params parameters declared with collection types other than arrays. In particular, params ReadOnlySpan<T> and params Span<T> are supported, and overload resolution prefers a params span type over a params array type when both are applicable.

.NET 9 added params span overloads for various methods in the core .NET libraries. Those methods had pre-existing overloads that took params arrays. When you recompile code with existing calls to those methods where arguments are passed in expanded form, the compiler will now bind to the params span overload.

The new binding leads to a potential breaking change for existing calls to those overloads within Expression lambda expressions, which don't support ref struct instances. In those cases, the C# 13 compiler reports an error when binding to the params span overload.

For example, consider string.Join():

using System;
using System.Linq.Expressions;

Expression<Func<string, string, string>> join =
    (x, y) => string.Join("", x, y);

When compiled with .NET 8, the call binds to Join(String, String[]), without errors.

When compiled with C# 13 and .NET 9, the call binds to Join(String, ReadOnlySpan<String>), and because the call is within an expression tree, the following errors are reported:

error CS8640: Expression tree cannot contain value of ref struct or restricted type 'ReadOnlySpan'. error CS9226: An expression tree may not contain an expanded form of non-array params

Version introduced

.NET 9

Previous behavior

Prior to C# 13, params parameters were limited to array types only. Calls to those methods in expanded form resulted in implicit array instances only, which are supported in Expression lambda expressions.

New behavior

With C# 13 and .NET 9, for methods with overloads that take params array types and params span types, overload resolution prefers the params span overload. Such a call creates an implicit span instance at the call site. For calls within Expression lambda expressions, the implicit ref struct span instance is reported as a compiler error.

Type of breaking change

This change can affect source compatibility.

Reason for change

The new method overloads were added for performance reasons. params span support allows the compiler to avoid an allocation for the params argument at the call site.

If your code is affected, the recommended workaround is to call the method with an explicit array so the call binds to the params array overload.

For the previous example, use new string[] { ... }:

Expression<Func<string, string, string>> join =
    (x, y) => string.Join("", new string[] { x, y });

Affected APIs

See also