Alterações significativas no Roslyn após o .NET 6.0.100 até o .NET 7.0.100

Este documento lista as alterações recentes conhecidas no Roslyn após o lançamento geral do .NET 6 (.NET SDK versão 6.0.100) até o lançamento geral do .NET 7 (.NET SDK versão 7.0.100).

Todos os locais de tipos restritos não são permitidos em métodos assíncronos

Introduzido no Visual Studio 2022 versão 17.6p1

Locais de tipos restritos não são permitidos em métodos assíncronos. Mas em versões anteriores, o compilador não notou alguns locais implicitamente declarados. Por exemplo, em foreach ou using afirmações ou desconstruções.
Agora, esses locais implicitamente declarados também não são permitidos.

ref struct RefStruct { public void Dispose() { } }
public class C 
{
    public async Task M() 
    {
        RefStruct local = default; // disallowed
        using (default(RefStruct)) { } // now disallowed too ("error CS9104: A using statement resource of this type cannot be used in async methods or async lambda expressions")
    }
}

Veja https://github.com/dotnet/roslyn/pull/66264

Os ponteiros devem estar sempre em contextos inseguros.

Introduzido no Visual Studio 2022 versão 17.6

Em SDKs anteriores, o compilador ocasionalmente permitia locais onde ponteiros poderiam ser referenciados, sem marcar explicitamente esse local como inseguro. Agora, o unsafe modificador deve estar presente.
Por exemplo, using Alias = List<int*[]>; deve ser alterado para using unsafe Alias = List<int*[]>; para ser legal.
Um uso como void Method(Alias a) ... deve ser alterado para unsafe void Method(Alias a) ....

A regra é incondicional, exceto para using declarações de alias (que não permitiam um unsafe modificador antes do C# 12).
Assim, para using as declarações, a regra só produz efeitos se a versão linguística for escolhida como C# 12 ou superior.

System.TypedReference considerado gerenciado

Introduzido no Visual Studio 2022 versão 17.6

A partir de agora, o tipo System.TypedReference é considerado gerenciado.

unsafe
{
    TypedReference* r = null; // warning: This takes the address of, gets the size of, or declares a pointer to a managed type
    var a = stackalloc TypedReference[1]; // error: Cannot take the address of, get the size of, or declare a pointer to a managed type
}

Erros de segurança de referência não afetam a conversão de uma expressão lambda para um delegado

Introduzido no Visual Studio 2022 versão 17.5

Os erros de segurança de referência relatados em um corpo lambda não afetam mais se a expressão lambda é conversível em um tipo delegado. Essa alteração pode afetar a resolução de sobrecarga.

No exemplo abaixo, a chamada para M(x => ...) é ambígua com o Visual Studio 17.5 porque ambos M(D1) e M(D2) agora são considerados aplicáveis, mesmo que a chamada para F(ref x, ref y) dentro do corpo lambda resulte em uma segurança ref com M(D1) (consulte os exemplos em d1 e d2 para comparação). Anteriormente, a chamada vinculava-se inequivocamente a M(D2) porque a sobrecarga M(D1) era considerada não aplicável.

using System;

ref struct R { }

delegate R D1(R r);
delegate object D2(object o);

class Program
{
    static void M(D1 d1) { }
    static void M(D2 d2) { }

    static void F(ref R x, ref Span<int> y) { }
    static void F(ref object x, ref Span<int> y) { }

    static void Main()
    {
        // error CS0121: ambiguous between: 'M(D1)' and 'M(D2)'
        M(x =>
            {
                Span<int> y = stackalloc int[1];
                F(ref x, ref y);
                return x;
            });

        D1 d1 = x1 =>
            {
                Span<int> y1 = stackalloc int[1];
                F(ref x1, ref y1); // error CS8352: 'y2' may expose referenced variables
                return x1;
            };

        D2 d2 = x2 =>
            {
                Span<int> y2 = stackalloc int[1];
                F(ref x2, ref y2); // ok: F(ref object x, ref Span<int> y)
                return x2;
            };
    }
}

Para contornar as alterações na resolução de sobrecarga, use tipos explícitos para os parâmetros lambda ou para o delegado.

        // ok: M(D2)
        M((object x) =>
            {
                Span<int> y = stackalloc int[1];
                F(ref x, ref y); // ok: F(ref object x, ref Span<int> y)
                return x;
            });

Interpolações de strings brutas no início da linha.

Introduzido no Visual Studio 2022 versão 17.5

No .NET SDK 7.0.100 ou anterior, o seguinte foi erroneamente permitido:

var x = $"""
    Hello
{1 + 1}
    World
    """;

Isso violou a regra de que o conteúdo das linhas (incluindo onde uma interpolação começa) deve começar com o mesmo espaço em branco que a linha final """; . É agora necessário que o texto acima seja escrito como:

var x = $"""
    Hello
    {1 + 1}
    World
    """;

O tipo de delegado inferido para métodos inclui valores de parâmetro padrão e params modificador

Introduzido no Visual Studio 2022 versão 17.5

No .NET SDK 7.0.100 ou anterior, os tipos de delegado inferidos a partir de métodos ignoraram os valores padrão de parâmetros e os modificadores params, conforme demonstrado no código a seguir.

void Method(int i = 0, params int[] xs) { }
var action = Method; // System.Action<int, int[]>
DoAction(action, 1); // ok
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });

No .NET SDK 7.0.200 ou posterior, esses métodos são inferidos como tipos de delegados anónimos sintetizados, com os mesmos valores padrão para parâmetros e modificadores params. Esta alteração pode quebrar o código acima, como demonstrado abaixo:

void Method(int i = 0, params int[] xs) { }
var action = Method; // delegate void <anonymous delegate>(int arg1 = 0, params int[] arg2)
DoAction(action, 1); // error CS1503: Argument 1: cannot convert from '<anonymous delegate>' to 'System.Action<int, int[]>'
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });

Pode saber mais sobre esta alteração na proposta associada.

Para fins de análise de atribuição definida, invocações de funções locais assíncronas não são mais tratadas como aguardadas

Introduzido no Visual Studio 2022 versão 17.5

Para fins de análise de atribuição definida, as invocações de uma função local assíncrona não são mais tratadas como aguardadas e, portanto, a função local não é considerada totalmente executada. Veja a fundamentação em https://github.com/dotnet/roslyn/issues/43697.

O código abaixo agora vai relatar um erro de atribuição definido:

    public async Task M()
    {
        bool a;
        await M1();
        Console.WriteLine(a); // error CS0165: Use of unassigned local variable 'a'  

        async Task M1()
        {
            if ("" == String.Empty)
            {
                throw new Exception();
            }
            else
            {
                a = true;
            }
        }
    }

INoneOperation nós agora são IAttributeOperation nós para atributos.

Introduzido no Visual Studio 2022 versão 17.5, .NET SDK versão 7.0.200

Em versões anteriores do compilador, a árvore de um atributo tinha a sua raiz num nó IOperation. Adicionamos suporte nativo para atributos, o que significa que a raiz da árvore agora é um IAttributeOperation. Alguns analisadores, incluindo versões mais antigas dos analisadores do SDK do .NET, não estão esperando essa forma de árvore e avisarão incorretamente (ou potencialmente falharão em avisar) ao encontrá-la. As soluções alternativas para isso são:

  • Atualize a versão do analisador, se possível. Se estiver usando o SDK do .NET ou versões mais antigas do Microsoft.CodeAnalysis.FxCopAnalyzers, atualize para Microsoft.CodeAnalysis.NetAnalyzers 7.0.0-preview1.22464.1 ou mais recente.
  • Suprima quaisquer falsos positivos dos analisadores até que eles possam ser atualizados com uma versão que leve em conta essa alteração.

Não há suporte para testes de tipo para ref estruturas.

Introduzido no Visual Studio 2022 versão 17.4

Quando um ref tipo struct é usado num operador 'is' ou 'as', em alguns casos o compilador estava relatando anteriormente um aviso incorreto sobre o teste de tipo sempre falhando em tempo de execução, omitindo a verificação de tipo e levando a um comportamento incorreto. Quando era possível haver comportamento incorreto em tempo de execução, o compilador agora produzirá um erro.

ref struct G<T>
{
    public void Test()
    {
        if (this is G<int>) // Will now produce an error, used to be treated as always `false`.
        {

Resultados não utilizados de ref local são desreferenciados.

Introduzido no Visual Studio 2022 versão 17.4

Quando uma ref variável local é referenciada por valor, mas o resultado não é utilizado (por exemplo, ao ser atribuído a um descarte), o resultado era ignorado anteriormente. O compilador irá agora desreferenciar essa variável local, garantindo que quaisquer efeitos secundários sejam observados.

ref int local = Unsafe.NullRef<int>();
_ = local; // Will now produce a `NullReferenceException`

Os tipos não podem ser nomeados scoped

Introduzido no Visual Studio 2022 versão 17.4. A partir do C# 11, os tipos não podem ser nomeados scoped. O compilador relatará um erro em todos esses nomes de tipo. Para contornar isso, o nome do tipo e todos os usos devem ser precedidos por um @.

class scoped {} // Error CS9056
class @scoped {} // No error
ref scoped local; // Error
ref scoped.nested local; // Error
ref @scoped local2; // No error

Foi feito porque scoped agora é um modificador para declarações de variáveis e reservado, seguido por ref em um tipo ref.

Os tipos não podem ser nomeados file

Introduzido no Visual Studio 2022 versão 17.4. A partir do C# 11, os tipos não podem ser nomeados file. O compilador relatará um erro em todos esses nomes de tipo. Para contornar isso, o nome do tipo e todos os usos devem ser precedidos por um @.

class file {} // Error CS9056
class @file {} // No error

Isso foi feito porque file agora é um modificador para declarações de tipo.

Você pode saber mais sobre essa alteração no problema de csharplang associado.

Espaços necessários nas diretivas de #line span

Introduzido no .NET SDK 6.0.400, Visual Studio 2022 versão 17.3.

Quando a diretiva span foi introduzida #line no C# 10, ela não exigia nenhum espaçamento específico.
Por exemplo, isso seria válido: #line(1,2)-(3,4)5"file.cs".

No Visual Studio 17.3, o compilador requer: espaços antes do primeiro parêntese, deslocamento de caractere e nome do ficheiro.
Portanto, o exemplo acima não é analisado, a menos que espaços sejam adicionados: #line (1,2)-(3,4) 5 "file.cs".

Operadores verificados em System.IntPtr e System.UIntPtr

Introduzido no .NET SDK 7.0.100, Visual Studio 2022 versão 17.3.

Quando a plataforma suporta tipos numéricosIntPtr e UIntPtr (conforme indicado pela presença de System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr), os operadores integrados de nint e nuint são aplicados a esses tipos subjacentes. Isso significa que em tais plataformas, IntPtr e UIntPtr têm operadores embutidos checked , que agora podem lançar quando ocorre um estouro.

IntPtr M(IntPtr x, int y)
{
    checked
    {
        return x + y; // may now throw
    }
}

unsafe IntPtr M2(void* ptr)
{
    return checked((IntPtr)ptr); // may now throw
}

As possíveis soluções alternativas são:

  1. Especificar unchecked contexto
  2. Downgrade para uma plataforma/TFM sem tipos numéricos IntPtr/UIntPtr

Além disso, as conversões implícitas entre IntPtr/UIntPtr e outros tipos numéricos são tratadas como conversões padrão nessas plataformas. Isso pode afetar a resolução de sobrecarga em alguns casos.

Essas alterações podem causar uma alteração comportamental se o código do usuário estiver dependendo de exceções de estouro em um contexto não verificado ou se não estiver esperando exceções de estouro em um contexto verificado. Um analisador foi adicionado no 7.0 para ajudar a detetar tais mudanças comportamentais e tomar as medidas apropriadas. O analisador produzirá diagnósticos sobre possíveis alterações comportamentais, que são padronizadas para a gravidade das informações, mas podem ser atualizadas para avisos via editorconfig.

Adição de System.UIntPtr e System.Int32

Introduzido no .NET SDK 7.0.100, Visual Studio 2022 versão 17.3.

Quando a plataforma suporta tipos numéricosIntPtr e UIntPtr tipos (como indicado pela presença de System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr), o operador +(UIntPtr, int) definido em System.UIntPtr não poderá mais ser usado. Em vez disso, adicionar expressões de tipos System.UIntPtr e um System.Int32 resulta em um erro:

UIntPtr M(UIntPtr x, int y)
{
    return x + y; // error: Operator '+' is ambiguous on operands of type 'nuint' and 'int'
}

As possíveis soluções alternativas são:

  1. Use o UIntPtr.Add(UIntPtr, int) método: UIntPtr.Add(x, y)
  2. Aplique uma conversão não marcada para digitar nuint no segundo operando: x + unchecked((nuint)y)

Nome do operador no atributo no método ou função local

Introduzido no .NET SDK 6.0.400, Visual Studio 2022 versão 17.3.

Quando a versão de idioma é C# 11 ou posterior, um nameof operador em um atributo em um método traz os parâmetros de tipo desse método no escopo. O mesmo se aplica às funções locais.
Um nameof operador presente em um atributo de um método, ou nos seus parâmetros de tipo e parâmetros, coloca os parâmetros desse método em evidência. O mesmo se aplica a funções locais, lambdas, delegados e indexadores.

Por exemplo, agora serão erros:

class C
{
  class TParameter
  {
    internal const string Constant = """";
  }
  [MyAttribute(nameof(TParameter.Constant))]
  void M<TParameter>() { }
}
class C
{
  class parameter
  {
    internal const string Constant = """";
  }
  [MyAttribute(nameof(parameter.Constant))]
  void M(int parameter) { }
}

As possíveis soluções alternativas são:

  1. Renomeie o parâmetro de tipo ou outro parâmetro para evitar ocultar o nome do escopo externo.
  2. Use um literal de texto em vez do operador nameof.

Não é possível devolver um parâmetro 'out' como referência

Introduzido no .NET SDK 7.0.100, Visual Studio 2022 versão 17.3.

Com a versão de idioma C# 11 ou posterior, ou com o .NET 7.0 ou posterior, um out parâmetro não pode ser retornado por referência.

static ref T ReturnOutParamByRef<T>(out T t)
{
    t = default;
    return ref t; // error CS8166: Cannot return a parameter by reference 't' because it is not a ref parameter
}

As possíveis soluções alternativas são:

  1. Use System.Diagnostics.CodeAnalysis.UnscopedRefAttribute para marcar a referência como sem escopo.

    static ref T ReturnOutParamByRef<T>([UnscopedRef] out T t)
    {
        t = default;
        return ref t; // ok
    }
    
  2. Altere a assinatura do método para passar o parâmetro por ref.

    static ref T ReturnRefParamByRef<T>(ref T t)
    {
        t = default;
        return ref t; // ok
    }
    

Método de instância em ref struct pode capturar parâmetros ref não escopados

Introduzido no .NET SDK 7.0.100, Visual Studio 2022 versão 17.4.

Com a versão de linguagem C# 11 ou posterior, ou com o .NET 7.0 ou posterior, presume-se que uma invocação de método de instância ref struct capture parâmetros ref ou in sem escopo.

R<int> Use(R<int> r)
{
    int i = 42;
    r.MayCaptureArg(ref i); // error CS8350: may expose variables referenced by parameter 't' outside of their declaration scope
    return r;
}

ref struct R<T>
{
    public void MayCaptureArg(ref T t) { }
}

Uma possível solução alternativa, se o parâmetro ref ou in não for capturado no método de instância ref struct, é declarar o parâmetro como scoped ref ou scoped in.

R<int> Use(R<int> r)
{
    int i = 42;
    r.CannotCaptureArg(ref i); // ok
    return r;
}

ref struct R<T>
{
    public void CannotCaptureArg(scoped ref T t) { }
}

Método ref struct return escape analysis depende de ref escape de argumentos ref

Introduzido no .NET SDK 7.0.100, Visual Studio 2022 versão 17.4.

Com a versão de linguagem C# 11 ou posterior, ou com .NET 7.0 ou posterior, um ref struct retornado de uma invocação de método, seja como um valor de retorno ou dentro de out parâmetros, só é seguro para escapar se todos os ref e in argumentos para a invocação do método forem "ref-safe-to-escape". Os in argumentos podem incluir valores de parâmetros padrão implícitos.

ref struct R { }

static R MayCaptureArg(ref int i) => new R();

static R MayCaptureDefaultArg(in int i = 0) => new R();

static R Create()
{
    int i = 0;
    // error CS8347: Cannot use a result of 'MayCaptureArg(ref int)' because it may expose
    // variables referenced by parameter 'i' outside of their declaration scope
    return MayCaptureArg(ref i);
}

static R CreateDefault()
{
    // error CS8347: Cannot use a result of 'MayCaptureDefaultArg(in int)' because it may expose
    // variables referenced by parameter 'i' outside of their declaration scope
    return MayCaptureDefaultArg();
}

Uma possível solução alternativa, se o argumento ref ou in não for capturado no valor de retorno ref struct, é declarar o parâmetro como scoped ref ou scoped in.

static R CannotCaptureArg(scoped ref int i) => new R();

static R Create()
{
    int i = 0;
    return CannotCaptureArg(ref i); // ok
}

ref ao ref struct argumento considerado não abrangido pelo escopo em __arglist

Introduzido no .NET SDK 7.0.100, Visual Studio 2022 versão 17.4.

Com a versão de linguagem C# 11 ou posterior, ou com o .NET 7.0 ou posterior, um ref para um ref struct tipo é considerado uma referência sem escopo quando passado como um argumento para __arglist.

ref struct R { }

class Program
{
    static void MayCaptureRef(__arglist) { }

    static void Main()
    {
        var r = new R();
        MayCaptureRef(__arglist(ref r)); // error: may expose variables outside of their declaration scope
    }
}

Operador de turno direito não assinado

Introduzido no .NET SDK 6.0.400, Visual Studio 2022 versão 17.3. O idioma adicionou suporte para um operador "Unsigned Right Shift" (>>>). Isso desabilita a capacidade de consumir métodos que implementam operadores "Unsigned Right Shift" definidos pelo usuário como métodos regulares.

Por exemplo, há uma biblioteca existente desenvolvida em alguma linguagem (diferente de VB ou C#) que expõe um operador "Unsigned Right Shift" definido pelo usuário para o tipo C1. O seguinte código costumava compilar com êxito anteriormente:

static C1 Test1(C1 x, int y) => C1.op_UnsignedRightShift(x, y); //error CS0571: 'C1.operator >>>(C1, int)': cannot explicitly call operator or accessor

Uma possível solução é mudar para o operador >>>:

static C1 Test1(C1 x, int y) => x >>> y;

Foreach enumerador como uma estrutura de referência

Introduzido no .NET SDK 6.0.300, Visual Studio 2022 versão 17.2. A foreach usando um tipo de enumerador ref struct relata um erro se a versão do idioma estiver definida como 7.3 ou anterior.

Isso corrige um bug em que o recurso era suportado em compiladores mais recentes visando uma versão do C# antes de seu suporte.

As possíveis soluções alternativas são:

  1. Altere o ref struct tipo para um struct ou class tipo.
  2. Atualize o <LangVersion> elemento para 7.3 ou posterior.

Async foreach prefere padrão baseado em DisposeAsync a uma implementação explícita de interface de IAsyncDisposable.DisposeAsync()

Introduzido no .NET SDK 6.0.300, Visual Studio 2022 versão 17.2. Um async foreach prefere vincular usando um método baseado em DisposeAsync() padrão em vez de IAsyncDisposable.DisposeAsync().

Por exemplo, o DisposeAsync() será escolhido, em vez do IAsyncEnumerator<int>.DisposeAsync() método em AsyncEnumerator:

await foreach (var i in new AsyncEnumerable())
{
}

struct AsyncEnumerable
{
    public AsyncEnumerator GetAsyncEnumerator() => new AsyncEnumerator();
}

struct AsyncEnumerator : IAsyncDisposable
{
    public int Current => 0;
    public async ValueTask<bool> MoveNextAsync()
    {
        await Task.Yield();
        return false;
    }
    public async ValueTask DisposeAsync()
    {
        Console.WriteLine("PICKED");
        await Task.Yield();
    }
    ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
}

Essa alteração corrige uma violação de especificação em que o método público DisposeAsync é visível no tipo declarado, enquanto a implementação de interface explícita só é visível usando uma referência ao tipo de interface.

Para contornar esse erro, remova o método baseado em padrão DisposeAsync do seu tipo.

Não permitir cadeias de caracteres convertidas como um argumento padrão

Introduzido no .NET SDK 6.0.300, Visual Studio 2022 versão 17.2. O compilador C# aceitaria valores de argumento padrão incorretos envolvendo uma conversão de referência de uma constante de cadeia de caracteres e emitiria null como o valor constante em vez do valor padrão especificado na fonte. No Visual Studio 17.2, isso se torna um erro. Ver roslyn#59806.

Essa alteração corrige uma violação de especificação no compilador. Os argumentos padrão devem ser constantes de tempo de compilação. As versões anteriores permitiam o seguinte código:

void M(IEnumerable<char> s = "hello")

A declaração anterior exigia uma conversão de string para IEnumerable<char>. O compilador permitiu essa construção, e emitiria null como o valor do argumento. O código anterior produz um erro de compilador a partir de 17.2.

Para contornar essa alteração, você pode fazer uma das seguintes alterações:

  1. Altere o tipo de parâmetro para que uma conversão não seja necessária.
  2. Altere o valor do argumento padrão para null restaurar o comportamento anterior.

A palavra-chave contextual var como um tipo explícito de retorno de lambda

Introduzido no .NET SDK 6.0.200, Visual Studio 2022 versão 17.1. A palavra-chave contextual var não pode ser usada como um tipo de retorno lambda explícito.

Essa alteração permite possíveis recursos futuros , garantindo que o var permaneça o tipo natural para o tipo de retorno de uma expressão lambda.

Você pode encontrar esse erro se tiver um tipo chamado var e definir uma expressão lambda usando um tipo de retorno explícito de var (o tipo).

using System;

F(var () => default);  // error CS8975: The contextual keyword 'var' cannot be used as an explicit lambda return type
F(@var () => default); // ok
F(() => default);      // ok: return type is inferred from the parameter to F()

static void F(Func<var> f) { }

public class var
{
}

As soluções alternativas incluem as seguintes alterações:

  1. Use @var como o tipo de retorno.
  2. Remova o tipo de retorno explícito para que o compilador determine o tipo de retorno.

Manipuladores de cadeia de caracteres interpolados e inicialização do indexador

Introduzido no .NET SDK 6.0.200, Visual Studio 2022 versão 17.1. Os indexadores que usam um manipulador de cadeia de caracteres interpolado e exigem o recetor como uma entrada para o construtor não podem ser usados em um inicializador de objeto.

Essa alteração não permite um cenário de caso de borda em que os inicializadores do indexador usam um manipulador de cadeia de caracteres interpolado e esse manipulador de cadeia de caracteres interpolado usa o recetor do indexador como um parâmetro do construtor. A razão para essa alteração é que esse cenário pode resultar no acesso a variáveis que ainda não foram inicializadas. Considere este exemplo:

using System.Runtime.CompilerServices;

// error: Interpolated string handler conversions that reference
// the instance being indexed cannot be used in indexer member initializers.
var c = new C { [$""] = 1 }; 

class C
{
    public int this[[InterpolatedStringHandlerArgument("")] CustomHandler c]
    {
        get => ...;
        set => ...;
    }
}

[InterpolatedStringHandler]
class CustomHandler
{
    // The constructor of the string handler takes a "C" instance:
    public CustomHandler(int literalLength, int formattedCount, C c) {}
}

As soluções alternativas incluem as seguintes alterações:

  1. Remova o tipo de recetor do manipulador de cadeia de caracteres interpolada.
  2. Altere o argumento do indexador para que seja um string

ref, readonly ref, in, out não são permitidos como parâmetros ou retornos em métodos que têm apenas chamadores não-gerenciados

Introduzido no .NET SDK 6.0.200, Visual Studio 2022 versão 17.1.ref/ref readonly/in/out não podem ser utilizados no retorno/parâmetros de um método atribuído com UnmanagedCallersOnly.

Esta alteração é uma correção de bug. Os valores e parâmetros de retorno não são "blittable". Passar argumentos ou retornar valores por referência pode causar um comportamento indefinido. Nenhuma das seguintes declarações será compilada:

using System.Runtime.InteropServices;
[UnmanagedCallersOnly]
static ref int M1() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static ref readonly int M2() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M3(ref int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M4(in int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M5(out int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

A solução alternativa é remover o modificador por referência.

Comprimento, Contagem assumida como não negativa em padrões

Introduzido no .NET SDK 6.0.200, Visual Studio 2022 versão 17.1.Length e Count as propriedades em tipos contáveis e indexáveis são consideradas não negativas para fins de subsunção e análise exaustiva de padrões e interruptores. Esses tipos podem ser usados com indexador de índice implícito e padrões de lista.

As Length propriedades e Count , mesmo que digitadas como int, são assumidas como não negativas ao analisar padrões. Considere este método de exemplo:

string SampleSizeMessage<T>(IList<T> samples)
{
    return samples switch
    {
        // This switch arm prevents a warning before 17.1, but will never happen in practice.
        // Starting with 17.1, this switch arm produces a compiler error.
        // Removing it won't introduce a warning.
        { Count: < 0 }    => throw new InvalidOperationException(),
        { Count:  0 }     => "Empty collection",
        { Count: < 5 }    => "Too small",
        { Count: < 20 }   => "reasonable for the first pass",
        { Count: < 100 }  => "reasonable",
        { Count: >= 100 } => "fine",
    };
}

void M(int[] i)
{
    if (i is { Length: -1 }) {} // error: impossible under assumption of non-negative length
}

Antes da versão 17.1, o primeiro ramo de mudança, verificar se Count é negativo, era necessário para evitar um alerta de que todos os valores possíveis não estavam considerados. A partir da versão 17.1, o primeiro braço do switch gera um erro no compilador. A solução alternativa é remover os braços de interruptor adicionados para os casos inválidos.

Essa alteração foi feita como parte da adição de padrões de lista. As regras de processamento são mais consistentes se cada uso de uma Length ou Count propriedade em uma coleção for considerado não negativo. Você pode ler mais detalhes sobre a mudança na questão do design da linguagem.

A solução é remover os braços do interruptor com condições inalcançáveis.

Adicionar inicializadores de campo a uma struct requer um construtor explicitamente declarado

Introduzido no .NET SDK 6.0.200, Visual Studio 2022 versão 17.1.struct As declarações de tipo com inicializadores de campo devem incluir um construtor explicitamente declarado. Além disso, todos os campos devem ser definitivamente atribuídos em struct construtores de instância que não têm um : this() inicializador, de modo que quaisquer campos que não tenham sido atribuídos anteriormente devem ser atribuídos a partir do construtor adicionado ou dos inicializadores de campo. Veja dotnet/csharplang#5552, dotnet/roslyn#58581.

Há duas maneiras de inicializar uma variável com seu valor padrão em C#: new() e default. Para as classes, a diferença é evidente, pois new cria uma nova instância e default retorna null. A diferença é mais sutil para structs, já que para default, structs retornam uma instância com cada campo/propriedade definido como seu próprio padrão. Adicionamos inicializadores de campo para structs em C# 10. Os inicializadores de campo são executados somente quando um construtor explicitamente declarado é executado. Significativamente, eles não são executados quando você usa default ou cria uma matriz de qualquer struct tipo.

Em 17.0, se houver inicializadores de campo, mas nenhum construtor declarado, um construtor sem parâmetros é sintetizado que executa inicializadores de campo. No entanto, isso significa que adicionar ou remover uma declaração de construtor pode afetar se um construtor sem parâmetros é sintetizado e, como resultado, pode alterar o comportamento do new().

Para resolver o problema, no .NET SDK 6.0.200 (VS 17.1) o compilador não sintetiza mais um construtor sem parâmetros. Se um struct contém inicializadores de campo e nenhum construtor explícito, o compilador gera um erro. Se um struct tem inicializadores de campo, ele deve declarar um construtor, porque caso contrário, os inicializadores de campo nunca são executados.

Além disso, todos os campos que não têm inicializadores de campo devem ser atribuídos em cada struct construtor, a menos que o construtor tenha um : this() inicializador.

Por exemplo:

struct S // error CS8983: A 'struct' with field initializers must include an explicitly declared constructor.
{
    int X = 1; 
    int Y;
}

A solução alternativa é a declaração de um construtor. A menos que os campos não tenham sido atribuídos anteriormente, esse construtor pode, e muitas vezes será, um construtor vazio sem parâmetros:

struct S
{
    int X = 1;
    int Y;

    public S() { Y = 0; } // ok
}

Os especificadores de formato não podem conter chaves

Introduzido no .NET SDK 6.0.200, Visual Studio 2022 versão 17.1. Os especificadores de formato em cadeias de caracteres interpoladas não podem conter chaves (ou {}). Em versões anteriores, {{ foi interpretado como um { carácter escapado, e }} foi interpretado como um } carácter escapado no especificador de formato. Agora, o primeiro } char em um especificador de formato termina a interpolação, e qualquer { char é um erro.

Isso torna o processamento de cadeia de caracteres interpolado consistente com o processamento para System.String.Format:

using System;
Console.WriteLine($"{{{12:X}}}");
//prints now: "{C}" - not "{X}}"

X é o formato para hexadecimal maiúsculo e C é o valor hexadecimal para 12.

A solução alternativa é remover as chaves extras na string de formato.

Você pode saber mais sobre essa mudança no problema roslyn associado.

Os tipos não podem ser nomeados required

Introduzido no Visual Studio 2022 versão 17.3. A partir do C# 11, os tipos não podem ser nomeados required. O compilador relatará um erro em todos esses nomes de tipo. Para contornar isso, o nome do tipo e todos os usos devem ser precedidos por um @.

class required {} // Error CS9029
class @required {} // No error

Isto foi realizado porque required agora é um modificador de membro para propriedades e campos.

Você pode saber mais sobre essa alteração no problema de csharplang associado.