Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este documento lista alterações disruptivas conhecidas no Roslyn após a versão geral do .NET 10 (SDK .NET versão 10.0.100) até à versão geral do .NET 11 (SDK .NET versão 11.0.100).
O safe-context de uma expressão de coleção do tipo Span/ReadOnlySpan é agora declaration-block
Introduzido em Visual Studio 2026 versão 18.3
O compilador C# fez uma alteração crítica para aderir corretamente às regras de segurança das referências na especificação das expressões de coleção. Especificamente, a seguinte cláusula:
- Se o tipo de destino for um tipo de segmento
System.Span<T>ouSystem.ReadOnlySpan<T>, o contexto seguro da expressão de coleção será o bloco de declaração .
Anteriormente, o compilador usava o safe-context function-member nesta situação. Agora fizemos uma alteração para usar o bloco de declaração conforme a especificação. Isto pode causar o surgimento de novos erros no código existente, como no cenário abaixo:
scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
foreach (var x in new[] { 1, 2 })
{
Span<int> items = [x];
if (x == 1)
items1 = items; // previously allowed, now an error
if (x == 2)
items2 = items; // previously allowed, now an error
}
Se o seu código for afetado por esta alteração, considere usar um tipo de array para as expressões de coleção relevantes:
scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
foreach (var x in new[] { 1, 2 })
{
int[] items = [x];
if (x == 1)
items1 = items; // ok, using 'int[]' conversion to 'Span<int>'
if (x == 2)
items2 = items; // ok
}
Alternativamente, mova a expressão de coleção para um âmbito onde a atribuição seja permitida:
scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
Span<int> items = [0];
foreach (var x in new[] { 1, 2 })
{
items[0] = x;
if (x == 1)
items1 = items; // ok
if (x == 2)
items2 = items; // ok
}
Ver também https://github.com/dotnet/csharplang/issues/9750.
Cenários que exigem que o compilador sintetize um ref readonly delegado que retorna agora exigem disponibilidade do System.Runtime.InteropServices.InAttribute tipo.
O compilador C# fez uma alteração decisiva para emitir corretamente metadados para ref readonlyos delegados sintetizados que retornavam
Isto pode causar o aparecimento de um "erro CS0518: O tipo pré-definido 'System.Runtime.InteropServices.InAttribute' não está definido ou importado" no código existente, como nos cenários abaixo:
var d = this.MethodWithRefReadonlyReturn;
var d = ref readonly int () => ref x;
Se o teu código for afetado por esta alteração de rutura, considera adicionar uma referência a um assembly que define System.Runtime.InteropServices.InAttribute no teu projeto.
Os cenários que ref readonly utilizam funções locais agora exigem disponibilidade do System.Runtime.InteropServices.InAttribute tipo.
Introduzido no Visual Studio 2026, versão 18.3
O compilador C# fez uma alteração significativa para poder emitir corretamente metadados para funções locais de retorno ref readonly.
Isto pode causar que apareça um "erro CS0518: O tipo pré-definido 'System.Runtime.InteropServices.InAttribute' não está definido ou importado" no código existente, como no cenário abaixo:
void Method()
{
...
ref readonly int local() => ref x;
...
}
Se o teu código for afetado por esta alteração de rutura, considera adicionar uma referência a um assembly que define System.Runtime.InteropServices.InAttribute no teu projeto.
A avaliação dinâmica dos operadores &&/|| não é permitida com o operando esquerdo tipado estaticamente como uma interface.
Introduzido no Visual Studio 2026 versão 18.3
O compilador C# agora gera um erro quando um tipo de interface é usado como operando esquerdo de um operador lógico && ou || com um operando dynamic à direita.
Anteriormente, o código compilava para um tipo de interface com true/false operadores, mas falhava em tempo de execução com a RuntimeBinderException porque o binder de runtime não pode invocar operadores definidos em interfaces.
Esta alteração previne um erro em tempo de execução ao sinalizá-lo em tempo de compilação. A mensagem de erro é:
erro CS7083: A expressão deve ser implicitamente convertível para booleano ou o seu tipo 'I1' não deve ser uma interface e deve definir o operador 'falso'.
interface I1
{
static bool operator true(I1 x) => false;
static bool operator false(I1 x) => false;
}
class C1 : I1
{
public static C1 operator &(C1 x, C1 y) => x;
public static bool operator true(C1 x) => false;
public static bool operator false(C1 x) => false;
}
void M()
{
I1 x = new C1();
dynamic y = new C1();
_ = x && y; // error CS7083: Expression must be implicitly convertible to Boolean or its type 'I1' must not be an interface and must define operator 'false'.
}
Se o seu código for afetado por esta alteração disruptiva, considere alterar o tipo estático do operando esquerdo de um tipo de interface para um tipo de classe concreta, ou para o tipo dynamic.
void M()
{
I1 x = new C1();
dynamic y = new C1();
_ = (C1)x && y; // Valid - uses operators defined on C1
_ = (dynamic)x && y; // Valid - uses operators defined on C1
}
Ver também https://github.com/dotnet/roslyn/issues/80954.
nameof(this.) em atributos não é permitido
Introduzido no Visual Studio 2026 versão 18.3 e .NET 10.0.200
O uso de this ou base palavra-chave dentro de um atributo nameof foi anteriormente permitido involuntariamente no Roslyn desde C# 12 e agora está devidamente proibido para alinhar com a especificação da linguagem.
Esta alteração pode ser mitigada removendo this. e acedendo ao membro sem o qualificador.
Ver também https://github.com/dotnet/roslyn/issues/82251.
class C
{
string P;
[System.Obsolete(nameof(this.P))] // now disallowed
[System.Obsolete(nameof(P))] // workaround
void M() { }
}
Análise de 'com' dentro de um braço de expressão de troca
Introduzido em 2026 na versão 18.4 do Visual Studio
Ver https://github.com/dotnet/roslyn/issues/81837 e https://github.com/dotnet/roslyn/pull/81863
Anteriormente, ao ver o seguinte, o compilador tratava (X.Y)when como uma expressão de cast. Especificamente, convertendo o identificador contextual when para (X.Y):
x switch
{
(X.Y) when
}
Isto era indesejável e significava que uma simples when verificação do padrão (como (X.Y) when a > b =>) não faria a análise adequada. Agora, isto é tratado como um padrão (X.Y) constante seguido de um when clause.
with()como elemento de expressão de coleção, é tratado como argumentos de construção de coleções
Introduzido no Visual Studio 2026 versão 18.4
with(...) quando usado como elemento numa expressão de coleção, e quando o LangVersion está definido como 15 ou superior, é vinculado como argumentos passados ao construtor ou método de fábrica usado para criar a coleção, em vez de como uma expressão de invocação de um método chamado with.
Para ligar a um método chamado with, use @with em vez disso.
object x, y, z = ...;
object[] items;
items = [with(x, y), z]; // C# 14: call to with() method; C# 15: error args not supported for object[]
items = [@with(x, y), z]; // call to with() method
object with(object a, object b) { ... }
Os tipos de ponteiros já não requerem um contexto inseguro
Introduzido em Visual Studio 2026 versão 18.7
Numa versão futura de C# (atualmente em langversion:preview), os tipos de ponteiro (por exemplo, int*, delegate*<void>) já não requerem um contexto inseguro.
Apenas as operações de indireção do ponteiro (desreferência, acesso de membros via ->, acesso a elementos, etc.) exigem insegurança.
Isto faz parte da funcionalidade de evolução insegura .
Como os tipos de ponteiros são agora permitidos em contextos seguros, a resolução por sobrecarga pode considerar candidatos que anteriormente estavam excluídos. Isto pode causar novos erros de ambiguidade:
using System;
class Program
{
static void Main()
{
M(x => { }); // C# 14: prints "2"; C# preview: error CS0121 (ambiguous)
}
static void M(F1 f) { Console.WriteLine(1); }
static void M(F2 f) { Console.WriteLine(2); }
}
unsafe delegate void F1(int* x);
delegate void F2(int x);
Anteriormente, a lambda x => { } não podia converter para F1 num contexto seguro porque int* exigia um contexto inseguro, sendo assim apenas M(F2) aplicável.
Agora que int* é legal em contextos seguros, a lambda é convertível para ambos os delegados e produz um erro de ambiguidade.
Se o seu código for afetado pela alteração de ambiguidade, adicione tipos de parâmetros explícitos ao lambda para desambiguar:
M((int x) => { }); // Resolves to M(F2)
Um stackalloc não inicializado dentro de SkipLocalsInit requer um contexto inseguro
Numa versão futura em C# (atualmente em langversion:preview), uma stackalloc expressão sem inicializador dentro de um método marcado com [SkipLocalsInit] agora requer um contexto inseguro, mesmo quando o tipo alvo é Span<T>.
Isto deve-se ao facto SkipLocalsInit de impedir a inicialização zero da memória alocada, tornando possível ler dados não inicializados – uma preocupação de segurança de memória.
Isto faz parte da funcionalidade de evolução insegura .
[System.Runtime.CompilerServices.SkipLocalsInit]
void M()
{
Span<int> a = stackalloc int[5]; // previously ok, now error CS9361
Span<int> b = stackalloc int[] { 1, 2 }; // ok (has initializer)
Span<int> c = stackalloc int[2] { 1, 2 }; // ok (has initializer)
}
Se o teu código for afetado, podes de qualquer forma:
- Adicione um
unsafebloco à volta dostackalloc:[SkipLocalsInit] void M() { Span<int> a; unsafe { a = stackalloc int[5]; } } - Ou fornecer um inicializador para que a memória fique totalmente inicializada:
[SkipLocalsInit] void M() { Span<int> a = stackalloc int[5] { 0, 0, 0, 0, 0 }; }
Roslyn breaking changes