Dela via


Brytande ändringar i Roslyn efter .NET 9.0.100 genom .NET 10.0.100

Det här dokumentet listar kända icke-bakåtkompatibla ändringar i Roslyn efter den allmänna versionen av .NET 9 (.NET SDK version 9.0.100) till den allmänna versionen av .NET 10 (.NET SDK version 10.0.100).

scoped i en lambda-parameterlista är nu alltid en modifierare.

Introducerades i Visual Studio 2022 version 17.13

C# 14 introducerar möjligheten att skriva en lambda med parametermodifierare, utan att behöva ange en parametertyp: Enkla lambda-parametrar med modifierare

Som en del av det här arbetet accepterades en brytande förändring där scoped skall alltid behandlas som en modifierare i en lambda-parameter, även där den tidigare kunde ha accepterats som ett typnamn. Till exempel:

var v = (scoped scoped s) => { ... };

ref struct @scoped { }

I C# 14 är detta ett fel eftersom båda scoped token behandlas som modifierare. Lösningen är att använda @ i typnamnspositionen så här:

var v = (scoped @scoped s) => { ... };

ref struct @scoped { }

Span<T> och ReadOnlySpan<T> överbelastningar gäller för fler scenarier i C# 14 och senare

Introducerades i Visual Studio 2022 version 17.13

C# 14 introducerar nya inbyggda span-konverteringar och typinferensregler. Det innebär att olika överlagringar kan väljas jämfört med C# 13, och ibland kan ett tvetydigt kompileringsfel uppstå eftersom en ny överbelastning är tillämplig men det inte finns någon enda bästa överbelastning.

I följande exempel visas vissa tvetydigheter och möjliga lösningar. Observera att en annan lösning är att API-författare använder OverloadResolutionPriorityAttribute.

var x = new long[] { 1 };
Assert.Equal([2], x); // previously Assert.Equal<T>(T[], T[]), now ambiguous with Assert.Equal<T>(ReadOnlySpan<T>, Span<T>)
Assert.Equal([2], x.AsSpan()); // workaround

var y = new int[] { 1, 2 };
var s = new ArraySegment<int>(y, 1, 1);
Assert.Equal(y, s); // previously Assert.Equal<T>(T, T), now ambiguous with Assert.Equal<T>(Span<T>, Span<T>)
Assert.Equal(y.AsSpan(), s); // workaround

En Span<T>-överlagring kan väljas i C# 14 där en överlagring som använder ett gränssnitt som implementeras av T[] (till exempel IEnumerable<T>) valdes i C# 13, vilket kan leda till en ArrayTypeMismatchException vid körning om den används med en kovariant matris.

string[] s = new[] { "a" };
object[] o = s; // array variance

C.R(o); // wrote 1 previously, now crashes in Span<T> constructor with ArrayTypeMismatchException
C.R(o.AsEnumerable()); // workaround

static class C
{
    public static void R<T>(IEnumerable<T> e) => Console.Write(1);
    public static void R<T>(Span<T> s) => Console.Write(2);
    // another workaround:
    public static void R<T>(ReadOnlySpan<T> s) => Console.Write(3);
}

Därför föredras ReadOnlySpan<T> vanligtvis framför Span<T> vid överbelastningsupplösning i C# 14. I vissa fall kan det leda till kompileringsbrytningar, till exempel när det finns överlagringar för både Span<T> och ReadOnlySpan<T>, både tar och returnerar samma intervalltyp:

double[] x = new double[0];
Span<ulong> y = MemoryMarshal.Cast<double, ulong>(x); // previously worked, now compilation error
Span<ulong> z = MemoryMarshal.Cast<double, ulong>(x.AsSpan()); // workaround

static class MemoryMarshal
{
    public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span) => default;
    public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span) => default;
}

Enumerable.Reverse

När du använder C# 14 eller senare och riktar in sig på ett .NET som är äldre än net10.0 eller .NET Framework med System.Memory referens, sker en icke-bakåtkompatibel ändring med Enumerable.Reverse och matriser.

Försiktighet

Detta påverkar bara kunder som använder C# 14 och riktar in sig på .NET tidigare än net10.0, vilket är en konfiguration som inte stöds.

int[] x = new[] { 1, 2, 3 };
var y = x.Reverse(); // previously Enumerable.Reverse, now MemoryExtensions.Reverse

net10.0finns det Enumerable.Reverse(this T[]) som har företräde och därför undviks pausen. Annars löses MemoryExtensions.Reverse(this Span<T>) som har olika semantik än Enumerable.Reverse(this IEnumerable<T>) (som tidigare löstes i C# 13 och lägre). Specifikt utför Span-tillägget återföringen på plats och returnerar void. Som en lösning kan man definiera sina egna Enumerable.Reverse(this T[]) eller använda Enumerable.Reverse explicit:

int[] x = new[] { 1, 2, 3 };
var y = Enumerable.Reverse(x); // instead of 'x.Reverse();'

Diagnostik rapporteras nu för den mönsterbaserade borttagningsmetoden i foreach

Introducerades i Visual Studio 2022 version 17.13

Till exempel rapporteras nu en föråldrad DisposeAsync-metod i await foreach.

await foreach (var i in new C()) { } // 'C.AsyncEnumerator.DisposeAsync()' is obsolete

class C
{
    public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
    {
        throw null;
    }

    public sealed class AsyncEnumerator : System.IAsyncDisposable
    {
        public int Current { get => throw null; }
        public Task<bool> MoveNextAsync() => throw null;

        [System.Obsolete]
        public ValueTask DisposeAsync() => throw null;
    }
}

Ange tillståndet för uppräkningsobjektet till "efter" under bortskaffande

Introducerades i Visual Studio 2022 version 17.13

Tillståndsmaskinen för uppräknare tillät felaktigt att utförandet återupptas efter att uppräknaren hade frigjorts.
Nu returnerar MoveNext() på en borttagen uppräknare korrekt false utan att köra någon mer användarkod.

var enumerator = C.GetEnumerator();

Console.Write(enumerator.MoveNext()); // prints True
Console.Write(enumerator.Current); // prints 1

enumerator.Dispose();

Console.Write(enumerator.MoveNext()); // now prints False

class C
{
    public static IEnumerator<int> GetEnumerator()
    {
        yield return 1;
        Console.Write("not executed after disposal")
        yield return 2;
    }
}

Varna för redundanta mönster i enkla or mönster

Introducerades i Visual Studio 2022 version 17.13

I ett disjunkt or mönster som is not null or 42 eller is not int or string är det andra mönstret redundant och sannolikt ett resultat av missförstånd av prioritetordningen för not och or mönsterkombinatorer.
Kompilatorn ger en varning i vanliga fall om det här misstaget:

_ = o is not null or 42; // warning: pattern "42" is redundant
_ = o is not int or string; // warning: pattern "string" is redundant

Det är troligt att användaren menade is not (null or 42) eller is not (int or string) i stället.

UnscopedRefAttribute kan inte användas med gamla referenssäkerhetsregler

Introducerades i Visual Studio 2022 version 17.13

Den UnscopedRefAttribute:an påverkade oavsiktligt kod som kompilerats av nya Roslyn-kompilatorversioner även när koden kompilerades i samband med de tidigare referenssäkerhetsreglerna (dvs. som riktar sig mot C# 10 eller tidigare med net6.0 eller tidigare). Attributet bör dock inte ha någon effekt i den kontexten och det är nu åtgärdat.

Kod som tidigare inte rapporterar några fel i C# 10 eller tidigare med net6.0 eller tidigare kan nu inte kompileras:

using System.Diagnostics.CodeAnalysis;
struct S
{
    public int F;

    // previously allowed in C# 10 with net6.0
    // now fails with the same error as if the [UnscopedRef] wasn't there:
    // error CS8170: Struct members cannot return 'this' or other instance members by reference
    [UnscopedRef] public ref int Ref() => ref F;
}

För att förhindra missförstånd (att tro att attributet har en effekt, men det gör det faktiskt inte eftersom koden kompileras med de tidigare referenssäkerhetsreglerna), rapporteras en varning när attributet används i C# 10 eller tidigare med net6.0 eller tidigare:

using System.Diagnostics.CodeAnalysis;
struct S
{
    // both are errors in C# 10 with net6.0:
    // warning CS9269: UnscopedRefAttribute is only valid in C# 11 or later or when targeting net7.0 or later.
    [UnscopedRef] public ref int Ref() => throw null!;
    public static void M([UnscopedRef] ref int x) { }
}

Microsoft.CodeAnalysis.EmbeddedAttribute valideras vid deklarationen

Introducerades i Visual Studio 2022 version 17.13

Kompilatorn verifierar nu formen på Microsoft.CodeAnalysis.EmbeddedAttribute när den deklareras i källan. Tidigare skulle kompilatorn tillåta användardefinierade deklarationer av det här attributet, men bara när det inte behövde generera en själv. Vi verifierar nu att:

  1. Det måste vara internt
  2. Det måste vara en klass
  3. Den måste vara förseglad
  4. Det måste vara icke-statiskt
  5. Den måste ha en intern eller offentlig parameterlös konstruktor
  6. Den måste ärva från System.Attribute.
  7. Det måste vara tillåtet för alla typer av deklarationer (klass, struktur, gränssnitt, uppräkning eller delegering)
namespace Microsoft.CodeAnalysis;

// Previously, sometimes allowed. Now, CS9271
public class EmbeddedAttribute : Attribute {}

Uttryck field i en egenskapsåtkomstor refererar till syntetiserat bakgrundsfält

Introducerades i Visual Studio 2022 version 17.12

Uttrycket field, när det används i en egenskapsaccessor, refererar till ett syntetiserat backing field för egenskapen.

Varningen CS9258 rapporteras när identifieraren skulle ha varit bunden till en annan symbol i språkversion 13 eller tidigare.

Om du vill undvika att generera ett syntetiserat bakgrundsfält och referera till den befintliga medlemmen använder du "this.field" eller "@field" i stället. Alternativt kan du byta namn på den befintliga medlemmen och referensen till den medlemmen för att undvika en konflikt med field.

class MyClass
{
    private int field = 0;

    public object Property
    {
        get
        {
            // warning CS9258: The 'field' keyword binds to a synthesized backing field for the property.
            // To avoid generating a synthesized backing field, and to refer to the existing member,
            // use 'this.field' or '@field' instead.
            return field;
        }
    }
}

Variabel med namnet field är inte tillåten i en property accessor

Introducerades i Visual Studio 2022 version 17.14

Uttrycket field, när det används i en egenskapsaccessor, refererar till ett syntetiserat backing field för egenskapen.

Felet CS9272 rapporteras när en lokal variabel eller en parameter i en kapslad funktion med namnet field deklareras i en egenskapsaccessor.

Undvik felet genom att byta namn på variabeln eller använda @field i deklarationen.

class MyClass
{
    public object Property
    {
        get
        {
            // error CS9272: 'field' is a keyword within a property accessor.
            // Rename the variable or use the identifier '@field' instead.
            int field = 0;
            return @field;
        }
    }
}

record och record struct typer kan inte definiera medlemmar av pekartyp, även när de tillhandahåller egna Equals-implementeringar

Introducerades i Visual Studio 2022 version 17.14

Specifikationen för typerna record class och record struct anger att alla pekartyper inte tillåts som instansfält. Detta tillämpades dock inte korrekt när record class eller record struct typ definierade sin egen Equals implementering.

Kompilatorn förbjuder nu detta korrekt.

unsafe record struct R(
    int* P // Previously fine, now CS8908
)
{
    public bool Equals(R other) => true;
}

För att generera enbart metadata-körbara filer krävs en startpunkt.

Introducerades i Visual Studio 2022 version 17.14

Tidigare togs startpunkten oavsiktligt bort när körbara filer skulle genereras i endast metadataläge (kallas även ref-sammansättningar). Det är nu korrigerat, men det innebär också att en saknad startpunkt är ett kompileringsfel:

// previously successful, now fails:
CSharpCompilation.Create("test").Emit(new MemoryStream(),
    options: EmitOptions.Default.WithEmitMetadataOnly(true))

CSharpCompilation.Create("test",
    // workaround - mark as DLL instead of EXE (the default):
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
    .Emit(new MemoryStream(),
        options: EmitOptions.Default.WithEmitMetadataOnly(true))

På samma sätt kan detta observeras när du använder kommandoradsargumentet /refonly eller egenskapen ProduceOnlyReferenceAssembly MSBuild.

partial kan inte vara en returtyp av metoder

Introducerades i Visual Studio 2022 version 17.14

Språkfunktionen partiella händelser och konstruktorer tillåter partial modifieraren på fler platser och kan därför inte vara en returtyp om den inte är undantagen:

class C
{
    partial F() => new partial(); // previously worked
    @partial F() => new partial(); // workaround
}

class partial { }

extension behandlas som ett kontextuellt nyckelord

Introducerades i Visual Studio 2022 version 17.14. Från och med C# 14 har nyckelordet extension ett särskilt syfte när det gäller att ange tilläggscontainrar. Detta ändrar hur kompilatorn tolkar vissa kodkonstruktioner.

Om du behöver använda "extension" som en identifierare i stället för ett nyckelord kan du undvika det med prefixet @ : @extension. Detta instruerar kompilatorn att behandla den som en vanlig identifierare i stället för ett nyckelord.

Kompilatorn parsar detta som en tilläggscontainer i stället för en konstruktor.

class @extension
{
    extension(object o) { } // parsed as an extension container
}

Kompilatorn parsar inte detta som en metod med returtypen extension.

class @extension
{
    extension M() { } // will not compile
}

Introducerades i Visual Studio 2026 version 18.0. Identifieraren "extension" kanske inte används som ett typnamn, så följande kompileras inte:

using extension = ...; // alias may not be named "extension"
class extension { } // type may not be named "extension"
class C<extension> { } // type parameter may not be named "extension"

Partiella egenskaper och händelser är nu implicit virtuella och offentliga

Introducerades i Visual Studio 2026 version 18.0 förhandsversion 1

Vi har korrigerat en inkonsekvens där partiella gränssnittsegenskaper och händelser inte skulle vara implicita och , till skillnad från deras icke-partiella motsvarigheter. Den här inkonsekvensen bevaras dock för partiella gränssnittsmetoder för att undvika en större icke-bakåtkompatibel ändring. Observera att Visual Basic och andra språk som inte stöder standardgränssnittsmedlemmar börjar kräva att implementera implicit virtuella partial gränssnittsmedlemmar.

Om du vill behålla det tidigare beteendet markerar du uttryckligen gränssnittsmedlemmar som partial (om de inte har några åtkomstmodifierare) och private (om de inte har modifieraren sealed som innebär private, och de inte redan har modifierare sealed eller virtual).

System.Console.Write(((I)new C()).P); // wrote 1 previously, writes 2 now

partial interface I
{
    public partial int P { get; }
    public partial int P => 1; // implicitly virtual now
}

class C : I
{
    public int P => 2; // implements I.P
}
System.Console.Write(((I)new C()).P); // inaccessible previously, writes 1 now

partial interface I
{
    partial int P { get; } // implicitly public now
    partial int P => 1;
}

class C : I;

Det rapporteras om saknade ParamCollectionAttribute i fler fall

Introducerades i Visual Studio 2026 version 18.0

Om du kompilerar en .netmodule (observera att detta inte gäller för vanliga DLL/EXE-kompileringar) och har en lambda eller en lokal funktion med en params samlingsparameter, och ParamCollectionAttribute inte hittas, rapporteras nu ett kompileringsfel (eftersom attributet nu måste genereras på den syntetiserade metoden, men själva attributtypen syntetiseras inte av kompilatorn till en .netmodule). Du kan kringgå det genom att definiera attributet själv.

using System;
using System.Collections.Generic;
class C
{
    void M()
    {
        Func<IList<int>, int> lam = (params IList<int> xs) => xs.Count; // error if ParamCollectionAttribute does not exist
        lam([1, 2, 3]);

        int func(params IList<int> xs) => xs.Count; // error if ParamCollectionAttribute does not exist
        func(4, 5, 6);
    }
}