Icke-bakåtkompatibla ändringar i Roslyn efter .NET 10.0.100 till och med .NET 11.0.100

Det här dokumentet visar kända icke-bakåtkompatibla ändringar i Roslyn efter .NET 10 allmänna versionen (.NET SDK version 10.0.100) till .NET 11 allmän version (.NET SDK version 11.0.100).

Säkerhetskontexten för ett samlingsuttryck av typen Span/ReadOnlySpan är nu deklarationsblock

Introduced in Visual Studio 2026 version 18.3

C#-kompilatorn gjorde en icke-bakåtkompatibel ändring för att korrekt följa referenssäkerhetsreglerna i specifikationen för funktionen samlingsuttryck. Mer specifikt följande sats:

  • Om måltypen är en spänntypSystem.Span<T> eller System.ReadOnlySpan<T>är samlingsuttryckets safe-context declaration-block.

Tidigare använde kompilatorn safe-context function-member i den här situationen. Vi har nu gjort en ändring för att använda deklarationsblock enligt specifikationen. Detta kan orsaka att nya fel visas i befintlig kod, till exempel i scenariot nedan:

scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
foreach (var x in new[] { 1, 2 })
{
    Span<int> items = [x];
    if (x == 1)
        items1 = items; // previously allowed, now an error

    if (x == 2)
        items2 = items; // previously allowed, now an error
}

Om koden påverkas av den här icke-bakåtkompatibla ändringen bör du överväga att använda en matristyp för relevanta samlingsuttryck i stället:

scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
foreach (var x in new[] { 1, 2 })
{
    int[] items = [x];
    if (x == 1)
        items1 = items; // ok, using 'int[]' conversion to 'Span<int>'

    if (x == 2)
        items2 = items; // ok
}

Du kan också flytta samlingsuttrycket till ett omfång där tilldelningen tillåts:

scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
Span<int> items = [0];
foreach (var x in new[] { 1, 2 })
{
    items[0] = x;
    if (x == 1)
        items1 = items; // ok

    if (x == 2)
        items2 = items; // ok
}

Se även https://github.com/dotnet/csharplang/issues/9750.

Scenarier som kräver att kompilatorn syntetiserar en ref readonly återvändande delegering kräver nu att System.Runtime.InteropServices.InAttribute-typen är tillgänglig.

Introduced in Visual Studio 2026 version 18.3

C#-kompilatorn gjorde en brytande ändring för att korrekt generera metadata för att returnera syntetiserade ombud

Detta kan orsaka att felet CS0518: Den fördefinierade typen "System.Runtime.InteropServices.InAttribute" inte har definierats eller importerats" visas i befintlig kod, till exempel i scenarierna nedan:

var d = this.MethodWithRefReadonlyReturn;
var d = ref readonly int () => ref x;

Om din kod påverkas av denna icke-bakåtkompatibla ändring kan du överväga att lägga till en referens till en assembly som definierar System.Runtime.InteropServices.InAttribute i ditt projekt.

Scenarier som ref readonly använder lokala funktioner kräver nu tillgänglighet av System.Runtime.InteropServices.InAttribute typen.

Introduced in Visual Studio 2026 version 18.3

C#-kompilatorn gjorde en icke-bakåtkompatibel förändring för att på rätt sätt generera metadata för ref readonly returnerande lokala funktioner.

Detta kan orsaka att felet CS0518: Den fördefinierade typen "System.Runtime.InteropServices.InAttribute" inte har definierats eller importerats" visas i befintlig kod, till exempel i scenariot nedan:

void Method()
{
    ...
    ref readonly int local() => ref x;
    ...
}

Om din kod påverkas av denna icke-bakåtkompatibla ändring kan du överväga att lägga till en referens till en assembly som definierar System.Runtime.InteropServices.InAttribute i ditt projekt.

Dynamisk utvärdering av &&/|| operatorer tillåts inte med den vänstra operanden statiskt skriven som ett gränssnitt.

Introduced in Visual Studio 2026 version 18.3

C#-kompilatorn rapporterar nu ett fel när en gränssnittstyp används som vänster operand för en logisk && eller || operator med en dynamic höger operand. Tidigare kompilerades kod för en gränssnittstyp med true/false operatorer, men misslyckades vid körning med en RuntimeBinderException eftersom körningsbindaren inte kan anropa operatorer som definierats i gränssnitt.

Den här ändringen förhindrar ett körningsfel genom att rapportera det vid kompileringstid i stället. Felmeddelandet är:

fel CS7083: Uttrycket måste implicit konverteras till booleskt värde eller så får dess typ "I1" inte vara ett gränssnitt och måste definiera operatorn "false".

interface I1
{
    static bool operator true(I1 x) => false;
    static bool operator false(I1 x) => false;
}

class C1 : I1
{
    public static C1 operator &(C1 x, C1 y) => x;
    public static bool operator true(C1 x) => false;
    public static bool operator false(C1 x) => false;
}

void M()
{
    I1 x = new C1();
    dynamic y = new C1();
    _ = x && y; // error CS7083: Expression must be implicitly convertible to Boolean or its type 'I1' must not be an interface and must define operator 'false'.
}

Om din kod påverkas av den här brytförändringen kan du överväga att ändra den statiska typen av den vänstra operanden från en gränssnittstyp till en konkret klass eller till dynamic-typen:

void M()
{
    I1 x = new C1();
    dynamic y = new C1();
    _ = (C1)x && y; // Valid - uses operators defined on C1
    _ = (dynamic)x && y; // Valid - uses operators defined on C1
}

Se även https://github.com/dotnet/roslyn/issues/80954.

nameof(this.) i attribut är inte tillåtet

Introducerad i Visual Studio 2026 version 18.3 och .NET 10.0.200

Att använda this eller base nyckelord i nameof i ett attribut tilläts tidigare oavsiktligt i Roslyn från och med C# 12 och har nu korrekt blivit otillåtet för att matcha språkspecifikationen. Den här ändringen kan mildras genom att ta bort this. och komma åt medlemmen utan kvalificerare.

Se även https://github.com/dotnet/roslyn/issues/82251.

class C
{
    string P;
    [System.Obsolete(nameof(this.P))] // now disallowed
    [System.Obsolete(nameof(P))] // workaround
    void M() { }
}

Parsning av "med" i en switch-expression-arm

Introduced in Visual Studio 2026 version 18.4

Se https://github.com/dotnet/roslyn/issues/81837 och https://github.com/dotnet/roslyn/pull/81863

Tidigare, när du ser följande, skulle kompilatorn behandla (X.Y)when som ett cast-uttryck. Mer specifikt gjuter du kontextidentifieraren when till (X.Y):

x switch
{
    (X.Y) when
}

Detta var oönskat och innebar att en enkel when kontroll av mönstret (som (X.Y) when a > b =>) inte skulle parsas korrekt. Nu behandlas detta som ett konstant mönster (X.Y) följt av en when clause.

with() som ett samlingsuttryckselement behandlas det som samlingskonstruktionsargument

Introduced in Visual Studio 2026 version 18.4

with(...) när det används som ett element i ett samlingsuttryck, och när LangVersion är inställt på 15 eller högre, är bunden som argument som skickas till konstruktorn eller fabriksmetoden som används för att skapa samlingen, i stället för som ett anropsuttryck för en metod med namnet with.

Om du vill binda till en metod med namnet withanvänder du @with i stället.

object x, y, z = ...;
object[] items;

items = [with(x, y), z];  // C# 14: call to with() method; C# 15: error args not supported for object[]
items = [@with(x, y), z]; // call to with() method
object with(object a, object b) { ... }

Pekartyper kräver inte längre en osäker kontext

Introduced in Visual Studio 2026 version 18.7

I en framtida C#-version (för närvarande i langversion:preview), kräver pekartyper (t.ex. int*, delegate*<void>) inte längre en osäker kontext. Endast indirekta pekaråtgärder (dereference, medlemsåtkomst via ->, elementåtkomst osv.) kräver osäkert kodläge. Detta är en del av funktionen icke-säker evolution.

Eftersom pekartyper nu är lagliga i säkra kontexter kan överlagringsupplösning beakta kandidater som tidigare exkluderats. Detta kan orsaka nya tvetydighetsfel:

using System;

class Program
{
    static void Main()
    {
        M(x => { }); // C# 14: prints "2"; C# preview: error CS0121 (ambiguous)
    }

    static void M(F1 f) { Console.WriteLine(1); }
    static void M(F2 f) { Console.WriteLine(2); }
}

unsafe delegate void F1(int* x);
delegate void F2(int x);

Tidigare kunde lambda x => { } inte konverteras till F1 i en säker kontext eftersom int* det krävdes en osäker kontext, så det var bara M(F2) tillämpligt. Nu när det int* är lagligt i säkra sammanhang är lambda konvertibel till båda delegaterna, vilket ger ett tvetydigt fel.

Om din kod påverkas av ändringen som orsakar tvetydighet, lägger du till uttryckliga parametertyper i lambdan för att undvika tvetydighet.

M((int x) => { }); // Resolves to M(F2)

En oinitialiserad stackalloc inuti SkipLocalsInit kräver ett osäkert sammanhang

Introduced in Visual Studio 2026 version 18.7

I en framtida C#-version (för närvarande i langversion:preview), kräver ett stackalloc uttryck utan initialiserare i en metod som markerats med [SkipLocalsInit] nu en osäker kontext, även när måltypen är Span<T>. Detta beror på SkipLocalsInit att förhindrar nollinitiering av det allokerade minnet, vilket gör det möjligt att läsa onitialiserade data – ett minnessäkerhetsproblem. Detta är en del av funktionen för osäker utveckling .

[System.Runtime.CompilerServices.SkipLocalsInit]
void M()
{
    Span<int> a = stackalloc int[5];           // previously ok, now error CS9361
    Span<int> b = stackalloc int[] { 1, 2 };   // ok (has initializer)
    Span<int> c = stackalloc int[2] { 1, 2 };  // ok (has initializer)
}

Om koden påverkas kan du antingen:

  • Lägg till ett unsafe block runt stackalloc:
    [SkipLocalsInit]
    void M()
    {
        Span<int> a;
        unsafe { a = stackalloc int[5]; }
    }
    
  • Eller ange en initialiserare så att minnet initieras helt:
    [SkipLocalsInit]
    void M()
    {
        Span<int> a = stackalloc int[5] { 0, 0, 0, 0, 0 };
    }