Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este documento lista as alterações recentes conhecidas no Roslyn após o lançamento geral do .NET 9 (.NET SDK versão 9.0.100) até o lançamento geral do .NET 10 (.NET SDK versão 10.0.100).
scoped numa lista de parâmetros lambda é sempre considerado um modificador.
Introduzido no Visual Studio 2022 versão 17.13
O C# 14 introduz a capacidade de escrever um lambda com modificadores de parâmetro, sem precisar especificar um tipo de parâmetro: Parâmetros lambda simples com modificadores
Como parte deste trabalho, uma alteração significativa foi aceita, onde scoped será sempre tratado como um modificador em um parâmetro lambda, mesmo que anteriormente pudesse ter sido aceito como um nome de tipo. Por exemplo:
var v = (scoped scoped s) => { ... };
ref struct @scoped { }
No C# 14, isso será um erro, pois ambos os tokens scoped são tratados como modificadores. A solução alternativa é usar @ na posição do nome do tipo, assim:
var v = (scoped @scoped s) => { ... };
ref struct @scoped { }
As sobrecargas Span<T> e ReadOnlySpan<T> são aplicáveis em mais cenários no C# 14 e em versões mais recentes.
Introduzido no Visual Studio 2022 versão 17.13
C# 14 introduz novas conversões de 'span' integradas e regras de inferência de tipo. Isso significa que diferentes sobrecargas podem ser escolhidas em comparação com o C# 13 e, às vezes, um erro em tempo de compilação devido a uma ambiguidade pode ser gerado porque uma nova sobrecarga é aplicável, mas não há uma única melhor sobrecarga.
O exemplo a seguir mostra algumas ambiguidades e possíveis soluções alternativas.
Observe que outra solução alternativa é que os autores da API usem o OverloadResolutionPriorityAttribute.
var x = new long[] { 1 };
Assert.Equal([2], x); // previously Assert.Equal<T>(T[], T[]), now ambiguous with Assert.Equal<T>(ReadOnlySpan<T>, Span<T>)
Assert.Equal([2], x.AsSpan()); // workaround
var y = new int[] { 1, 2 };
var s = new ArraySegment<int>(y, 1, 1);
Assert.Equal(y, s); // previously Assert.Equal<T>(T, T), now ambiguous with Assert.Equal<T>(Span<T>, Span<T>)
Assert.Equal(y.AsSpan(), s); // workaround
Uma sobrecarga de Span<T> pode ser escolhida em C# 14, onde uma sobrecarga que usa uma interface implementada por T[] (como IEnumerable<T>) foi escolhida em C# 13, e isso pode levar a um ArrayTypeMismatchException durante a execução se for usado com uma matriz covariante.
string[] s = new[] { "a" };
object[] o = s; // array variance
C.R(o); // wrote 1 previously, now crashes in Span<T> constructor with ArrayTypeMismatchException
C.R(o.AsEnumerable()); // workaround
static class C
{
public static void R<T>(IEnumerable<T> e) => Console.Write(1);
public static void R<T>(Span<T> s) => Console.Write(2);
// another workaround:
public static void R<T>(ReadOnlySpan<T> s) => Console.Write(3);
}
Por esse motivo, ReadOnlySpan<T> geralmente é preferido em relação ao Span<T> pela resolução de sobrecarga em C# 14.
Em alguns casos, isso pode causar falhas na compilação, por exemplo, quando há sobrecargas para Span<T> e ReadOnlySpan<T>, ambos aceitando e retornando o mesmo tipo de intervalo.
double[] x = new double[0];
Span<ulong> y = MemoryMarshal.Cast<double, ulong>(x); // previously worked, now compilation error
Span<ulong> z = MemoryMarshal.Cast<double, ulong>(x.AsSpan()); // workaround
static class MemoryMarshal
{
public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span) => default;
public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span) => default;
}
Enumerable.Reverse
Ao usar C# 14 ou mais recente e direcionar um .NET mais antigo ou net10.0 .NET Framework com System.Memory referência, há uma alteração significativa com Enumerable.Reverse e matrizes.
Atenção
Isso afeta apenas os clientes que usam C# 14 e direcionam o .NET antes do net10.0, que é uma configuração sem suporte.
int[] x = new[] { 1, 2, 3 };
var y = x.Reverse(); // previously Enumerable.Reverse, now MemoryExtensions.Reverse
No net10.0, existe o Enumerable.Reverse(this T[]) que assume precedência, evitando assim a interrupção.
Caso contrário, MemoryExtensions.Reverse(this Span<T>) é resolvido, o que tem uma semântica diferente de Enumerable.Reverse(this IEnumerable<T>) (que costumava ser resolvida nas versões C# 13 e anteriores).
Especificamente, a extensão Span faz a inversão in situ e retorna void.
Como solução alternativa, pode-se definir os seus próprios Enumerable.Reverse(this T[]) ou usar Enumerable.Reverse explicitamente.
int[] x = new[] { 1, 2, 3 };
var y = Enumerable.Reverse(x); // instead of 'x.Reverse();'
Diagnósticos agora relatados para o método de descarte baseado em padrões em foreach
Introduzido no Visual Studio 2022 versão 17.13
Por exemplo, um método de DisposeAsync obsoleto é agora relatado em await foreach.
await foreach (var i in new C()) { } // 'C.AsyncEnumerator.DisposeAsync()' is obsolete
class C
{
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
public sealed class AsyncEnumerator : System.IAsyncDisposable
{
public int Current { get => throw null; }
public Task<bool> MoveNextAsync() => throw null;
[System.Obsolete]
public ValueTask DisposeAsync() => throw null;
}
}
Defina o estado do objeto enumerador como "depois" durante o descarte
Introduzido no Visual Studio 2022 versão 17.13
A máquina de estados para enumeradores permitiu incorretamente retomar a execução depois de o enumerador ter sido descartado.
Agora, MoveNext() em um enumerador descartado retorna corretamente false sem executar mais nenhum código de usuário.
var enumerator = C.GetEnumerator();
Console.Write(enumerator.MoveNext()); // prints True
Console.Write(enumerator.Current); // prints 1
enumerator.Dispose();
Console.Write(enumerator.MoveNext()); // now prints False
class C
{
public static IEnumerator<int> GetEnumerator()
{
yield return 1;
Console.Write("not executed after disposal")
yield return 2;
}
}
Avisar sobre padrão redundante em padrões básicos or
Introduzido no Visual Studio 2022 versão 17.13
Em um padrão disjuntivo or como is not null or 42 ou is not int or string o segundo padrão é redundante e provavelmente resulta de uma má compreensão da ordem de precedência dos combinadores de padrões not e or.
O compilador fornece um aviso em casos comuns deste erro:
_ = o is not null or 42; // warning: pattern "42" is redundant
_ = o is not int or string; // warning: pattern "string" is redundant
É provável que o usuário quis dizer is not (null or 42) ou is not (int or string) em vez disso.
UnscopedRefAttribute não pode ser usado com regras de segurança antigas
Introduzido no Visual Studio 2022 versão 17.13
O UnscopedRefAttribute afetou de forma não intencional o código compilado por novas versões do compilador Roslyn, mesmo quando o código estava a ser compilado no contexto das regras de segurança ref anteriores (ou seja, destinado ao C# 10 ou anterior com net6.0 ou anterior).
No entanto, o atributo não deve ter um efeito nesse contexto, e isso agora está fixo.
O código que anteriormente não relatava nenhum erro no C# 10 ou anterior com net6.0 ou anterior agora pode falhar ao compilar:
using System.Diagnostics.CodeAnalysis;
struct S
{
public int F;
// previously allowed in C# 10 with net6.0
// now fails with the same error as if the [UnscopedRef] wasn't there:
// error CS8170: Struct members cannot return 'this' or other instance members by reference
[UnscopedRef] public ref int Ref() => ref F;
}
Para evitar mal-entendidos (pensar que o atributo tem um efeito, mas na verdade não tem porque o seu código é compilado com as regras de segurança ref anteriores), é emitido um aviso quando o atributo é usado em C# 10 ou anterior com net6.0 ou anterior.
using System.Diagnostics.CodeAnalysis;
struct S
{
// both are errors in C# 10 with net6.0:
// warning CS9269: UnscopedRefAttribute is only valid in C# 11 or later or when targeting net7.0 or later.
[UnscopedRef] public ref int Ref() => throw null!;
public static void M([UnscopedRef] ref int x) { }
}
Microsoft.CodeAnalysis.EmbeddedAttribute é validado na declaração
Introduzido no Visual Studio 2022 versão 17.13
O compilador agora valida a forma do Microsoft.CodeAnalysis.EmbeddedAttribute quando declarado no código-fonte. Anteriormente, o compilador permitia declarações definidas pelo usuário desse atributo, mas apenas quando não precisava gerar uma. Validamos agora que:
- Deve ser interno
- Deve ser uma classe
- Deve ser selado
- Deve ser não-estático
- Ele deve ter um construtor interno ou público sem parâmetros
- Ele deve herdar de System.Attribute.
- Ele deve ser permitido em qualquer declaração de tipo (classe, struct, interface, enum ou delegado)
namespace Microsoft.CodeAnalysis;
// Previously, sometimes allowed. Now, CS9271
public class EmbeddedAttribute : Attribute {}
A expressão field num acessador de propriedade refere-se ao campo de suporte sintetizado
Introduzido no Visual Studio 2022 versão 17.12
A expressão field, quando usada dentro de um acessador de propriedades, refere-se a um campo de apoio sintetizado para a propriedade.
O aviso CS9258 é emitido quando o identificador estaria associado a um símbolo diferente com a versão do idioma 13 ou anterior.
Para evitar gerar um campo de suporte sintetizado e para se referir ao membro existente, use 'this.field' ou '@field' em vez disso.
Como alternativa, renomeie o membro existente e a referência a esse membro para evitar um conflito com field.
class MyClass
{
private int field = 0;
public object Property
{
get
{
// warning CS9258: The 'field' keyword binds to a synthesized backing field for the property.
// To avoid generating a synthesized backing field, and to refer to the existing member,
// use 'this.field' or '@field' instead.
return field;
}
}
}
Variável chamada field não é permitida num acessor de propriedade
Introduzido no Visual Studio 2022 versão 17.14
A expressão field, quando usada dentro de um acessador de propriedades, refere-se a um campo de apoio sintetizado para a propriedade.
O erro CS9272 é relatado quando uma variável local, ou um parâmetro de uma função aninhada, com o nome field, é declarado num acessor de propriedade.
Para evitar o erro, renomeie a variável ou use @field na declaração.
class MyClass
{
public object Property
{
get
{
// error CS9272: 'field' is a keyword within a property accessor.
// Rename the variable or use the identifier '@field' instead.
int field = 0;
return @field;
}
}
}
Os tipos record e record struct não podem definir membros de tipos de ponteiros, mesmo quando fornecem as suas próprias implementações de Equals.
Introduzido no Visual Studio 2022 versão 17.14
A especificação para os tipos record class e record struct indicou que todos os tipos de ponteiro não são permitidos como campos de instância.
No entanto, isto não foi aplicado corretamente quando o tipo record class ou record struct definiu a sua própria implementação Equals.
O compilador agora proíbe corretamente isso.
unsafe record struct R(
int* P // Previously fine, now CS8908
)
{
public bool Equals(R other) => true;
}
A emissão de executáveis somente de metadados requer um ponto de entrada
Introduzido no Visual Studio 2022 versão 17.14
Anteriormente, o ponto de entrada era involuntariamente desconfigurado ao emitir executáveis no modo somente metadados (também conhecido como assemblies ref). Isso agora foi corrigido, mas também significa que um ponto de entrada ausente é um erro de compilação:
// previously successful, now fails:
CSharpCompilation.Create("test").Emit(new MemoryStream(),
options: EmitOptions.Default.WithEmitMetadataOnly(true))
CSharpCompilation.Create("test",
// workaround - mark as DLL instead of EXE (the default):
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.Emit(new MemoryStream(),
options: EmitOptions.Default.WithEmitMetadataOnly(true))
Da mesma forma, isso pode ser observado ao usar o argumento de linha de comando /refonly ou a propriedade ProduceOnlyReferenceAssembly MSBuild.
partial não pode ser um tipo de método de retorno
Introduzido no Visual Studio 2022 versão 17.14
O recurso de linguagem de eventos parciais e construtores permite que o partial modificador seja usado em mais lugares e, portanto, não pode ser um tipo de retorno, a menos que seja escapado:
class C
{
partial F() => new partial(); // previously worked
@partial F() => new partial(); // workaround
}
class partial { }
extension tratada como uma palavra-chave contextual
Introduzido no Visual Studio 2022 versão 17.14. Começando com C# 14, a extension palavra-chave serve um propósito especial ao denotar contêineres de extensão.
Isso altera a forma como o compilador interpreta determinadas construções de código.
Se você precisar usar "extensão" como um identificador em vez de uma palavra-chave, escape-o com o prefixo @ : @extension. Isso diz ao compilador para tratá-lo como um identificador regular em vez de uma palavra-chave.
O compilador analisará isso como um contêiner de extensão em vez de um construtor.
class @extension
{
extension(object o) { } // parsed as an extension container
}
O compilador falhará ao analisar isso como um método com tipo de retorno extension.
class @extension
{
extension M() { } // will not compile
}
Introduzido no Visual Studio 2026 versão 18.0. O identificador "extensão" não pode ser usado como um nome de tipo, portanto, o seguinte não será compilado:
using extension = ...; // alias may not be named "extension"
class extension { } // type may not be named "extension"
class C<extension> { } // type parameter may not be named "extension"
Propriedades parciais e eventos agora são implicitamente virtuais e públicos
Introduzido no Visual Studio 2026 versão 18.0 preview 1
Corrigimos uma inconsistência em que as propriedades e eventos parciais da interface não seriam implícitos virtual e public ao contrário dos equivalentes não parciais.
Esta inconsistência é, no entanto, preservada para métodos de interface parcial para evitar uma alteração de quebra maior.
Observe que o Visual Basic e outras linguagens que não suportam membros de interface padrão começarão a exigir a implementação implícita de membros da interface virtual partial .
Para manter o comportamento anterior, marque explicitamente os membros da interface como partial (caso não tenham nenhum modificador de acessibilidade) e private (caso não tenham o modificador sealed, que implica private, e não tenham já o modificador sealed ou virtual).
System.Console.Write(((I)new C()).P); // wrote 1 previously, writes 2 now
partial interface I
{
public partial int P { get; }
public partial int P => 1; // implicitly virtual now
}
class C : I
{
public int P => 2; // implements I.P
}
System.Console.Write(((I)new C()).P); // inaccessible previously, writes 1 now
partial interface I
{
partial int P { get; } // implicitly public now
partial int P => 1;
}
class C : I;
Ausências ParamCollectionAttribute são relatadas em um maior número de casos
Introduzido no Visual Studio 2026 versão 18.0
Se estiver a compilar um .netmodule (observe que isto não se aplica a compilações DLL/EXE normais) e tiver uma função lambda ou local com um parâmetro de params coleção, e o ParamCollectionAttribute não for encontrado, será relatado um erro de compilação (porque o atributo agora deve ser emitido no método sintetizado, mas o tipo de atributo em si não é sintetizado pelo compilador em um .netmodule).
Você mesmo pode contornar isso definindo o atributo.
using System;
using System.Collections.Generic;
class C
{
void M()
{
Func<IList<int>, int> lam = (params IList<int> xs) => xs.Count; // error if ParamCollectionAttribute does not exist
lam([1, 2, 3]);
int func(params IList<int> xs) => xs.Count; // error if ParamCollectionAttribute does not exist
func(4, 5, 6);
}
}
Roslyn breaking changes