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

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

Os modificadores ref de argumentos dinâmicos devem ser compatíveis com modificadores ref de parâmetros correspondentes

Introduzido no Visual Studio 2022 versão 17.10

Os modificadores ref de argumentos dinâmicos devem ser compatíveis com modificadores ref de parâmetros correspondentes em tempo de compilação. Isso pode fazer com que uma resolução de sobrecarga envolvendo argumentos dinâmicos falhe em tempo de compilação em vez de tempo de execução.

Anteriormente, uma incompatibilidade era permitida em tempo de compilação, atrasando a falha de resolução de sobrecarga para o tempo de execução.

Por exemplo, o seguinte código costumava compilar sem erro, mas resultava numa exceção: "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: A melhor correspondência de método sobrecarregado para 'C.f(ref object)' tem alguns argumentos inválidos". Vai resultar num erro de compilação agora.

public class C
{
    public void f(ref dynamic a) 
    {
    }
    
    public void M(dynamic d)
    {
        f(d); // error CS1620: Argument 1 must be passed with the 'ref' keyword
    }
}

A expressão de coleção para o tipo que implementa IEnumerable deve ter elementos implicitamente conversíveis para object

Introduzido no Visual Studio 2022 versão 17.10

A conversão de uma expressão de coleção em uma struct ou class que implementa System.Collections.IEnumerable e não tem uma tipagem GetEnumerator() forte requer que os elementos na expressão de coleção sejam implicitamente conversíveis em object. Anteriormente, os elementos de uma expressão de coleção direcionada a uma IEnumerable implementação eram assumidos como conversíveis para object, e convertidos somente quando ligados ao método aplicável Add.

Esse requisito adicional significa que as conversões de expressão de coleção para IEnumerable implementações são tratadas de forma consistente com outros tipos de destino em que os elementos na expressão de coleção devem ser implicitamente conversíveis para o tipo de iteração do tipo de destino.

Essa alteração afeta expressões de coleção que visam IEnumerable implementações em que os elementos dependem da tipagem de destino para um tipo de parâmetro de método fortemente tipado Add. No exemplo abaixo, é relatado um erro que _ => { } não pode ser convertido implicitamente em object.

class Actions : IEnumerable
{
    public void Add(Action<int> action);
    // ...
}

Actions a = [_ => { }]; // error CS8917: The delegate type could not be inferred.

Para resolver o erro, a expressão do elemento pode ser digitada explicitamente.

a = [(int _) => { }];          // ok
a = [(Action<int>)(_ => { })]; // ok

O tipo de destino da expressão de coleção deve ter um construtor e um método Add

Introduzido no Visual Studio 2022 versão 17.10

A conversão de uma expressão de coleção para um struct ou class que implementa System.Collections.IEnumerable e não tem um CollectionBuilderAttribute requer que o tipo de destino tenha um construtor acessível que possa ser chamado sem argumentos e, se a expressão de coleção não estiver vazia, o tipo de destino deve ter um método acessível Add que possa ser chamado com um único argumento.

Anteriormente, o construtor e os métodos Add eram necessários para a construção da instância de coleção, mas não para a conversão. Isso significava que a chamada a seguir era ambígua, uma vez que ambos char[] e string eram tipos de destino válidos para a expressão de coleção. A chamada não é mais ambígua porque string não tem um construtor ou Add método sem parâmetros.

Print(['a', 'b', 'c']); // calls Print(char[])

static void Print(char[] arg) { }
static void Print(string arg) { }

ref os argumentos podem ser passados para in parâmetros

Introduzido no Visual Studio 2022 versão 17.8p2

Parâmetros de recurso ref readonly relaxado resolução de sobrecarga permitindo que ref os argumentos sejam passados para in parâmetros quando LangVersion é definido como 12 ou posterior. Isso pode levar a alterações de comportamento ou a mudanças que comprometem a compatibilidade com o código original.

var i = 5;
System.Console.Write(new C().M(ref i)); // prints "E" in C# 11, but "C" in C# 12
System.Console.Write(E.M(new C(), ref i)); // workaround: prints "E" always

class C
{
    public string M(in int i) => "C";
}
static class E
{
    public static string M(this C c, ref int i) => "E";
}
var i = 5;
System.Console.Write(C.M(null, ref i)); // prints "1" in C# 11, but fails with an ambiguity error in C# 12
System.Console.Write(C.M((I1)null, ref i)); // workaround: prints "1" always

interface I1 { }
interface I2 { }
static class C
{
    public static string M(I1 o, ref int x) => "1";
    public static string M(I2 o, in int x) => "2";
}

Prefira o descarte baseado em padrão em vez do descarte baseado em interface no contexto assíncrono using

Introduzido no Visual Studio 2022 versão 17.10p3

Uma operação assíncrona using prefere ligar usando um método baseado em padrão DisposeAsync() em vez de um baseado em interface IAsyncDisposable.DisposeAsync().

Por exemplo, o método público DisposeAsync() será escolhido, em vez da implementação da interface privada:

await using (var x = new C()) { }

public class C : System.IAsyncDisposable
{
    ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked

    public async ValueTask DisposeAsync()
    {
        Console.WriteLine("PICKED");
        await Task.Yield();
    }
}