次の方法で共有


ImmutableArrays 用の Roslyn アナライザーとコード対応ライブラリ

.NET Compiler Platform ("Roslyn") は、コード対応ライブラリの構築に役立ちます。 コード対応ライブラリには、ライブラリを最適な方法で使用したり、エラーを回避したりするのに役立つツール (Roslyn アナライザー) を使用できる機能が用意されています。 このトピックでは、System.Collections.Immutable NuGet パッケージを使用するときに一般的なエラーをキャッチする実際の Roslyn アナライザーを構築する方法について説明します。 この例では、アナライザーによって検出されたコードの問題に対してコード修正を提供する方法も示します。 Visual Studio 電球 UI にコード修正プログラムが表示され、コードの修正プログラムを自動的に適用できます。

作業の開始

この例をビルドするには、次のものが必要です。

  • Visual Studio 2015 (Express Edition ではありません) 以降のバージョン。 Visual Studio Community Edition 無料で使用できます
  • Visual Studio SDK。 また、Visual Studio をインストールするときに、Common Tools の下にある Visual Studio Extensibility Tools を確認して、SDK を同時にインストールすることもできます。 Visual Studio を既にインストールしている場合は、メイン メニューの [ファイル] [新しいプロジェクト] に移動し、左側のナビゲーション ウィンドウで [C# ] を選択し、[拡張を選択して、この SDK をインストールすることもできます。 [Visual Studio Extensibility Tools をインストール] 階層リンク プロジェクト テンプレートを選ぶと、SDK をダウンロードしてインストールするように求められます。
  • .NET Compiler Platform ("Roslyn") SDK。 この SDK をインストールするには、メイン メニューの [ファイル][新しいプロジェクト] に移動し、左側のナビゲーション ウィンドウで [C# ] を選択し、[機能拡張選択します。 [.NET Compiler Platform SDKのダウンロード] を選択すると、階層リンク プロジェクト テンプレートをダウンロードしてインストールするように求められます。 この SDK には、Roslyn Syntax Visualizer が含まれています。 この便利なツールは、アナライザーで検索する必要があるコード モデルの種類を把握するのに役立ちます。 アナライザー インフラストラクチャは特定のコード モデルの種類に対してコードを呼び出します。そのため、コードは必要なときにのみ実行され、関連するコードの分析にのみ集中できます。

何がそんなに問題ですか。

ImmutableArray (たとえば、System.Collections.Immutable.ImmutableArray<T>) サポートを備えたライブラリを提供するとします。 C# 開発者には、.NET 配列に関する多くの経験があります。 ただし、実装で使用される ImmutableArrays と最適化手法の性質により、C# 開発者の直感により、ライブラリのユーザーは、次に説明するように壊れたコードを記述します。 さらに、ユーザーには実行時までエラーが表示されません。これは、Visual Studio と .NET で使用される品質エクスペリエンスではありません。

ユーザーは、次のようなコードの記述に慣れている。

var a1 = new int[0];
Console.WriteLine("a1.Length = {0}", a1.Length);
var a2 = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("a2.Length = {0}", a2.Length);

後続のコード行を入力する空の配列を作成し、コレクション初期化子構文を使用することは、C# 開発者にとってよく知られています。 ただし、ImmutableArray に対して同じコードを記述すると、実行時にクラッシュします。

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);

最初のエラーは、基になるデータ ストレージをラップするために構造体を使用する ImmutableArray 実装が原因です。 default(T) 式がゼロまたは null のすべてのメンバーを持つ構造体を返すことができるように、構造体にはパラメーターなしのコンストラクターが必要です。 コードが b1.Lengthにアクセスすると、ImmutableArray 構造体に基になるストレージ配列がないため、実行時の null 逆参照エラーが発生します。 空の ImmutableArray を作成する正しい方法は ImmutableArray<int>.Emptyです。

コレクション初期化子のエラーは、ImmutableArray.Add メソッドが呼び出すたびに新しいインスタンスを返すからです。 ImmutableArrays は変更されないため、新しい要素を追加すると、新しい ImmutableArray オブジェクトが返されます (パフォーマンス上の理由から、以前に既存の ImmutableArray とストレージを共有する可能性があります)。 b2 は、Add() を 5 回呼び出す前に最初の ImmutableArray を指しているため、b2 は既定の ImmutableArray です。 その Length を呼び出すと、null 逆参照エラーでクラッシュします。 手動で Add を呼び出さずに ImmutableArray を初期化する正しい方法は、ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})を使用する方法です。

アナライザーをトリガーする関連する構文ノードの種類を検索する

アナライザーの構築を開始するには、まず、検索する必要がある SyntaxNode の種類を調べてください。 メニューの [表示]>[その他のウィンドウ]>[Roslyn Syntax Visualizer] から Syntax Visualizer を起動します。

b1を宣言する行にエディターのキャレットを配置します。 構文ビジュアライザーで、あなたが構文ツリーの LocalDeclarationStatement ノードにいることが示されています。 このノードには VariableDeclarationがあり、VariableDeclaratorがあり、EqualsValueClauseがあり、最後に ObjectCreationExpressionがあります。 ノードの構文ビジュアライザー ツリーをクリックすると、エディター ウィンドウの構文が強調表示され、そのノードによって表されるコードが表示されます。 SyntaxNode サブタイプの名前は、C# 文法で使用される名前と一致します。

アナライザー プロジェクトを作成する

メイン メニューから [ファイル]>[新規]>[プロジェクト] を選択します。 [新しいプロジェクト] ダイアログで、左のナビゲーションバーの[C# プロジェクト]の下にある[機能拡張]を選択し、右側のウィンドウで[Analyzer with Code Fix]プロジェクトテンプレートを選択します。 名前を入力し、ダイアログを確認します。

テンプレートによって DiagnosticAnalyzer.cs ファイルが開きます。 そのエディター バッファー タブを選択します。このファイルには、DiagnosticAnalyzer (Roslyn API 型) から派生したアナライザー クラス (プロジェクトに指定した名前から形成されます) があります。 新しいクラスには、アナライザーが C# 言語に関連していることを宣言する DiagnosticAnalyzerAttribute があり、コンパイラがアナライザーを検出して読み込みます。

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzer : DiagnosticAnalyzer
{}

C# コードを対象とする Visual Basic を使用してアナライザーを実装できます。その逆も同様です。 DiagnosticAnalyzerAttribute では、アナライザーが 1 つの言語または両方を対象とするかどうかを選択する方が重要です。 言語の詳細なモデリングを必要とするより高度なアナライザーは、1 つの言語のみを対象とすることができます。 たとえば、アナライザーで型名またはパブリック メンバー名のみをチェックする場合、Visual Basic と C# 全体で Roslyn が提供する共通言語モデルを使用できる場合があります。 たとえば、FxCop は、クラスが ISerializableを実装することを警告しますが、クラスには言語に依存しない SerializableAttribute 属性はなく、Visual Basic と C# の両方のコードで機能します。

アナライザーを初期化する

Initialize メソッドを表示するには、DiagnosticAnalyzer クラスで少し下にスクロールします。 コンパイラは、アナライザーをアクティブ化するときにこのメソッドを呼び出します。 このメソッドは、アナライザーがコンテキスト情報を取得し、分析するコードの種類のイベントのコールバックを登録できるようにする AnalysisContext オブジェクトを受け取ります。

public override void Initialize(AnalysisContext context) {}

このメソッドで新しい行を開き、「context.」と入力して、IntelliSense の入力候補一覧を表示します。 完了リストには、さまざまな種類のイベントを処理する Register... メソッドが多数あります。 たとえば、1 つ目の RegisterCodeBlockAction は、ブロックのコードにコールバックします。これは通常、中かっこの間のコードです。 ブロックに登録すると、フィールドの初期化子、属性に指定された値、または省略可能なパラメーターの値のコードも呼び出されます。

別の例として、RegisterCompilationStartActionコンパイルの開始時にコードを呼び出します。これは、さまざまな場所で状態を収集する必要がある場合に便利です。 たとえば、使用されているすべてのシンボルを収集するデータ構造を作成できます。また、アナライザーが何らかの構文またはシンボルに対して呼び出されるたびに、データ構造内の各場所に関する情報を保存できます。 コンパイルの終了が原因で呼び出された場合は、保存したすべての場所を分析して、たとえば、各 using ステートメントからコードで使用されるシンボルを報告できます。

構文ビジュアライザーを使用して、コンパイラが ObjectCreationExpression を処理するときに呼び出す必要があることを学習しました。 コールバックを設定するには、次のコードを使用します。

context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
                                 SyntaxKind.ObjectCreationExpression);

構文ノードに登録し、オブジェクト作成構文ノードのみをフィルター処理します。 慣例により、アナライザー作成者はアクションを登録するときにラムダを使用します。これは、アナライザーをステートレスに保つのに役立ちます。 AnalyzeObjectCreation メソッドを作成するには、Visual Studio 機能 [Generate From Usage]\(使用状況から生成\) を使用できます。 これにより、コンテキスト パラメーターの正しい型も生成されます。

アナライザーのユーザーのプロパティを設定する

アナライザーが Visual Studio UI に適切に表示されるように、次のコード行を探して変更し、アナライザーを識別します。

internal const string Category = "Naming";

"Naming""API Guidance"に変更します。

次に、ソリューション エクスプローラーを使用して、プロジェクト内の Resources.resx ファイルを見つけて開きます。 アナライザー、タイトルなどの説明を入力できます。現時点では、これらのすべての値を "Don't use ImmutableArray<T> constructor" に変更できます。 文字列の書式設定引数を文字列 ({0}、{1}など) に配置できます。後で Diagnostic.Create()を呼び出すときに、渡される引数の params 配列を指定できます。

オブジェクト作成式を分析する

AnalyzeObjectCreation メソッドは、コード アナライザー フレームワークによって提供される異なる種類のコンテキストを受け取ります。 Initialize メソッドの AnalysisContext を使用すると、アクション コールバックを登録してアナライザーを設定できます。 たとえば、SyntaxNodeAnalysisContext には CancellationToken があり、これを渡すことができます。 ユーザーがエディターで入力を開始すると、Roslyn は実行中のアナライザーを取り消して作業を保存し、パフォーマンスを向上させます。 別の例として、このコンテキストには、オブジェクト作成構文ノードを返す Node プロパティがあります。

ノードを取得します。これは、構文ノード アクションをフィルター処理した型であると想定できます。

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

初めてアナライザーを使用して Visual Studio を起動する

アナライザーをビルドして実行して Visual Studio を起動します (F5キーを押します)。 ソリューション エクスプローラー のスタートアップ プロジェクトは VSIX プロジェクトであるため、コードを実行すると、コードと VSIX がビルドされ、その VSIX がインストールされた Visual Studio が起動されます。 この方法で Visual Studio を起動すると、個別のレジストリ ハイブで起動されるため、アナライザーのビルド中に Visual Studio の主な使用がテスト インスタンスの影響を受けなくなります。 この方法で初めて起動すると、Visual Studio をインストールした後に Visual Studio を初めて起動したときと同様に、いくつかの初期化が実行されます。

コンソール プロジェクトを作成し、コンソール アプリケーション Main メソッドに配列コードを入力します。

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);

ImmutableArray を含むコード行には波線が表示されます。これは、不変の NuGet パッケージを取得し、コードに using ステートメントを追加する必要があるためです。 ソリューション エクスプローラーの でプロジェクト ノードの右ポインター ボタンを押し、[NuGet パッケージの管理]選択します。 NuGet マネージャーで、検索ボックスに「Immutable」と入力し、左側のウィンドウで System.Collections.Immutable 項目を選択し ([Microsoft.Bcl.Immutableを選択しない])、右側のウィンドウの [インストール] ボタンを押します。 パッケージをインストールすると、プロジェクト参照への参照が追加されます。

ImmutableArray の下に赤い波線が引き続き表示されるので、その識別子にキャレットを置き、Ctrl + . (ピリオド) キーを押して修正候補メニューを表示し、適切な using ステートメントを追加することを選択します。

すべて保存して閉じ、 2つ目の Visual Studio インスタンスを閉じて、クリーンな状態にして続行します。

"編集と継続機能を使用してアナライザーの作業を完了する"

Visual Studio の最初のインスタンスで、AnalyzeObjectCreation メソッドの先頭にブレークポイントを設定するには、まず1行目にカーソルを移動し、F9 キーを押します。

F5でアナライザーを再度起動し、Visual Studio の 2 番目のインスタンスで、前回作成したコンソール アプリケーションをもう一度開きます。

Roslyn コンパイラがオブジェクト作成式を検出してアナライザーを呼び出したため、ブレークポイントで最初の Visual Studio のインスタンスに戻ります。

オブジェクト作成ノードを取得します。 F10 キーを押して objectCreation 変数を設定する行をステップオーバーし、[イミディエイト ウィンドウ] で式 "objectCreation.ToString()" を評価します。 変数が指す構文ノードは、探しているコード "new ImmutableArray<int>()"であることがわかります。

ImmutableArray<T> Type オブジェクトを取得します。 作成される型が ImmutableArray であるかどうかを確認する必要があります。 まず、この型を表すオブジェクトを取得します。 セマンティック モデルを使用して型をチェックして、正確に適切な型があることを確認し、ToString()の文字列を比較しません。 関数の末尾に次のコード行を入力します。

var immutableArrayOfTType =
    context.SemanticModel
           .Compilation
           .GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");

メタデータでは、バックティック (') とジェネリック パラメーターの数を使用してジェネリック型を指定します。 そのため、メタデータ名に "ImmutableArray<T>" が表示されないのです。

セマンティック モデルには、シンボル、データ フロー、可変有効期間などに関する質問をできる便利なものが多数用意されています。Roslyn は、さまざまなエンジニアリング上の理由 (パフォーマンス、誤ったコードのモデリングなど) のために、構文ノードをセマンティック モデルから分離します。 正確な比較のために、コンパイル モデルで参照に含まれる情報を検索する必要があります。

エディター ウィンドウの左側にある黄色の実行ポインターをドラッグできます。 変数を設定する行までドラッグし、F10使用して新しいコード行の上にステップ オーバーします。 immutableArrayOfType変数の上にマウス ポインターを置くと、セマンティック モデルで正確な型が見つかったことがわかります。

オブジェクト作成式の型を取得します。 この記事では "Type" をいくつかの方法で使用しますが、これは "新しい Foo" 式がある場合は、Foo のモデルを取得する必要があることを意味します。 オブジェクト作成式の型を取得して、ImmutableArray<T> 型であるかどうかを確認する必要があります。 セマンティック モデルをもう一度使用して、オブジェクト作成式の型シンボル (ImmutableArray) のシンボル情報を取得します。 関数の末尾に次のコード行を入力します。

var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;

アナライザーはエディター バッファー内の不完全なコードまたは正しくないコードを処理する必要があるため (たとえば、using ステートメントが不足しています)、symbolInfonullされているかどうかを確認する必要があります。 分析を完了するには、シンボル情報オブジェクトから名前付き型 (INamedTypeSymbol) を取得する必要があります。

型を比較します。 探しているオープン ジェネリック型 T があり、コード内の型は具体的なジェネリック型であるため、型の構築元 (オープン ジェネリック型) のシンボル情報に対してクエリを実行し、その結果を immutableArrayOfTTypeと比較します。 メソッドの末尾に次のように入力します。

if (symbolInfo != null &&
    symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}

診断を報告します。 診断の報告は非常に簡単です。 プロジェクト テンプレートで作成したルールを使用します。これは Initialize メソッドの前に定義されています。 コード内のこの状況はエラーであるため、rule を初期化した行を変更して、DiagnosticSeverity.Warning (緑の波線) を DiagnosticSeverity.Error (赤い波線) に置き換えることができます。 ルールの残りの部分は、チュートリアルの冒頭付近で編集したリソースから初期化されます。 また、波線の場所を報告する必要もあります。これは、オブジェクト作成式の型指定の場所です。 if ブロックに次のコードを入力します。

context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));

関数は次のようになります (形式が異なる場合があります)。

private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
    var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
    var immutableArrayOfTType =
        context.SemanticModel
               .Compilation
               .GetTypeByMetadataName(
                   "System.Collections.Immutable.ImmutableArray`1");
    var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as
        INamedTypeSymbol;
    if (symbolInfo != null &&
        symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
    {
        context.ReportDiagnostic(
            Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
    }
}

アナライザーの動作を確認できるようにブレークポイントを削除します (Visual Studio の最初のインスタンスへの戻りを停止します)。 実行ポインターをメソッドの先頭にドラッグし、F5 押して実行を続行します。 Visual Studio の 2 番目のインスタンスに戻ると、コンパイラはコードの再検証を開始し、アナライザーを呼び出します。 ImmutableType<int> の下に波線が表示されます。

コードの問題に対する "コード修正" の追加

開始する前に、Visual Studio の 2 番目のインスタンスを閉じ、Visual Studio の最初のインスタンス (アナライザーを開発している場所) でデバッグを停止します。

新しいクラスを追加します。 ソリューション エクスプローラーの でプロジェクト ノードのショートカット メニュー (右ポインター ボタン) を使用し、新しい項目を追加することを選択します。 BuildCodeFixProviderというクラスを追加します。 このクラスは から派生する必要があり、Ctrl使用する必要があります。 (ピリオド) を使用して、正しい ステートメントを追加するコード修正プログラムを呼び出します。 このクラスには ExportCodeFixProvider 属性で注釈を付ける必要もあります。また、LanguageNames 列挙型を解決するには、using ステートメントを追加する必要があります。 クラス ファイルには、次のコードが含まれている必要があります。

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;

namespace ImmutableArrayAnalyzer
{
    [ExportCodeFixProvider(LanguageNames.CSharp)]
    class BuildCodeFixProvider : CodeFixProvider
    {}

派生メンバーをスタブします。 次に、エディターのキャレットを識別子 CodeFixProvider に配置し、Ctrl キーを押しながら を押して、+ (ピリオド)を入力し、この抽象基底クラスの実装をスタブ化します。 これにより、プロパティとメソッドが生成されます。

プロパティを実装します。 FixableDiagnosticIds プロパティの get 本体に次のコードを入力します。

return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);

Roslyn は、これらの識別子 (単なる文字列) を照合することで、診断と修正をまとめます。 プロジェクト テンプレートによって診断 ID が生成され、自由に変更できます。 プロパティ内のコードは、アナライザー クラスから ID を返すだけです。

RegisterCodeFixAsync メソッドはコンテキストを受け取ります。 コード修正が複数の診断に適用される場合や、コード行に複数の問題が発生する可能性があるため、コンテキストは重要です。 メソッドの本体に「context.」と入力すると、IntelliSense の入力候補一覧に、有用なメンバーがいくつか表示されます。 CancellationToken メンバーがあり、何かが修正をキャンセルしようとしているかどうかを確認できます。 多くの有用なメンバーを持つ Document メンバーがあり、プロジェクトおよびソリューション モデル オブジェクトにアクセスできます。 診断を報告する際に指定したコードの位置に対応する、開始および終了を示す Span メンバーが存在します。

メソッドを非同期にします。 最初に行う必要があるのは、生成されたメソッド宣言を async メソッドに修正することです。 抽象クラスの実装をスタブアウトするためのコード修正には、メソッドが Taskを返す場合でも、async キーワードは含まれません。

構文ツリーのルートを取得します。 コードを変更するには、コード修正によって行う変更を含む新しい構文ツリーを生成する必要があります。 GetSyntaxRootAsyncを呼び出すには、コンテキストからの Document が必要です。 これは非同期メソッドです。これは、構文ツリーを取得するための不明な作業があるためです。これには、ディスクからのファイルの取得、解析、Roslyn コード モデルの構築などが含まれます。 この間、async を使用することで Visual Studio の UI は応答性が向上します。 メソッド内のコード行を次のように置き換えます。

var root = await context.Document
                        .GetSyntaxRootAsync(context.CancellationToken);

問題のあるノードを見つけます。 コンテキストのスパンを渡しますが、見つけたノードが変更する必要があるコードではない可能性があります。 報告された診断では、(波線が属している) 型識別子のスパンのみが指定されていましたが、オブジェクト作成式全体を置き換える必要があります。これには、先頭の new キーワードと末尾のかっこが含まれます。 次のコードをメソッドに追加します (Ctrl+. を使用して、ObjectCreationExpressionSyntaxusing ステートメントを追加します)。

var objectCreation = root.FindNode(context.Span)
                         .FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();

電球 UI のコード修正プログラムを登録します。 コード修正プログラムを登録すると、Roslyn は Visual Studio 電球 UI に自動的にプラグインします。 エンド ユーザーは、アナライザーが不適切な ImmutableArray<T> コンストラクターの使用に波線を表示したときに、Ctrl+. (ピリオド) キーを使用できることが分かります。 コード修正プロバイダーは問題が発生したときにのみ実行されるため、探していたオブジェクト作成式があると仮定できます。 コンテキスト パラメーターから、次のコードをメソッドの末尾に追加することで、新しいコード修正 RegisterCodeFixAsync 登録できます。

context.RegisterCodeFix(
            CodeAction.Create("Use ImmutableArray<T>.Empty",
                              c => ChangeToImmutableArrayEmpty(objectCreation,
                                                               context.Document,
                                                               c)),
            context.Diagnostics[0]);

エディターのキャレットを識別子 CodeAction に置き、Ctrl + . (ピリオド) キーを使って、この型の適切な using ステートメントを追加する必要があります。

次に、エディターのキャレットを ChangeToImmutableArrayEmpty 識別子に配置し、Ctrl + をもう一度使用して、このメソッドスタブを生成します。

この最後に追加したコード スニペットは、検出された問題の種類の CodeAction と診断 ID を渡すことによって、コード修正を登録します。 この例では、このコードで修正プログラムを提供する診断 ID は 1 つだけであるため、診断 ID 配列の最初の要素を渡すだけで済みます。 CodeActionを作成するときに、電球 UI がコード修正の説明として使用するテキストを渡します。 CancellationToken を受け取り、新しい Document を返す関数も渡します。 新しいドキュメントには、ImmutableArray.Emptyを呼び出す修正プログラムが適用されたコードを含む新しい構文ツリーがあります。 このコード スニペットでは、objectCreation ノードとコンテキストの Document を閉じることができるように、ラムダを使用します。

新しい構文ツリーを構築します。 先ほどスタブを生成した ChangeToImmutableArrayEmpty メソッドに、コード行 ImmutableArray<int>.Empty;を入力します。 構文ビジュアライザー ツール ウィンドウをもう一度表示すると、この構文が SimpleMemberAccessExpression ノードであることがわかります。 これは、このメソッドで新しい Document を構築して返すために必要なものです。

ChangeToImmutableArrayEmpty に対する最初の変更は、Task<Document> する前に async を追加することです。これは、メソッドを非同期にする必要があるとコード ジェネレーターが想定できないためです。

メソッドが次のように表示されるように、本文に次のコードを入力します。

private async Task<Document> ChangeToImmutableArrayEmpty(
    ObjectCreationExpressionSyntax objectCreation, Document document,
    CancellationToken c)
{
    var generator = SyntaxGenerator.GetGenerator(document);
    var memberAccess =
        generator.MemberAccessExpression(objectCreation.Type, "Empty");
    var oldRoot = await document.GetSyntaxRootAsync(c);
    var newRoot = oldRoot.ReplaceNode(objectCreation, memberAccess);
    return document.WithSyntaxRoot(newRoot);
}

エディターのキャレットを 識別子に配置し、Ctrl使用する必要があります。 (ピリオド) を使用して、この型の適切な ステートメントを追加します。

このコードでは SyntaxGeneratorを使用します。これは、新しいコードを構築するための便利な型です。 コードの問題があるドキュメントのジェネレーターを取得した後、ChangeToImmutableArrayEmptyMemberAccessExpressionを呼び出し、アクセスするメンバーを持つ型を渡し、メンバーの名前を文字列として渡します。

次に、メソッドはドキュメントのルートをフェッチします。これは一般的なケースでは任意の作業を伴う可能性があるため、コードはこの呼び出しを待機し、キャンセル トークンを渡します。 Roslyn コード モデルは、.NET 文字列の操作と同様に不変です。文字列を更新すると、新しい文字列オブジェクトが返されます。 ReplaceNodeを呼び出すと、新しいルート ノードが返されます。 構文ツリーのほとんどは共有されますが (不変であるため)、objectCreation ノードは memberAccess ノードと、構文ツリー ルートまでのすべての親ノードに置き換えられます。

コード修正を試す

F5 押して、Visual Studio の 2 番目のインスタンスでアナライザーを実行できるようになりました。 前に使用したコンソール プロジェクトを開きます。 これで、新しいオブジェクト作成式が ImmutableArray<int>の場所に電球が表示されます。 Ctrlキーを押してから+(ピリオド)を入力すると、コード修正が表示され、電球UIで自動生成されたコードの差分プレビューが表示されます。 Roslyn はこれを自動的に作成します。

Pro のヒント: Visual Studio の 2 番目のインスタンスを起動しても、コード修正で電球が表示されない場合は、Visual Studio コンポーネント キャッシュのクリアが必要になる場合があります。 キャッシュをクリアすると、Visual Studio でコンポーネントが再検査されるため、Visual Studio は最新のコンポーネントを取得する必要があります。 最初に、Visual Studio の 2 番目のインスタンスをシャットダウンします。 次に、Windows エクスプローラーで、%LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\に移動します。 (Visual Studio を使用すると、"16.0" がバージョンからバージョンに変更されます)。ComponentModelCacheサブディレクトリを削除します。

ビデオを話してコード プロジェクトを終了する

完成したコード はすべて、で確認できます。 DoNotUseImmutableArrayCollectionInitializer と DoNotUseImmutableArrayCtor サブ フォルダーには、それぞれ問題を見つけるための C# ファイルと、Visual Studio 電球 UI に表示されるコード修正を実装する C# ファイルがあります。 完成したコードでは、ImmutableArray<T> 型のオブジェクトを何度も取得しないように、多少抽象化を加えています。 入れ子になった登録済みアクションを使用して、サブ アクション (オブジェクトの作成の分析とコレクションの初期化の分析) が実行されるたびに使用できるコンテキストに型オブジェクトを保存します。