A resolução de sobrecarga em C# prefere sobrecargas do tipo span params
O C# 13 adicionou suporte para parâmetros params
declarados com tipos de coleção que não sejam arrays. Em particular, há suporte para params ReadOnlySpan<T>
e params Span<T>
e a resolução de sobrecarga prefere um tipo de intervalo params
em vez de um tipo de matriz params
quando ambos são aplicáveis.
O .NET 9 adicionou params
sobrecargas de span para vários métodos nas principais bibliotecas do .NET. Esses métodos tinham sobrecargas pré-existentes que aceitavam matrizes params
. Quando você recompilar o código com chamadas existentes desses métodos em que os argumentos são passados de forma expandida, o compilador agora ficará vinculado à sobrecarga de span params
.
O novo vínculo leva a uma possível alteração significativa para chamadas existentes a essas sobrecargas em Expression expressões lambda que não dão suporte a instâncias ref struct
. Nesses casos, o compilador C# 13 relata um erro ao se vincular à sobrecarga do span params
.
Por exemplo, considere string.Join()
:
using System;
using System.Linq.Expressions;
Expression<Func<string, string, string>> join =
(x, y) => string.Join("", x, y);
Quando compilada com o .NET 8, a chamada se associa a Join(String, String[]), sem erros.
Quando compilada com C# 13 e .NET 9, a chamada se associa a Join(String, ReadOnlySpan<String>)e, como a chamada está dentro de uma árvore de expressão , os seguintes erros são relatados:
erro CS8640: a árvore de expressão não pode conter o valor do struct ref ou do tipo restrito 'ReadOnlySpan'. erro CS9226: Uma árvore de expressão pode não conter uma forma expandida de parâmetros não matrizes
Versão introduzida
.NET 9
Comportamento anterior
Antes do C# 13, os parâmetros de params
eram limitados apenas a tipos de matriz. As chamadas a esses métodos na forma expandida resultaram apenas em instâncias de matriz implícita, que têm suporte em expressões lambda Expression.
Novo comportamento
Com o C# 13 e o .NET 9, para métodos com sobrecargas que levam tipos de matrizes params
e tipos de span params
, a resolução de sobrecarga prefere a sobrecarga de span params
. Essa chamada cria uma instância de intervalo implícita no ponto de chamada. Para chamadas dentro de expressões lambda Expression, a instância de span implícita de ref struct
é relatada como erro do compilador.
Tipo de alteração interruptiva
Essa alteração pode afetar a compatibilidade da origem.
Motivo da alteração
As sobrecargas do novo método foram adicionadas por questões de desempenho. O suporte a span params
permite que o compilador evite uma alocação para o argumento params
no ponto de chamada.
Ação recomendada
Se o código for afetado, a solução alternativa recomendada será chamar o método com uma matriz explícita para que a chamada se associe à sobrecarga da matriz params
.
Para o exemplo anterior, use new string[] { ... }
:
Expression<Func<string, string, string>> join =
(x, y) => string.Join("", new string[] { x, y });
APIs afetadas
- 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>)