Freigeben über


Fehlerhafte Änderungen in Roslyn nach .NET 9.0.100 bis .NET 10.0.100

In diesem Dokument werden bekannte Änderungen in Roslyn nach der allgemeinen Version von .NET 9 (.NET SDK Version 9.0.100) bis zur allgemeinen Version von .NET 10 (.NET SDK Version 10.0.100) aufgeführt.

scoped in einer Lambda-Parameterliste ist jetzt immer ein Modifizierer.

Eingeführt in Visual Studio 2022, Version 17.13

C# 14 bietet die Möglichkeit, eine Lambda-Funktion mit Parametermodifizierern zu schreiben, ohne einen Parametertyp angeben zu müssen: https://github.com/dotnet/csharplang/blob/main/proposals/simple-lambda-parameters-with-modifiers.md

Im Rahmen dieser Arbeit wurde eine bahnbrechende Änderung akzeptiert, bei der scoped immer als Modifizierer in einem Lambda-Parameter behandelt wird, auch wenn er in der Vergangenheit als Typname akzeptiert wurde. Zum Beispiel:

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

ref struct @scoped { }

In C# 14 ist dies ein Fehler, da beide scoped Token als Modifizierer behandelt werden. Die Problemumgehung besteht darin, @ in der Typnamenposition wie folgt zu verwenden:

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

ref struct @scoped { }

Span<T> and ReadOnlySpan<T> Überladungen sind in mehr Szenarien in C# 14 und neuer anwendbar

Eingeführt in Visual Studio 2022, Version 17.13

C# 14 führt eine neue eingebaute Spannenkonvertierungen und Regeln für die Typinferenz. Dies bedeutet, dass im Vergleich zu C# 13 unterschiedliche Überladungen gewählt werden könnten, und manchmal könnte ein Mehrdeutigkeits-Kompilierfehler ausgelöst werden, weil eine neue Überladung anwendbar ist, es aber keine einzige beste Überladung gibt.

Das folgende Beispiel zeigt einige Ambiguitäten und mögliche Problemumgehungen. Beachten Sie, dass eine weitere Abhilfe für API-Autoren darin besteht, die 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

A Span<T> Überladung könnte in C# 14 gewählt werden, wo eine Überladung, die eine Schnittstelle nimmt, die von T[] (wie zum Beispiel IEnumerable<T>) wurde in C# 13 gewählt, und das kann zu einer ArrayTypeMismatchException zur Laufzeit, wenn sie mit einem kovarianten Array verwendet wird:

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

Aus diesem Grund, ReadOnlySpan<T> wird im Allgemeinen vorgezogen gegenüber Span<T> durch Überlastauflösung in C# 14. In einigen Fällen kann das zu Kompilierungspausen führen, zum Beispiel wenn es Überlastungen sowohl für Span<T> und ReadOnlySpan<T>, die beide denselben Spantyp annehmen und zurückgeben:

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

Wenn Sie C# 14 oder neuer verwenden und ein älteres .NET als net10.0 oder .NET Framework mit System.Memory Referenz, gibt es eine entscheidende Änderung mit Enumerable.Reverse und Arrays:

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

Auf net10.0, Es gibt Enumerable.Reverse(this T[]) die Vorrang hat und somit der Bruch vermieden wird. Ansonsten, MemoryExtensions.Reverse(this Span<T>) aufgelöst wird, was eine andere Semantik hat als Enumerable.Reverse(this IEnumerable<T>) (die früher in C# 13 und niedrigeren Versionen behoben wurden). Genauer gesagt, die Span Verlängerung führt die Umkehrung an Ort und Stelle durch und kehrt zurück void. Als Abhilfe kann man seine eigene Enumerable.Reverse(this T[]) oder verwenden Enumerable.Reverse ausdrücklich:

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

Diagnostik jetzt für musterbasierte Entsorgungsmethode in foreach

Eingeführt in Visual Studio 2022, Version 17.13

Zum Beispiel kann ein veralteter DisposeAsync Methode wird nun in 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;
    }
}

Zustand des Enumerator-Objekts bei der Entsorgung auf „nach“ setzen

Eingeführt in Visual Studio 2022, Version 17.13

Der Zustandsautomat für Enumeratoren erlaubte fälschlicherweise die Wiederaufnahme der Ausführung, nachdem der Enumerator entsorgt wurde.
Jetzt, MoveNext() auf einen entsorgten Enumerator gibt ordnungsgemäß zurück false ohne weiteren Benutzercode auszuführen.

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

UnscopedRefAttribute kann nicht mit alten Sicherheitsvorschriften verwendet werden

Eingeführt in Visual Studio 2022, Version 17.13

Die UnscopedRefAttribute Unbeabsichtigt betroffener Code, der von neuen Roslyn-Compiler-Versionen kompiliert wurde, selbst wenn der Code im Kontext der früheren Ref-Sicherheitsregeln kompiliert wurde (d. h. für C# 10 oder früher mit net6.0 oder früher). Das Attribut sollte jedoch in diesem Kontext keine Auswirkung haben, und dies ist jetzt behoben.

Code, der zuvor keine Fehler in C# 10 oder früher mit net6.0 oder früher gemeldet hat, kann jetzt nicht kompiliert werden:

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

Um Missverständnissen vorzubeugen (wenn Sie denken, dass das Attribut eine Wirkung hat, es aber tatsächlich nicht der Fall ist, weil Ihr Code mit den früheren Ref-Sicherheitsregeln kompiliert wurde), wird eine Warnung ausgegeben, wenn das Attribut in C# 10 oder früher mit net6.0 oder früher verwendet wird:

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 wird bei der Anmeldung validiert

Eingeführt in Visual Studio 2022, Version 17.13

Der Compiler validiert nun die Form von Microsoft.CodeAnalysis.EmbeddedAttribute wenn sie im Quelltext deklariert sind. Zuvor hatte der Compiler benutzerdefinierte Deklarationen dieses Attributs zugelassen, aber nur dann, wenn er nicht selbst einen generieren musste. Wir überprüfen jetzt Folgendes:

  1. Es muss intern sein
  2. Es muss eine Klasse sein
  3. Es muss versiegelt sein
  4. Es muss nicht-statisch sein
  5. Es muss über einen internen oder öffentlichen parameterlosen Konstruktor verfügen.
  6. Es muss von System.Attribute erben.
  7. Sie muss für jede Typdeklaration (Klasse, Struktur, Schnittstelle, Enum oder Delegat) zulässig sein.
namespace Microsoft.CodeAnalysis;

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

Der Ausdruck field in einem Eigenschaftenaccessor bezieht sich auf das synthetisierte Unterstützungsfeld

Eingeführt in Visual Studio 2022 Version 17.12

Der Ausdruck field bezieht sich bei Verwendung in einem Eigenschaftenaccessor auf ein synthetisiertes Unterstützungsfeld für die Eigenschaft.

Die Warnung CS9258 wird gezeigt, wenn der Bezeichner in Sprachversion 13 oder früher an ein anderes Symbol gebunden wäre.

Um das Generieren eines synthetisierten Sicherungsfelds zu vermeiden und auf das vorhandene Element zu verweisen, verwenden Sie stattdessen "this.field" oder "@field". Benennen Sie alternativ das vorhandene Mitglied und den Verweis auf dieses Mitglied um, um einen Konflikt mit fieldzu vermeiden.

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

Die Variable mit dem Namen field ist in einem Eigenschaftsaccessor unzulässig

Eingeführt in Visual Studio 2022 Version 17.14

Der Ausdruck field, wenn er in einem Eigenschaftsaccessor verwendet wird, bezieht sich auf ein synthetisiertes Hintergrundfeld für die Eigenschaft.

Der Fehler CS9272 wird gezeigt, wenn ein lokaler oder ein Parameter einer eingebetteten Funktion mit dem Namen field in einem Eigenschaftsaccessor deklariert wird.

Um den Fehler zu vermeiden, benennen Sie die Variable um, oder verwenden Sie @field in der Deklaration.

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- und record struct-Typen können keine Zeigertyp-Mitglieder definieren, auch wenn sie ihre eigenen Equals-Implementierungen bereitstellen.

Einführung in Visual Studio 2022 Version 17.14

Die Spezifikation für record class- und record struct Typen weist darauf hin, dass zeigertypen als Instanzfelder unzulässig sind. Dies wurde jedoch nicht ordnungsgemäß erzwungen, wenn der record class- oder record struct-Typ eine eigene Implementierung von Equals definiert hat.

Der Compiler verbietet dies jetzt richtig.

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

Das Erstellen von ausführbaren Programmen nur mit Metadaten erfordert einen Einsprungspunkt.

Eingeführt in Visual Studio 2022 Version 17.14

Zuvor war der Einstiegspunkt unbeabsichtigt nicht festgelegt, wenn ausführbare Dateien im Modus „Nur-Metadaten“ (auch als Referenzassemblys bezeichnet) gesendet wurden. Das ist jetzt korrigiert, bedeutet aber auch, dass ein fehlender Eintragspunkt ein Kompilierungsfehler ist:

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

Ebenso kann dies bei Verwendung des Befehlszeilenarguments /refonly oder der ProduceOnlyReferenceAssembly MSBuild-Eigenschaft beobachtet werden.