Поделиться через


Критические изменения в Roslyn после .NET 7.0.100 до .NET 8.0.100

В этом документе перечислены известные критические изменения в Roslyn после общего выпуска .NET 7 (пакет SDK для .NET версии 7.0.100) до общего выпуска .NET 8.0.100.

Модификаторы ссылок динамических аргументов должны быть совместимы с модификаторами ссылок соответствующих параметров

Представлено в Visual Studio 2022 версии 17.10

Модификаторы ref динамических аргументов должны быть совместимы с модификаторами ref соответствующих параметров при компиляции. Это может привести к сбою разрешения перегрузки, включающей динамические аргументы во время компиляции, а не во время выполнения.

Ранее допускалось несовпадение во время компиляции, и сбой разрешения перегрузки откладывался до времени выполнения.

Например, следующий код ранее компилировался без ошибки, но завершался сбоем с исключением: "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: лучшее соответствие перегруженного метода для 'C.f(ref object)' имеет некоторые недопустимые аргументы." Теперь это вызовет ошибку компиляции.

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
    }
}

Выражение коллекции для реализации IEnumerable типа должно иметь элементы, неявно преобразуемые в object

Представлено в Visual Studio 2022 версии 17.10

Преобразование коллекции в struct или class, которое поддерживает System.Collections.IEnumerable и не имеет строго типизированного GetEnumerator(), требует, чтобы элементы коллекции были неявно преобразуемы в object. Ранее считалось, что элементы выражения коллекции, ориентированные на реализацию IEnumerable, преобразуются в object, и преобразуются только при привязке к применимому методу Add.

Это дополнительное требование означает, что преобразования выражений коллекции в IEnumerable реализации обрабатываются согласованно с другими целевыми типами, в которых элементы в выражении коллекции должны быть неявно преобразованы в тип итерации целевого типа.

Это изменение касается выражений коллекций, нацеленных на реализации IEnumerable, где элементы полагаются на использование целевых типов для параметров методов, строго типизированных к типу Add. В приведенном ниже примере сообщается об ошибке, связанной с тем, что _ => { } невозможно неявно преобразовать в object.

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

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

Чтобы устранить ошибку, выражение элемента может быть явно типизированным.

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

Тип целевого объекта выражения коллекции должен иметь конструктор и Add метод

Представлено в Visual Studio 2022 версии 17.10

Преобразование выражения коллекции в реализацию struct или classSystem.Collections.IEnumerableне имеетCollectionBuilderAttribute целевого типа, чтобы иметь доступный конструктор, который можно вызывать без аргументов, и если выражение коллекции не пусто, целевой тип должен иметь доступный Add метод, который можно вызвать с одним аргументом.

Ранее конструктор и Add методы были необходимы для создания экземпляра коллекции, но не для преобразования. Это означает, что следующий вызов является неоднозначным, так как оба char[] и string были допустимыми целевыми типами для выражения коллекции. Вызов больше не является неоднозначным, так как string не имеет конструктора или Add метода без параметров.

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

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

ref Аргументы можно передать в in параметры

Представлено в Visual Studio 2022 версии 17.8p2

Функция ref readonly обеспечивает ослабление разрешения перегрузки, позволяя аргументы ref передавать в параметры in, когда LangVersion установлено значение 12 или более позднее. Это может привести к изменениям в поведении или изменению кода, нарушающему его работу:

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";
}

Предпочитайте удаление на основе шаблонов, а не на основе интерфейсов в асинхронном режиме using

Представлено в Visual Studio 2022 версии 17.10p3

Асинхронный using предпочитает выполнять привязку с использованием шаблона на основе метода DisposeAsync(), вместо интерфейса на основе IAsyncDisposable.DisposeAsync().

Например, будет выбран общедоступный DisposeAsync() метод, а не реализация частного интерфейса:

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();
    }
}