次の方法で共有


.NET 9.0.100 から .NET 10.0.100 以降の Roslyn での破壊的変更

このドキュメントでは、.NET 9 一般リリース (.NET SDK バージョン 9.0.100) から .NET 10 一般リリース (.NET SDK バージョン 10.0.100) までの Roslyn での既知の破壊的変更を示します。

ラムダ パラメーター リスト内の scoped は常に修飾子と見なされる

Visual Studio 2022 バージョン 17.13 で導入

C# 14 では、パラメーター型を指定せずに、パラメーター修飾子を使用してラムダを記述する機能が導入されています。 修飾子を持つ単純なラムダ パラメーター

この作業の一環として、以前は型名として受け入れられていた場合でも、scoped を常にラムダ パラメーターの修飾子として扱うという重大な変更が行われました。 次に例を示します。

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

ref struct @scoped { }

C# 14 では、両方の scoped トークンが修飾子として扱われるため、これはエラーになります。 回避策としては、次のように型名の位置で @ を使用します。

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

ref struct @scoped { }

Span<T> および ReadOnlySpan<T> オーバーロードは、C# 14 以降の他のシナリオに適用されます

Visual Studio 2022 バージョン 17.13 で導入

C# 14 では、新しい組み込みのスパン変換と型推論規則が導入されています。 つまり、C# 13 と比較して異なるオーバーロードが選択される可能性があり、新しいオーバーロードが適用されるものの最適なオーバーロードがないため、あいまいなコンパイル時エラーが発生することがあります。

次の例は、いくつかのあいまいさと考えられる回避策を示しています。 別の回避策は、API 作成者が 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

C# 14 では、C# 13 では Span<T> が実装するインターフェイスを受け取るオーバーロード (例: T[]) が選ばれていたところで、IEnumerable<T> のオーバーロードが選ばれることがあります。これは、共変配列で使用した場合、実行時に ArrayTypeMismatchException が生じる可能性があります。

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

そのため、ReadOnlySpan<T> は一般に、C# 14 でのオーバーロード解決によって Span<T> よりも優先されます。 場合によっては、これによってコンパイルが中断される可能性があります。たとえば、Span<T>ReadOnlySpan<T> の両方に対するオーバーロードがある場合、どちらも同じスパン型を取得して返す場合などです。

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

C# 14 以降を使用し、net10.0参照を使用して System.Memory または .NET Framework より古い .NET を対象とする場合、Enumerable.Reverseと配列に重大な変更があります。

注意事項

これは、C# 14 を使用し、サポートされていない構成である net10.0 より前の .NET をターゲットにしているお客様にのみ影響します。

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

net10.0 では優先される Enumerable.Reverse(this T[]) があるため、中断は回避されます。 それ以外の場合、MemoryExtensions.Reverse(this Span<T>)Enumerable.Reverse(this IEnumerable<T>) とは異なるセマンティクスで解決されます (Enumerable.Reverse(this IEnumerable<T>) は以前、C# 13 以下で解決されていました)。 具体的には、Span 拡張はその場で反転を行い、voidを返します。 回避策として、独自の Enumerable.Reverse(this T[]) を定義することも、Enumerable.Reverse を明示的に使用することもできます。

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

パターンベースの廃棄方法の診断が foreach で報告されるようになりました

Visual Studio 2022 バージョン 17.13 で導入

たとえば、古い DisposeAsync メソッドが 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;
    }
}

破棄中に列挙子オブジェクトの状態を "after" に設定する

Visual Studio 2022 バージョン 17.13 で導入

列挙子のステート マシンで、列挙子が破棄された後の実行の再開が誤って許可されました。
現在、破棄された列挙子の MoveNext() は、それ以上ユーザー コードを実行せずに false を正しく返します。

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

単純な or パターンで冗長パターンを警告する

Visual Studio 2022 バージョン 17.13 で導入

oris not null or 42などの不整合なis not int or stringパターンでは、2 番目のパターンは冗長であり、notorパターンの組み合わせ子の優先順位を誤解した可能性があります。
コンパイラは、この間違いの一般的なケースで警告を提供します。

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

ユーザーが代わりに is not (null or 42) または is not (int or string) を意図している可能性があります。

UnscopedRefAttribute は古い ref 安全規則では使用できません

Visual Studio 2022 バージョン 17.13 で導入

新しい Roslyn コンパイラ バージョンによってコンパイルされたコードには、UnscopedRefAttribute が、以前の ref 安全規則のコンテキスト (すなわち、net6.0 以前で C# 10 以前を対象)でコンパイルされた場合でも、意図せず影響を及ぼしました。 ただし、この属性はそのコンテキストで効果を持つべきではないため、現在は修正されています。

以前に net6.0 以前で C# 10 以前のエラーを報告しなかったコードは、現在コンパイルに失敗する可能性があります。

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

誤解を防ぐために (属性には効果があるが、実際にはコードが以前の ref 安全規則でコンパイルされているため実際には効果があると考える)、この属性が net6.0 以前の C# 10 以前で使用されている場合に警告が報告されます。

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 は宣言時に検証されます。

Visual Studio 2022 バージョン 17.13 で導入

コンパイラは、ソースで宣言されたときに Microsoft.CodeAnalysis.EmbeddedAttribute の形状を検証するようになりました。 以前は、コンパイラは、この属性のユーザー定義宣言を許可していましたが、それ自体を生成する必要がない場合に限られていました。 現在は次の点を検証します。

  1. 内部である必要がある
  2. クラスである必要があります
  3. シールされている必要がある
  4. 静的以外である必要がある
  5. 内部またはパブリック パラメーターレス コンストラクターが必要である
  6. System.Attribute から継承する必要がある
  7. 任意の型宣言 (クラス、構造体、インターフェイス、列挙型、またはデリゲート) で許可する必要がある
namespace Microsoft.CodeAnalysis;

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

プロパティ アクセサにおける式 field は、生成されたバッキングフィールドを参照しています。

Visual Studio 2022 バージョン 17.12 で導入

field は、プロパティ アクセサー内で使用される場合、プロパティの合成バッキング フィールドを参照します。

警告 CS9258 は、識別子が言語バージョン 13 以前の別のシンボルにバインドされていた場合に報告されます。

合成バッキング フィールドの生成を回避し、既存のメンバーを参照するには、代わりに 'this.field' または '@field' を使用します。 または、既存のメンバーとそのメンバーへの参照の名前を変更して、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;
        }
    }
}

プロパティ アクセサーでは field という名前の変数が許可されない

Visual Studio 2022 バージョン 17.14 で導入

field は、プロパティ アクセサー内で使用される場合、プロパティの合成バッキング フィールドを参照します。

エラー CS9272 は、プロパティ アクセサーでローカル変数または入れ子になった関数のパラメーターが field という名前で宣言されたときに報告されます。

このエラーを回避するには、変数の名前を変更するか、宣言で @field を使用します。

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 型および record struct 型は、独自のEquals実装を提供する場合でも、ポインター型のメンバーを定義できません

Visual Studio 2022 バージョン 17.14 で導入

record class 型と record struct 型の仕様では、インスタンス フィールドとしてポインター型が許可されていないことを示していました。 ただし、record class 型または record struct 型が独自の Equals 実装を定義した場合、この仕様は正しく適用されませんでした。

これをコンパイラが正しく禁止するようになりました。

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

メタデータのみの実行可能ファイルを出力するには、エントリポイントが必要です

Visual Studio 2022 バージョン 17.14 で導入

以前は、メタデータのみのモード (ref アセンブリとも呼ばれます) で実行可能ファイルを出力する際に、エントリポイントが意図せず設定解除されていました。 これは修正されましたが、不足しているエントリポイントがコンパイル エラーであることを意味します。

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

同様に、これは、コマンド ライン引数 /refonly または ProduceOnlyReferenceAssembly MSBuild プロパティを使用する場合に確認できます。

partial メソッドの戻り値の型にすることはできません

Visual Studio 2022 バージョン 17.14 で導入

部分イベントとコンストラクター 言語機能を使用すると、partial 修飾子をより多くの場所で使用できるため、エスケープされない限り戻り値の型にすることはできません。

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

class partial { }

extension コンテキスト キーワードとして扱われる

Visual Studio 2022 バージョン 17.14 で導入されました。 C# 14 以降では、 extension キーワードは拡張コンテナーを示す特別な目的を果たします。 これにより、コンパイラが特定のコードコンストラクトを解釈する方法が変更されます。

キーワードではなく識別子として "extension" を使用する必要がある場合は、 @ プレフィックス ( @extension) でエスケープします。 これは、キーワードではなく通常の識別子として扱うようにコンパイラに指示します。

コンパイラはこれをコンストラクターではなく拡張コンテナーとして解析します。

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

コンパイラは、戻り値の型 extensionを持つメソッドとしてこれを解析できません。

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

Visual Studio 2026 バージョン 18.0 で導入されました。 "extension" 識別子は型名として使用できない可能性があるため、次のコードはコンパイルされません。

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"

部分的なプロパティとイベントが暗黙的に仮想およびパブリックになりました

Visual Studio 2026 バージョン 18.0 プレビュー 1 で導入されました

部分インターフェイスのプロパティとイベントが暗黙的にされず、非部分等価物とは異なりvirtualというpublicを修正しました。 ただし、この不整合は、より大きな破壊的変更を回避するために、部分インターフェイス メソッドでは 保持されます 。 既定のインターフェイス メンバーをサポートしていない Visual Basic やその他の言語では、暗黙的に仮想 partial インターフェイス メンバーを実装する必要が生じ始める点に注意してください。

前の動作を維持するには、インターフェイスメンバーpartialprivateとして明示的にマークし(アクセシビリティ修飾子がない場合)、sealed (privateを意味するsealed修飾子がなく、修飾子virtualまたはsealedがない場合) を指定します。

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;

より多くの場合に不足 ParamCollectionAttribute が報告されます

Visual Studio 2026 バージョン 18.0 で導入されました

.netmoduleをコンパイルする場合 (通常の DLL/EXE コンパイルには適用されません)、params コレクション パラメーターを持つラムダまたはローカル関数があり、ParamCollectionAttributeが見つからない場合は、コンパイル エラーが報告されるようになりました (合成メソッドで属性を出力する必要がありますが、属性の型自体がコンパイラによって.netmoduleに合成されていないため)。 この問題を回避するには、属性を自分で定義します。

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