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


Серьезные изменения в Roslyn в диапазоне от .NET 6.0.100 до .NET 7.0.100

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

Локальные переменные ограниченных типов запрещены в асинхронных методах

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

Локальные типы ограниченных типов запрещены в асинхронных методах. Но в более ранних версиях компилятор не заметил некоторых неявно объявленных локальных языков. Например, в foreach инструкциях или using деконструкциях.
Теперь такие неявно объявленные локальные переменные также запрещены.

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

См. https://github.com/dotnet/roslyn/pull/66264

Указатели всегда должны находиться в небезопасных контекстах.

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

В более ранних пакетах SDK компилятор иногда разрешает расположения, на которые можно ссылаться, без явной маркировки этого расположения как небезопасного. Теперь модификатор unsafe должен присутствовать.
Например, using Alias = List<int*[]>; следует изменить на using unsafe Alias = List<int*[]>;, чтобы это соответствовало требованиям.
Такое использование void Method(Alias a) ... должно быть заменено на unsafe void Method(Alias a) ....

Правило является безусловным, за исключением using объявлений псевдонимов (которые не допускали использование unsafe модификатора до версии C# 12).
Поэтому для using объявлений правило действует только в том случае, если языковая версия выбрана как C# 12 или более поздняя.

System.TypedReference считается управляемым

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

Перемещение вперед типа System.TypedReference считается управляемым.

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
}

Ошибки безопасности ссылок не влияют на преобразование из лямбда-выражения в делегат

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

Ошибки безопасности, сообщаемые в лямбда-тексте, больше не влияют на то, преобразуется ли лямбда-выражение в тип делегата. Это изменение может повлиять на разрешение перегрузки.

В примере ниже вызов M(x => ...) в версии Visual Studio 17.5 является неоднозначным, так как M(D1) и M(D2) теперь считаются применимыми, даже если вызов F(ref x, ref y) в теле лямбда-выражения приведет к безопасности ссылок с M(D1) (см. примеры для сравнения в d1 и d2). Ранее вызов однозначно привязался к M(D2), так как перегрузка M(D1) считалась неприменимой.

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

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

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

Необработанные интерполяции строк в начале строки.

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

В пакете SDK для .NET 7.0.100 или более ранней версии было ошибочно разрешено следующее:

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

Это нарушило правило, которое содержимое строк (включая начало интерполяции) должно начинаться с того же пробела, что и последняя """; строка. Теперь необходимо, чтобы приведенные выше тексты были написаны следующим образом:

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

Выводный тип делегата для методов включает значения параметров по умолчанию и params модификатор

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

В SDK .NET 7.0.100 или более ранней версии типы делегатов, выведенные из методов, игнорируют значения параметров по умолчанию и params модификаторы, как показано в следующем коде.

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

В пакете SDK для .NET 7.0.200 или более поздней версии такие методы выводятся как анонимные синтезированные типы делегатов с теми же значениями параметров и params модификаторами по умолчанию. Это изменение может нарушить приведенный выше код, как показано ниже:

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

Дополнительные сведения об этом изменении см. в связанном предложении.

Для анализа определенных назначений вызовы асинхронных локальных функций больше не рассматриваются как ожидаемые

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

Для определенного анализа назначения вызовы асинхронной локальной функции больше не рассматриваются как ожидаемые и, следовательно, локальная функция не считается полностью выполненной. Смотрите https://github.com/dotnet/roslyn/issues/43697 для обоснования.

Приведенный ниже код теперь будет сообщать об определенной ошибке назначения:

    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 узлы для атрибутов теперь это IAttributeOperation узлы.

Представлено в Visual Studio 2022 версии 17.5, пакет SDK для .NET версии 7.0.200

В предыдущих версиях компилятора дерево для атрибута имело корень в виде узла IOperation. Мы добавили встроенную поддержку атрибутов, что означает, что корень дерева теперь является IAttributeOperation. Некоторые анализаторы, включая старые версии анализаторов .NET SDK, не ожидают этой формы дерева и могут неправильно предупреждать (или потенциально не предупреждать вовсе), столкнувшись с ней. Ниже приведены обходные пути.

  • При возможности обновите версию анализатора. При использовании пакета SDK для .NET или более ранних версий Microsoft.CodeAnalysis.FxCopAnalyzers обновите до версии Microsoft.CodeAnalysis.NetAnalyzers 7.0.0-preview1.22464.1 или более новой.
  • Подавляйте любые ложные срабатывания в анализаторах, пока они не будут обновлены версией, которая учитывает это изменение.

Тесты типов для ref структур не поддерживаются.

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

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

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

Неиспользуемые результаты из ref local являются разыменованными значениями.

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

Если локальная переменная ref ссылается на значение, но результат не используется (например, присваивается списанию), этот результат раньше игнорировался. Теперь компилятор будет разыменовывать эту локальную переменную, гарантируя наблюдаемость всех побочных эффектов.

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

Не удается назвать типы scoped

Представлено в Visual Studio 2022 версии 17.4. Начиная с C# 11, типы не могут быть названы scoped. Компилятор сообщит об ошибке по всем таким именам типов. Чтобы обойти эту проблему, имя типа и все случаи его использования необходимо экранировать с помощью @.

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

Это было сделано, так как scoped теперь модификатор для объявлений переменных и зарезервированный, используя ref в типе ссылки.

Не удается назвать типы file

Представлено в Visual Studio 2022 версии 17.4. Начиная с C# 11, типы не могут быть названы file. Компилятор сообщит об ошибке по всем таким именам типов. Чтобы обойти эту проблему, имя типа и все случаи его использования необходимо экранировать с помощью @.

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

Это было сделано, так как file теперь является модификатором в объявлениях типов.

Дополнительные сведения об этом изменении см. в ассоциированной задаче csharplang.

Требуемые пробелы в директивах #line span

Представлен в пакете SDK для .NET 6.0.400, Visual Studio 2022 версии 17.3.

Когда директива #line диапазона была введена в C# 10, она не требует определенного интервала.
Например, это допустимо: #line(1,2)-(3,4)5"file.cs"

В Visual Studio 17.3 компилятор требует пробелов перед первой скобкой, смещением позиции символов и именем файла.
Поэтому приведенный выше пример не может анализироваться, если не добавляются пробелы. #line (1,2)-(3,4) 5 "file.cs"

Проверенные операторы в System.IntPtr и System.UIntPtr

Представлен в пакете SDK для .NET 7.0.100, Visual Studio 2022 версии 17.3.

Когда платформа поддерживает числовыеIntPtr и UIntPtr типы (что видно по наличию System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr), встроенные операторы из nint и nuint применяются к этим базовым типам. Это означает, что на таких платформах IntPtr и UIntPtr имеют встроенные операторы checked, которые теперь могут вызываться при возникновении переполнения.

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
}

Возможные обходные пути:

  1. Укажите контекст unchecked
  2. Переход на платформу или TFM без числовых IntPtr/UIntPtr типов

Кроме того, неявные преобразования между IntPtr/UIntPtr другими числовыми типами рассматриваются как стандартные преобразования на таких платформах. Это может повлиять на разрешение перегрузки в некоторых случаях.

Эти изменения могут привести к изменению поведения, если код пользователя был в зависимости от исключений переполнения в незаверяемом контексте или если он не ожидал исключений переполнения в проверяемом контексте. Анализатор был добавлен в версии 7.0 , чтобы помочь обнаружить такие изменения поведения и принять соответствующие меры. Анализатор создаст диагностику возможных изменений в поведении, которая по умолчанию имеет уровень серьезности "info", но может быть повышена до уровня предупреждений с помощью editorconfig.

Добавление System.UIntPtr и System.Int32

Представлен в пакете SDK для .NET 7.0.100, Visual Studio 2022 версии 17.3.

Если платформа поддерживает числовыеIntPtr и UIntPtr типы (как указано в присутствии System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr), оператор +(UIntPtr, int) , определенный в System.UIntPtr ней, больше не может использоваться. Добавление выражений типов System.UIntPtr и System.Int32 приводит к ошибке.

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

Возможные обходные пути:

  1. UIntPtr.Add(UIntPtr, int) Используйте метод:UIntPtr.Add(x, y)
  2. Примените непроверенное приведение к типу nuint на второй операнд: x + unchecked((nuint)y)

Оператор Nameof в атрибуте метода или локальной функции

Представлен в пакете SDK для .NET 6.0.400, Visual Studio 2022 версии 17.3.

Если языковая версия — C# 11 или более поздняя, оператор nameof в атрибуте метода делает параметры типа этого метода доступными в области видимости. Это же относится к локальным функциям.
Оператор nameof в атрибуте метода или его параметров типа и параметров делает параметры этого метода доступными в области видимости. Это же относится к локальным функциям, лямбда-файлам, делегатам и индексаторам.

Например, теперь это будут ошибки:

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

Возможные обходные пути:

  1. Переименуйте параметр типа или параметр, чтобы избежать скрытия имени из внешней области видимости.
  2. Используйте строковый литерал вместо оператора nameof.

Не удается вернуть параметр out по ссылке

Представлен в пакете SDK для .NET 7.0.100, Visual Studio 2022 версии 17.3.

С языковой версией C# 11 или более поздней или с .NET 7.0 или более поздней, параметр out нельзя вернуть по ссылке.

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
}

Возможные обходные пути:

  1. Используйте System.Diagnostics.CodeAnalysis.UnscopedRefAttribute, чтобы отметить ссылку как недоступную.

    static ref T ReturnOutParamByRef<T>([UnscopedRef] out T t)
    {
        t = default;
        return ref t; // ok
    }
    
  2. Измените сигнатуру метода, чтобы передать параметр с помощью ref.

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

Метод экземпляра в ref-структуре может захватывать параметры ссылок без проверки области действия

Представлен в пакете SDK для .NET 7.0.100, Visual Studio 2022 версии 17.4.

При использовании версии языка C# 11 или более поздней, или с .NET 7.0 или более поздней, вызов метода экземпляра ref struct предполагает захват неуправляемых параметров ref или in.

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

Возможное обходное решение, если параметр ref или in не записан в методе экземпляра ref struct, — объявить параметр как scoped ref или 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) { }
}

Метод анализа выхода структуры ref зависит от анализа выхода аргументов ref.

Представлен в пакете SDK для .NET 7.0.100, Visual Studio 2022 версии 17.4.

С языковой версией C# 11 или более поздней, или с .NET 7.0 или более поздней, ref struct, возвращаемый из вызова метода, будет out только если все и ref аргументы вызова метода являются in. Аргументы in могут включать неявные значения параметров по умолчанию.

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

Возможная альтернатива, если аргумент ref или in не записан в возвращаемом значении ref struct, — объявить параметр как scoped ref или scoped in.

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

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

ref аргумент ref struct , который считается незамеченным в __arglist

Представлен в пакете SDK для .NET 7.0.100, Visual Studio 2022 версии 17.4.

С C# версии 11 или более поздней и с .NET 7.0 или более поздней, тип ref считается неограниченной ссылкой, когда передается в качестве аргумента для ref struct.

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

Оператор беззнакового сдвига вправо

Представлен в пакете SDK для .NET 6.0.400, Visual Studio 2022 версии 17.3. Язык добавил поддержку оператора Unsigned Right Shift (>>>). Это отключает возможность использовать методы, реализующие определяемые пользователем операторы Unsigned Right Shift в качестве обычных методов.

Например, существует библиотека, разработанная на одном из языков (кроме VB или C#), которая предоставляет пользовательский оператор беззнакового сдвига вправо для типа C1. Ранее следующий код успешно компилировался:

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

Возможное решение заключается в переключении на использование >>> оператора:

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

Перечислитель foreach в виде структуры ссылок

Представлен в пакете SDK для .NET 6.0.300, Visual Studio 2022 версии 17.2. При foreach использовании типа перечислителя структуры ссылок сообщается об ошибке, если для языковой версии задано значение 7.3 или более ранних версий.

Это исправляет ошибку, из-за которой функция была поддерживается в более новых компиляторах, предназначенных для версии C# до ее поддержки.

Возможные обходные пути:

  1. Измените тип ref struct на тип struct или class.
  2. <LangVersion> Обновите элемент до версии 7.3 или более поздней версии.

Async foreach предпочитает шаблонный подход DisposeAsync вместо явной реализации интерфейса IAsyncDisposable.DisposeAsync()

Представлен в SDK для .NET 6.0.300, Visual Studio 2022 версии 17.2. Асинхронный foreach предпочитает связываться через шаблон-зависимый DisposeAsync() метод, а не через IAsyncDisposable.DisposeAsync().

Например, DisposeAsync() будет выбран вместо метода IAsyncEnumerator<int>.DisposeAsync() на 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
}

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

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

Запретить преобразованные строки в качестве аргумента по умолчанию

Представлен в пакете SDK для .NET 6.0.300, Visual Studio 2022 версии 17.2. Компилятор C# принимает неверные значения аргументов по умолчанию, включающие преобразование ссылочной константы строки, и будет выдаваться null в качестве константного значения вместо значения по умолчанию, указанного в источнике. В Visual Studio 17.2 это становится ошибкой. См. roslyn#59806.

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

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

Предыдущее объявление требовало преобразования из string в IEnumerable<char>. Компилятор разрешил эту конструкцию и сгенерировал null в качестве значения аргумента. Предыдущий код создает ошибку компилятора, начиная с 17.2.

Чтобы обойти это изменение, можно внести одно из следующих изменений:

  1. Измените тип параметра, чтобы преобразование не требовалось.
  2. Измените значение аргумента по умолчанию на null, чтобы восстановить предыдущее поведение.

Контекстное ключевое слово var в качестве явного лямбда-возвращаемого типа

Представлен в пакете SDK для .NET 6.0.200, Visual Studio 2022 версии 17.1. Контекстное ключевое слово var нельзя использовать в качестве явного лямбда-возвращаемого типа.

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

Эта ошибка может возникнуть, если у вас есть тип с именем var и вы определяете лямбда-выражение с явным возвращаемым типом var (типа).

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

Обходные пути включают следующие изменения:

  1. Используйте @var в качестве возвращаемого типа.
  2. Удалите явный тип возвращаемого значения, чтобы компилятор определил тип возвращаемого значения.

Интерполированные обработчики строк и инициализация индексатора

Представлен в пакете SDK для .NET 6.0.200, Visual Studio 2022 версии 17.1. Индексаторы, принимающий интерполированный обработчик строк и требующие приемника в качестве входных данных конструктора, не могут использоваться в инициализаторе объектов.

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

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

Обходные пути включают следующие изменения:

  1. Удалите тип приемника из интерполированного обработчика строк.
  2. Измените аргумент на индексатор, чтобы он был string

ref, readonly ref, in, out не допускаются в качестве параметров или возвращаемых значений в методах, вызываемых только для неуправляемого кода.

Представленные в пакете SDK для .NET 6.0.200 и Visual Studio 2022 версии 17.1 ref/ref readonly/in не допускается использовать на возвращаемых значениях или параметрах метода с атрибутом /.

Это изменение является исправлением ошибок. Возвращаемые значения и параметры не являются блитабельными. Передача аргументов или возвращаемых значений по ссылке может привести к неопределенному поведению. Ни одно из следующих объявлений не будет компилироваться:

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'.

Обходной путь — удалить модификатор ссылок.

Длина и количество считаются неотрицательными в шаблонах.

Представлен в пакете SDK для .NET 6.0.200, Visual Studio 2022 версии 17.1.Length и Count свойства для подсчитываемых и индексируемых типов считаются неотрицательными для целей подстановки и исчерпывающего анализа шаблонов и переключателей. Эти типы можно использовать с неявным индексатором и шаблонами списков.

Предполагается, что свойства Length и Count, хотя и типизированы как int, не являются отрицательными при анализе шаблонов. Рассмотрим этот пример метода:

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
}

До 17.1 первый переключатель, тест, является ли Count отрицательным, нужно было, чтобы избежать предупреждения, что не все возможные значения были учтены. Начиная с версии 17.1, первая ветвь переключателя вызывает ошибку компилятора. Решение заключается в удалении рычагов переключения, добавленных для недопустимых случаев.

Это изменение было сделано в рамках добавления шаблонов списка. Правила обработки более последовательны, если каждое использование свойств Length или Count в коллекции рассматривается как неотрицательное. Дополнительные сведения об изменении проблемы с проектированием языка см. в статье.

Обходной путь заключается в удалении рычагов переключения с недоступными условиями.

Добавление инициализаторов полей в структуру требует явно объявленного конструктора

Представлен в пакете SDK для .NET 6.0.200, Visual Studio 2022 версии 17.1.struct Объявления типов с инициализаторами полей должны содержать явно объявленный конструктор. Кроме того, все поля должны быть определенно назначены в struct конструкторах экземпляров, которые не имеют : this() инициализатора, поэтому любые ранее неназначенные поля должны быть назначены из добавленного конструктора или из инициализаторов полей. См. dotnet/csharplang#5552, dotnet/roslyn#58581.

Существует два способа инициализации переменной по умолчанию в C#: new() и default. Для классов разница очевидна, так как new создается новый экземпляр и default возвращается null. Разница более тонкая для структур, так как default возвращает экземпляр с каждым полем или свойством, установленным по умолчанию. Мы добавили инициализаторы полей для структур в C# 10. Инициализаторы полей выполняются только при запуске явно объявленного конструктора. Они существенно не выполняются, когда вы используете default или создаете массив любого типа struct.

В версии 17.0, если есть инициализаторы полей, но не объявлены конструкторы, синтезируется конструктор без параметров, выполняющий инициализаторы полей. Однако это означало, что добавление или удаление объявления конструктора может повлиять на то, синтезируется ли конструктор без параметров, и в результате может изменить поведение new().

Чтобы устранить проблему, в пакете SDK для .NET 6.0.200 (VS 17.1) компилятор больше не синтезирует конструктор без параметров. Если struct содержит инициализаторы полей и не имеет явных конструкторов, компилятор генерирует ошибку. Если у struct имеются инициализаторы полей, необходимо объявить конструктор, в противном случае инициализаторы полей никогда не будут выполнены.

Кроме того, все поля, у которых нет инициализаторов полей, должны быть назначены в каждом struct конструкторе, если конструктор не имеет инициализатора : this() .

Например:

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

Решение заключается в объявлении конструктора. Если поля ранее не были не назначены, этот конструктор может и часто будет пустым конструктором без параметров:

struct S
{
    int X = 1;
    int Y;

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

Описатели формата не могут содержать фигурные скобки

Представлен в пакете SDK для .NET 6.0.200, Visual Studio 2022 версии 17.1. Описатели формата в интерполированных строках не могут содержать фигурные скобки ( { или }). В предыдущих версиях {{ интерпретировался как экранированный {, а }} интерпретировался как экранированный } символ в спецификаторе формата. Теперь первый } символ в форматном описателе заканчивает интерполяцию, и любой { символ является ошибкой.

Это делает интерполированную обработку строк согласованной с обработкой для System.String.Format:

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

X — это формат шестнадцатеричного верхнего регистра и C шестнадцатеричное значение для 12.

Обходной путь заключается в удалении дополнительных фигурных скобок в строке форматирования.

Дополнительные сведения об этом изменении можно узнать в связанном вопросе Roslyn.

Не удается назвать типы required

Представлено в Visual Studio 2022 версии 17.3. Начиная с C# 11, типы не могут быть названы required. Компилятор сообщит об ошибке по всем таким именам типов. Чтобы обойти эту проблему, имя типа и все случаи его использования необходимо экранировать с помощью @.

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

Это было сделано, так как required теперь является модификатором члена для свойств и полей.

Дополнительные сведения об этом изменении см. в ассоциированной задаче csharplang.