La resolución de sobrecarga en C# prefiere las sobrecargas de tipo de intervalo params
.
C# 13 agregó compatibilidad para parámetros params
declarados con tipos de colecciones diferentes a arrays. En concreto, se admiten params ReadOnlySpan<T>
y params Span<T>
, y la resolución de sobrecarga prefiere un tipo de intervalo params
en lugar de un tipo de matriz params
cuando ambos son aplicables.
.NET 9 agregóparams
sobrecargas de intervalo para varios métodos en las bibliotecas principales de .NET. Esos métodos tenían sobrecargas preexistentes que tomaban matrices params
. Al volver a compilar código con llamadas existentes a esos métodos en los que los argumentos se pasan de forma expandida, el compilador ahora se enlazará a la sobrecarga de intervalo params
.
El nuevo enlace conduce a un posible cambio importante para las llamadas existentes a esas sobrecargas dentro de expresiones lambda Expression, que no admiten instancias de ref struct
. En esos casos, el compilador de C# 13 notifica un error al vincularse con la sobrecarga de intervalo params
.
Por ejemplo, considere string.Join()
:
using System;
using System.Linq.Expressions;
Expression<Func<string, string, string>> join =
(x, y) => string.Join("", x, y);
Cuando se compila con .NET 8, la llamada se enlaza a Join(String, String[]), sin errores.
Cuando se compila con C# 13 y .NET 9, la llamada se enlaza a Join(String, ReadOnlySpan<String>)y porque la llamada está dentro de un árbol de expresiones de , se notifican los errores siguientes:
error CS8640: El árbol de expresiones no puede contener el valor de la estructura ref o el tipo restringido "ReadOnlySpan". error CS9226: Es posible que un árbol de expresiones no contenga una forma expandida de parámetros que no sean de matriz
Versión introducida
.NET 9
Comportamiento anterior
Antes de C# 13, los parámetros de params
solo se limitaban a los tipos de matriz. Las llamadas a esos métodos en formato expandido solo dieron lugar a instancias implícitas de matriz, que se admiten en expresiones lambda Expression.
Nuevo comportamiento
Con C# 13 y .NET 9, para los métodos con sobrecargas que aceptan tipos de matriz params
y tipos de intervalo params
, la resolución de sobrecargas prefiere la sobrecarga de intervalo params
. Esta llamada crea una instancia de intervalo implícita en el sitio de llamada. Para llamadas dentro de expresiones lambda Expression, la instancia de intervalo implícita ref struct
se notifica como error de compilador.
Tipo de cambio disruptivo
Este cambio puede afectar a la compatibilidad de orígenes.
Motivo del cambio
Las nuevas sobrecargas de método se agregaron por razones de rendimiento. El intervalo params
permite que el compilador evite una asignación para el argumento params
en el punto de llamada.
Acción recomendada
Si su código se ve afectado, la solución alternativa recomendada es llamar al método con una matriz explícita para que la llamada se enlace a la sobrecarga de matriz params
.
Para el ejemplo anterior, use new string[] { ... }
:
Expression<Func<string, string, string>> join =
(x, y) => string.Join("", new string[] { x, y });
APIs afectadas
- System.Collections.Immutable.ImmutableArray.Create<T>(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableArray<T>.AddRange(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableArray<T>.InsertRange(Int32, ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableArray<T>.Builder.AddRange(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableArray<T>.Builder.AddRange<TDerived>(ReadOnlySpan<TDerived>)
- System.Collections.Immutable.ImmutableHashSet.Create<T>(IEqualityComparer<T>, ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableHashSet.Create<T>(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableList.Create<T>(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableQueue.Create<T>(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableSortedSet.Create<T>(IComparer<T>, ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableSortedSet.Create<T>(ReadOnlySpan<T>)
- System.Collections.Immutable.ImmutableStack.Create<T>(ReadOnlySpan<T>)
- System.Console.Write(String, ReadOnlySpan<Object>)
- System.Console.WriteLine(String, ReadOnlySpan<Object>)
- System.Diagnostics.Metrics.Counter<T>.Add(T, ReadOnlySpan<KeyValuePair<String,Object>>)
- System.Diagnostics.Metrics.Gauge<T>.Record(T, ReadOnlySpan<KeyValuePair<String,Object>>)
- System.Diagnostics.Metrics.UpDownCounter<T>.Add(T, ReadOnlySpan<KeyValuePair<String,Object>>)
- System.Diagnostics.Metrics.Histogram<T>.Record(T, ReadOnlySpan<KeyValuePair<String,Object>>)
- System.MemoryExtensions.TryWrite(Span<Char>, IFormatProvider, CompositeFormat, Int32, ReadOnlySpan<Object>)
- System.Delegate.Combine(ReadOnlySpan<Delegate>)
- System.String.Concat(ReadOnlySpan<Object>)
- System.String.Concat(ReadOnlySpan<String>)
- System.String.Format(IFormatProvider, CompositeFormat, ReadOnlySpan<Object>)
- System.String.Format(IFormatProvider, String, ReadOnlySpan<Object>)
- System.String.Format(String, ReadOnlySpan<Object>)
- System.String.Join(Char, ReadOnlySpan<Object>)
- System.String.Join(Char, ReadOnlySpan<String>)
- System.String.Join(String, ReadOnlySpan<Object>)
- System.String.Join(String, ReadOnlySpan<String>)
- System.String.Split(ReadOnlySpan<Char>)
- System.CodeDom.Compiler.IndentedTextWriter.Write(String, ReadOnlySpan<Object>)
- System.CodeDom.Compiler.IndentedTextWriter.WriteLine(String, ReadOnlySpan<Object>)
- System.IO.Path.Combine(ReadOnlySpan<String>)
- System.IO.Path.Join(ReadOnlySpan<String>)
- System.IO.StreamWriter.Write(String, ReadOnlySpan<Object>)
- System.IO.StreamWriter.WriteLine(String, ReadOnlySpan<Object>)
- System.IO.TextWriter.Write(String, ReadOnlySpan<Object>)
- System.IO.TextWriter.WriteLine(String, ReadOnlySpan<Object>)
- System.Text.StringBuilder.AppendFormat(IFormatProvider, CompositeFormat, ReadOnlySpan<Object>)
- System.Text.StringBuilder.AppendFormat(IFormatProvider, String, ReadOnlySpan<Object>)
- System.Text.StringBuilder.AppendFormat(String, ReadOnlySpan<Object>)
- System.Text.StringBuilder.AppendJoin(Char, ReadOnlySpan<Object>)
- System.Text.StringBuilder.AppendJoin(Char, ReadOnlySpan<String>)
- System.Text.StringBuilder.AppendJoin(String, ReadOnlySpan<Object>)
- System.Text.StringBuilder.AppendJoin(String, ReadOnlySpan<String>)
- System.Threading.CancellationTokenSource.CreateLinkedTokenSource(ReadOnlySpan<CancellationToken>)
- System.Threading.Tasks.Task.WaitAll(ReadOnlySpan<Task>)
- System.Threading.Tasks.Task.WhenAll(ReadOnlySpan<Task>)
- System.Threading.Tasks.Task.WhenAll<TResult>(ReadOnlySpan<Task<TResult>>)
- System.Threading.Tasks.Task.WhenAny(ReadOnlySpan<Task>)
- System.Threading.Tasks.Task.WhenAny<TResult>(ReadOnlySpan<Task<TResult>>)
- JsonArray(JsonNodeOptions, ReadOnlySpan<JsonNode>)
- JsonArray(ReadOnlySpan<JsonNode>)
- System.Text.Json.Serialization.Metadata.JsonTypeInfoResolver.Combine(ReadOnlySpan<IJsonTypeInfoResolver>)