Udostępnij za pośrednictwem


Zmiany powodujące niezgodność w kompilatorze Roslyn pomiędzy .NET 6.0.100 a .NET 7.0.100

Ten dokument wymienia znane zmiany powodujące niekompatybilność w Roslyn od ogólnego wydania platformy .NET 6 (wersja zestawu .NET SDK 6.0.100) do ogólnego wydania platformy .NET 7 (wersja zestawu .NET SDK 7.0.100).

Wszystkie lokalne zmienne typów ograniczonych są niedozwolone w metodach asynchronicznych

Wprowadzono w programie Visual Studio 2022 w wersji 17.6p1

Typy z lokalnymi ograniczeniami są niedozwolone w metodach asynchronicznych. Jednak we wcześniejszych wersjach kompilator nie zauważył niektórych niejawnie zadeklarowanych ustawień lokalnych. Na przykład w foreach instrukcjach lub using dekonstrukcji.
Teraz takie zmienne lokalne, zadeklarowane niejawnie, są również niedozwolone.

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

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

Wskaźniki muszą zawsze znajdować się w niebezpiecznych kontekstach.

Wprowadzono w programie Visual Studio 2022 w wersji 17.6

We wcześniejszych zestawach SDK kompilator od czasu do czasu zezwalałby na lokalizacje, w których można odwoływać się do wskaźników, bez jawnego oznaczania tej lokalizacji jako niebezpiecznej. Teraz modyfikator unsafe musi być obecny.
Na przykład using Alias = List<int*[]>; należy zmienić wartość na using unsafe Alias = List<int*[]>; legalną.
Użycie, takie jak void Method(Alias a) ... należy zmienić na unsafe void Method(Alias a) ....

Reguła jest bezwarunkowa, z wyjątkiem using deklaracji aliasów (które nie zezwalały na unsafe modyfikator przed C# 12).
W przypadku using deklaracji reguła ma zastosowanie tylko wtedy, gdy wersja języka zostanie wybrana jako C# 12 lub nowsza.

System.TypedReference uznawany za zarządzany

Wprowadzono w programie Visual Studio 2022 w wersji 17.6

W przyszłości System.TypedReference typ jest uważany za zarządzany.

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
}

Błędy bezpieczeństwa dla ref nie wpływają na konwersję wyrażenia lambda na delegata

Wprowadzono w programie Visual Studio 2022 w wersji 17.5

Błędy bezpieczeństwa ref zgłoszone w ciele wyrażenia lambda nie mają już wpływu na to, czy wyrażenie lambda może być przekształcane na typ delegata. Ta zmiana może mieć wpływ na rozwiązanie przeciążenia.

W poniższym przykładzie wywołanie M(x => ...) jest niejednoznaczne w programie Visual Studio 17.5, ponieważ zarówno M(D1), jak i M(D2) są teraz uznawane za możliwe do zastosowania, mimo że wywołanie F(ref x, ref y) w treści lambdy spowoduje powstanie bezpieczeństwa odwołania z M(D1) (zobacz przykłady w d1 i d2 dla porównania). Wcześniej wywołanie było powiązane jednoznacznie z M(D2) ponieważ przeciążenie M(D1) zostało uznane za nieodpowiednie.

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

Aby obejść zmiany rozpoznawania przeciążenia, użyj jawnych typów dla parametrów lambda lub delegata.

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

Surowe interpolacje ciągów znaków na początku wiersza.

Wprowadzono w programie Visual Studio 2022 w wersji 17.5

W wersji .NET SDK 7.0.100 lub starszej błędnie były dozwolone następujące elementy.

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

Narusza to regułę, że zawartość wierszy (w tym miejsce rozpoczęcia interpolacji) musi zaczynać się od tego samego odstępu co ostatni """; wiersz. Teraz wymagane jest, aby powyższe elementy zostały zapisane jako:

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

Wnioskowany typ delegata dla metod zawiera domyślne wartości parametrów i params modyfikator

Wprowadzono w programie Visual Studio 2022 w wersji 17.5

W wersji .NET SDK 7.0.100 lub starszej typy delegatów wywnioskowane z metod ignorowały domyślne wartości parametrów i modyfikatory params, jak pokazano w poniższym kodzie.

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

W zestawie .NET SDK 7.0.200 lub nowszym takie metody są wnioskowane jako anonimowe syntetyzowane typy delegatów z tymi samymi domyślnymi wartościami parametrów i params modyfikatorami. Ta zmiana może przerwać powyższy kod, jak pokazano poniżej:

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

Więcej informacji na temat tej zmiany można uzyskać w skojarzonej propozycji.

W celu analizy jednoznacznego przypisania wywołania funkcji lokalnych asynchronicznych nie są już traktowane jako oczekujące na wykonanie.

Wprowadzono w programie Visual Studio 2022 w wersji 17.5

Do celów analizy jednoznacznego przypisania, wywołania lokalnej funkcji asynchronicznej nie są już traktowane jako czekania, z tego powodu funkcja lokalna nie jest uważana za w pełni wykonaną. Zobacz https://github.com/dotnet/roslyn/issues/43697 dla uzasadnienia.

Poniższy kod zgłosi teraz określony błąd przypisania:

    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 węzły atrybutów są teraz IAttributeOperation węzłami.

Wprowadzono w programie Visual Studio 2022 w wersji 17.5, .NET SDK w wersji 7.0.200

W poprzednich wersjach kompilatora IOperation drzewo atrybutu zostało zakorzenione w węźle INoneOperation . Dodaliśmy natywną obsługę atrybutów, co oznacza, że korzeń drzewa jest teraz elementem IAttributeOperation. Niektóre analizatory, w tym starsze wersje analizatorów zestawu .NET SDK, nie spodziewają się tego kształtu drzewa składniowego i będą niepoprawnie ostrzegać (lub potencjalnie nie ostrzegać) po jego napotkaniu. Obejścia tego problemu są następujące:

  • Zaktualizuj wersję analizatora, jeśli to możliwe. Jeśli używasz zestawu .NET SDK lub starszych wersji microsoft.CodeAnalysis.FxCopAnalyzers, zaktualizuj do wersji Microsoft.CodeAnalysis.NetAnalyzers 7.0.0-preview1.22464.1 lub nowszej.
  • Pomijanie wszelkich wyników fałszywie dodatnich z analizatorów do momentu ich zaktualizowania przy użyciu wersji, która uwzględnia tę zmianę.

Testy typów dla ref struktur nie są obsługiwane.

Wprowadzono w programie Visual Studio 2022 w wersji 17.4

ref Gdy typ struktury jest używany w operatorach "is" lub "as", w niektórych scenariuszach kompilator wcześniej zgłaszał błędne ostrzeżenie informujące, że test typu zawsze kończy się niepowodzeniem w czasie wykonywania, pomijając faktyczne sprawdzenie typu, co prowadziło do nieprawidłowego zachowania. Jeśli możliwe było nieprawidłowe zachowanie w czasie wykonywania, kompilator utworzy teraz błąd.

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

Nieużywane wyniki zmiennej lokalnej odwołania są dereferencjami.

Wprowadzono w programie Visual Studio 2022 w wersji 17.4

Gdy zmienna lokalna ref jest przywoływana przez wartość, ale wynik nie jest używany (na przykład przypisywany do odrzucenia), wynik został wcześniej zignorowany. Kompilator wyłuszy teraz tę lokalną zmienną, ubezpieczając się, że wszelkie skutki uboczne są obserwowane.

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

Typy nie mogą być nazwane scoped

Wprowadzono w programie Visual Studio 2022 w wersji 17.4. Począwszy od języka C# 11, typy nie mogą mieć nazwy scoped. Kompilator zgłosi błąd dla wszystkich takich nazw typów. Aby obejść ten problem, nazwa typu i wszystkie użycia muszą zostać zaznaczone za pomocą znacznika @.

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

Zostało to zrobione, ponieważ scoped jest teraz modyfikatorem deklaracji zmiennych i jest zarezerwowany po ref w typie ref.

Typy nie mogą być nazwane file

Wprowadzono w programie Visual Studio 2022 w wersji 17.4. Począwszy od języka C# 11, typy nie mogą mieć nazwy file. Kompilator zgłosi błąd dla wszystkich takich nazw typów. Aby obejść ten problem, nazwa typu i wszystkie użycia muszą zostać zaznaczone za pomocą znacznika @.

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

Zostało to zrobione, ponieważ file jest teraz modyfikatorem deklaracji typów.

Więcej informacji na temat tej zmiany można znaleźć w skojarzonym zgłoszeniu csharplang.

Wymagane spacje w dyrektywach zakresu #line

Wprowadzono w zestawie .NET SDK 6.0.400, Visual Studio 2022 w wersji 17.3.

Gdy dyrektywa #line span została wprowadzona w C# 10, nie wymagała żadnego szczególnego odstępu.
Na przykład będzie to prawidłowe: #line(1,2)-(3,4)5"file.cs".

W programie Visual Studio 17.3 kompilator wymaga spacji przed pierwszym nawiasem, offsetem znaku i nazwą pliku.
Dlatego powyższy przykład nie zostanie poprawnie zinterpretowany, dopóki nie zostaną dodane spacje: #line (1,2)-(3,4) 5 "file.cs".

Sprawdzone operatory w System.IntPtr i System.UIntPtr

Wprowadzono w .NET SDK w wersji 7.0.100, Visual Studio 2022 wersji 17.3.

Gdy platforma obsługuje wartości liczboweIntPtr i UIntPtr typy (wskazywane przez obecność System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr) wbudowane operatory z nint i nuint mają zastosowanie do tych typów bazowych. Oznacza to, że na takich platformach IntPtr i UIntPtr mają wbudowane checked operatory, które mogą teraz generować wyjątek w przypadku wystąpienia przepełnienia.

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
}

Możliwe obejścia to:

  1. Określanie unchecked kontekstu
  2. Obniżanie do platformy/serwera TFM bez typów liczbowych IntPtr/UIntPtr

Ponadto niejawne konwersje między IntPtr/UIntPtr i innymi typami liczbowymi są traktowane jako standardowe konwersje na takich platformach. Może to mieć wpływ na rozwiązanie przeciążenia w niektórych przypadkach.

Te zmiany mogą spowodować zmianę zachowania, jeśli kod użytkownika był zależny od wyjątków przepełnienia w nieznakowanym kontekście lub jeśli nie spodziewał się wyjątków przepełnienia w zaznaczonym kontekście. Analizator został dodany w wersji 7.0 , aby pomóc wykryć takie zmiany behawioralne i podjąć odpowiednie działania. Analizator utworzy diagnostykę potencjalnych zmian behawioralnych, które domyślnie mają ważność informacji, ale mogą zostać uaktualnione do ostrzeżeń za pośrednictwem pliku editorconfig.

Dodanie elementów System.UIntPtr i System.Int32

Wprowadzono w .NET SDK w wersji 7.0.100, Visual Studio 2022 wersji 17.3.

Gdy platforma obsługuje typy wartości liczboweIntPtr i UIntPtr, wskazywane przez obecność System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr, operator +(UIntPtr, int) zdefiniowany w System.UIntPtr nie może być już używany. Zamiast tego dodanie wyrażeń typów System.UIntPtr i System.Int32 powoduje wystąpienie błędu:

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

Możliwe obejścia to:

  1. UIntPtr.Add(UIntPtr, int) Użyj metody :UIntPtr.Add(x, y)
  2. Zastosuj rzutowanie niezweryfikowane do typu nuint na drugi operand: x + unchecked((nuint)y)

Operator Nameof w atrybucie metody lub lokalnej funkcji

Wprowadzono w zestawie .NET SDK 6.0.400, Visual Studio 2022 w wersji 17.3.

Jeśli wersja języka to C# 11 lub nowsza, nameof operator w atrybucie na metodzie wprowadza parametry typu tej metody w jego zasięg. Dotyczy to również funkcji lokalnych.
Operatory nameof w atrybucie na metodzie, jej parametrach typu lub innych parametrach umożliwiają dostęp do tych parametrów w obrębie metody. To samo dotyczy funkcji lokalnych, lambdów, delegatów i indeksatorów.

Na przykład będą to błędy:

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

Możliwe obejścia to:

  1. Zmień nazwę parametru lub parametru typu, aby uniknąć cieniowania nazwy z zakresu zewnętrznego.
  2. Zamiast operatora nameof użyj literału ciągu.

Nie można zwrócić parametru out przez odwołanie

Wprowadzono w .NET SDK w wersji 7.0.100, Visual Studio 2022 wersji 17.3.

W wersji językowej C# 11 lub nowszej albo w wersji .NET 7.0 lub nowszej nie można zwrócić parametru out przez odwołanie.

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
}

Możliwe obejścia to:

  1. Użyj System.Diagnostics.CodeAnalysis.UnscopedRefAttribute do oznaczenia odwołania jako niezakresowego.

    static ref T ReturnOutParamByRef<T>([UnscopedRef] out T t)
    {
        t = default;
        return ref t; // ok
    }
    
  2. Zmień sygnaturę metody, aby przekazać parametr przez ref.

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

Metoda instancyjna w strukturze ref może przechwytywać nieokreślone parametry ref

Wprowadzono w zestawie .NET SDK 7.0.100, Visual Studio 2022 w wersji 17.4.

Zakłada się, że w wersji językowej C# 11 lub nowszej albo z platformą .NET 7.0 lub nowszą wywołanie metody instancji ref struct służy do przechwycenia niezakresowych parametrów ref lub 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) { }
}

Możliwe obejście, jeśli parametr ref lub in nie jest przechwytywany w metodzie instancji ref struct, polega na zadeklarowaniu parametru jako scoped ref lub 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) { }
}

Analiza ucieczki powrotnej struktury metody zależy od ucieczki ref argumentów ref

Wprowadzono w zestawie .NET SDK 7.0.100, Visual Studio 2022 w wersji 17.4.

W wersji językowej C# 11 lub nowszej albo z platformą .NET 7.0 lub nowszą ref struct zwracaną z wywołania metody jako wartość zwracaną lub w out parametrach jest bezpieczna do ucieczki tylko wtedy, gdy wszystkie ref argumenty i in do wywołania metody są ref-safe-to-escape. Argumenty in mogą zawierać niejawne wartości parametrów domyślnych.

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

Możliwe obejście, jeśli argument ref lub in nie jest przechwytywany w zwracanej wartości ref struct, to zadeklarowanie parametru jako scoped ref lub scoped in.

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

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

ref argument ref struct uważany za nieobjęty zakresem w __arglist

Wprowadzono w zestawie .NET SDK 7.0.100, Visual Studio 2022 w wersji 17.4.

W wersji językowej C# 11 lub nowszej albo w wersji .NET 7.0 lub nowszej referencja z ref do typu ref struct jest uznawana za niezakresowe odwołanie, gdy jest przekazywane jako argument do __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
    }
}

Operator niepodpisanego przesunięcia w prawo

Wprowadzono w zestawie .NET SDK 6.0.400, Visual Studio 2022 w wersji 17.3. Język dodał obsługę operatora "Niepodpisane przesunięcie w prawo" (>>>). Spowoduje to wyłączenie możliwości korzystania z metod implementowania zdefiniowanych przez użytkownika operatorów "Unsigned Right Shift" jako zwykłych metod.

Istnieje na przykład istniejąca biblioteka opracowana w niektórych językach (innym niż VB lub C#), która uwidacznia operator zdefiniowany przez użytkownika "Niepodpisane przesunięcie w prawo" dla typu C1. Poniższy kod, który kiedyś kompilował się pomyślnie:

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

Możliwe obejście polega na przełączeniu się na korzystanie z operatora >>>:

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

Moduł wyliczający Foreach jako struktura ref

Wprowadzono w zestawie .NET SDK 6.0.300, Visual Studio 2022 w wersji 17.2. Użycie typu wyliczającego ref struct zgłasza błąd, jeśli wersja języka jest ustawiona na 7.3 lub wcześniejszą.

To naprawia usterkę polegającą na tym, że funkcja była obsługiwana w nowszych kompilatorach przeznaczonych dla wersji języka C# przed jej obsługą.

Możliwe obejścia to:

  1. Zmień typ ref struct na typ struct lub class.
  2. Uaktualnij <LangVersion> element do wersji 7.3 lub nowszej.

Async foreach preferuje metodologię wzorcową DisposeAsync nad bezpośrednią implementacją interfejsu IAsyncDisposable.DisposeAsync()

Wprowadzono w zestawie .NET SDK 6.0.300, Visual Studio 2022 w wersji 17.2. Asynchroniczna foreach preferuje powiązanie przy użyciu metody opartej na wzorcu DisposeAsync() zamiast IAsyncDisposable.DisposeAsync().

Na przykład DisposeAsync() zostanie wybrany zamiast metody IAsyncEnumerator<int>.DisposeAsync() na 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
}

Ta zmiana naprawia naruszenie specyfikacji, gdy metoda publiczna DisposeAsync jest widoczna w zadeklarowanym typie, natomiast implementacja jawnego interfejsu jest widoczna tylko przy użyciu odwołania do typu interfejsu.

Aby obejść ten błąd, usuń metodę opartą na DisposeAsync wzorcu z typu.

Nie zezwalaj na użycie skonwertowanych stringów jako argumentu domyślnego

Wprowadzono w zestawie .NET SDK 6.0.300, Visual Studio 2022 w wersji 17.2. Kompilator języka C# akceptował niepoprawne domyślne wartości argumentów, które obejmowały konwersję odwołania stałej tekstowej, i emitował null jako wartość zamiast wartości domyślnej określonej w źródle. W programie Visual Studio 17.2 staje się to błędem. Zobacz roslyn#59806.

Ta zmiana naprawia naruszenie specyfikacji w kompilatorze. Argumenty domyślne muszą być stałymi czasu kompilacji. Poprzednie wersje zezwalały na następujący kod:

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

Poprzednia deklaracja wymagała konwersji z string na IEnumerable<char>. Kompilator zezwolił na tę konstrukcję i emituje null jako wartość argumentu. Powyższy kod generuje błąd kompilatora rozpoczynający się w wersji 17.2.

Aby obejść tę zmianę, możesz wprowadzić jedną z następujących zmian:

  1. Zmień typ parametru, aby konwersja nie jest wymagana.
  2. Aby przywrócić poprzednie zachowanie, zmień wartość domyślnego argumentu na null.

Kontekstowe słowo kluczowe var jako jawnie określony typ zwracania lambda

Wprowadzono w zestawie .NET SDK 6.0.200, Visual Studio 2022 w wersji 17.1. Kontekstowe słowo kluczowe var nie może być używane jako jawny typ zwracany przez lambda.

Ta zmiana umożliwia potencjalne przyszłe funkcje , zapewniając, że var pozostanie naturalnym typem zwracanego wyrażenia lambda.

Ten błąd może wystąpić, jeśli masz typ o nazwie var i definiujesz wyrażenie lambda przy użyciu jawnego typu zwracanego var (typ).

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

Obejścia obejmują następujące zmiany:

  1. Użyj typu zwracanego @var.
  2. Usuń jawny typ zwracany, żeby kompilator określał typ zwracany.

Procedury obsługi ciągów interpolowanych i inicjowanie indeksatora

Wprowadzono w zestawie .NET SDK 6.0.200, Visual Studio 2022 w wersji 17.1. Indeksatory, które przyjmują obsługę ciągów interpolowanych i wymagają odbiornika jako argumentu dla konstruktora, nie mogą być używane w inicjatorze obiektów.

Ta zmiana nie zezwala na sytuację brzegową, w której inicjator indeksatora używa obsługiwacza ciągów interpolowanych, a ten obsługiwacz przyjmuje odbiornik indeksatora jako parametr konstruktora. Przyczyną tej zmiany jest to, że ten scenariusz może spowodować uzyskanie dostępu do zmiennych, które nie zostały jeszcze zainicjowane. Rozważ taki przykład:

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

Obejścia obejmują następujące zmiany:

  1. Usuń typ odbiorcy z procedury obsługi ciągów interpolowanych.
  2. Zmień argument na indeksator na string

Nie można używać parametrów ani zwracać ref, readonly ref, in, out w metodach dostępnych tylko dla niezarządzanych wywołujących.

Wprowadzono w zestawie .NET SDK 6.0.200, Visual Studio 2022 w wersji 17.1.ref/ref readonly/in/out nie mogą być używane jako zwracane wartości/parametry metody oznaczonej UnmanagedCallersOnly.

Ta zmiana jest poprawką błędów. Wartości zwracane i parametry nie są bezpośrednio mapowalne. Przekazywanie argumentów lub zwracanych wartości przez odwołanie może spowodować niezdefiniowane zachowanie. Żadna z następujących deklaracji nie zostanie skompilowana

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

Obejście polega na usunięciu modyfikatora przekazywania przez referencję.

Długość, liczba przyjmowana jako nieujemna we wzorcach

Wprowadzono w zestawie .NET SDK 6.0.200, Visual Studio 2022 w wersji 17.1.Length oraz Count właściwości typów zliczalnych i indeksowalnych przyjmuje się jako nieujemne dla celów analizy podsumpcji oraz wyczerpującej analizy wzorców i przełączników. Te typy mogą być używane z indeksatorem Index i wzorcami list ukrytymi.

Właściwości Length i Count , mimo że typizowane jako int, zakłada się, że nie są ujemne podczas analizowania wzorców. Rozważmy tę przykładową metodę:

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
}

Przed 17.1, pierwsze ramię przełącznika, testowanie, czy Count jest ujemne, było konieczne, aby uniknąć ostrzeżenia, że wszystkie możliwe wartości nie zostały objęte. Począwszy od wersji 17.1, pierwsze ramię przełącznika generuje błąd kompilatora. Obejście polega na usunięciu ramion przełącznika dodanych do nieprawidłowych przypadków.

Ta zmiana została wprowadzona w ramach dodawania wzorców listy. Reguły przetwarzania są bardziej spójne, jeśli każde użycie właściwości Length lub Count w kolekcji jest uznawane za nieujemne. Więcej informacji na temat zmiany w kwestii projektowania języka można znaleźć w dokumentacji.

Obejście polega na usunięciu ramion przełącznika z niemożliwymi do osiągnięcia warunkami.

Dodawanie inicjatorów pól do struktury wymaga jawnie zadeklarowanego konstruktora

Wprowadzono w .NET SDK 6.0.200, Visual Studio 2022 w wersji 17.1.struct deklaracje typów z inicjalizatorami pól muszą zawierać jawnie zadeklarowany konstruktor. Ponadto wszystkie pola muszą być jednoznacznie przypisane w konstruktorach instancji struct, które nie mają inicjalizatora : this(), więc wszystkie wcześniej nieprzypisane pola muszą zostać przypisane za pomocą dodanego konstruktora lub inicjalizatorów pól. Zobacz dotnet/csharplang#5552, dotnet/roslyn#58581.

Istnieją dwa sposoby inicjowania zmiennej do jej wartości domyślnej w języku C#: new() i default. W przypadku klas różnica jest widoczna, ponieważ new tworzy nowe wystąpienie i default zwraca wartość null. Różnica jest bardziej subtelna dla struktur, ponieważ w przypadku defaultstruktur funkcja zwraca wystąpienie z każdym polem/właściwością ustawioną na własną wartość domyślną. Dodaliśmy inicjatory pól dla struktur w języku C# 10. Inicjatory pól są wykonywane tylko wtedy, gdy jawnie zadeklarowany konstruktor jest uruchamiany. Co ważne, nie są one wykonywane podczas używania default czy tworzenia tablicy o dowolnym struct typie.

W wersji 17.0, jeśli istnieją inicjatory pól, ale nie zadeklarowane konstruktory, konstruktor bez parametrów jest syntetyzowany, który uruchamia inicjatory pól. Oznaczało to jednak, że dodanie lub usunięcie deklaracji konstruktora może mieć wpływ na to, czy konstruktor bez parametrów jest syntetyzowany, a w rezultacie może zmienić zachowanie klasy new().

Aby rozwiązać ten problem, w zestawie .NET SDK 6.0.200 (VS 17.1) kompilator nie syntetyzuje już konstruktora bez parametrów. Jeśli element struct zawiera inicjatory pól i nie ma jawnych konstruktorów, kompilator generuje błąd. Jeśli element struct zawiera inicjatory pól, musi zadeklarować konstruktor, ponieważ w przeciwnym razie inicjatory pól nigdy nie są wykonywane.

Ponadto wszystkie pola, które nie mają inicjatorów pól, muszą być przypisane w każdym struct konstruktorze, chyba że konstruktor ma : this() inicjator.

Przykład:

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

Sposobem obejścia jest zadeklarowanie konstruktora. Jeśli pola nie zostały wcześniej nieprzypisane, ten konstruktor może i często będzie pustym konstruktorem bez parametrów:

struct S
{
    int X = 1;
    int Y;

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

Specyfikatory formatu nie mogą zawierać nawiasów klamrowych

Wprowadzono w zestawie .NET SDK 6.0.200, Visual Studio 2022 w wersji 17.1. Specyfikatory formatu w ciągach interpolowanych nie mogą zawierać nawiasów klamrowych ({ lub }). W poprzednich wersjach {{ był interpretowany jako znak ucieczki {, a }} jako znak ucieczki } w specyfikatorze formatu. Teraz pierwszy } znak w specyfikatorze formatu kończy interpolację, a dowolny { znak jest błędem.

Dzięki temu przetwarzanie ciągów interpolowanych jest spójne z przetwarzaniem dla elementu System.String.Format:

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

X jest szesnastkowym zapisem wielkimi literami, a C jest wartością szesnastkową liczby 12.

Rozwiązaniem jest usunięcie dodatkowych nawiasów klamrowych w łańcuchu formatującym.

Więcej informacji na temat tej zmiany można znaleźć w powiązanym zgłoszeniu roslyn.

Typy nie mogą być nazwane required

Wprowadzono w programie Visual Studio 2022 w wersji 17.3. Począwszy od języka C# 11, typy nie mogą mieć nazwy required. Kompilator zgłosi błąd dla wszystkich takich nazw typów. Aby obejść ten problem, nazwa typu i wszystkie użycia muszą zostać zaznaczone za pomocą znacznika @.

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

Zostało to zrobione, ponieważ required jest teraz modyfikatorem składowym dla właściwości i pól.

Więcej informacji na temat tej zmiany można znaleźć w skojarzonym zgłoszeniu csharplang.