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:
- Es muss intern sein
- Es muss eine Klasse sein
- Es muss versiegelt sein
- Es muss nicht-statisch sein
- Es muss über einen internen oder öffentlichen parameterlosen Konstruktor verfügen.
- Es muss von System.Attribute erben.
- 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 field
zu 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.
Roslyn breaking changes