csharp-refactoring について
動作を維持する C# リファクタリング向けのスキルです。依頼の分類、バインディングに基づく編集、変更規模に応じた検証を扱い、構造変更とアップグレード、機能追加、動作・契約変更を区別します。
主な機能
- 01編集前のリファクタリング依頼の分類
- 02バインディングに基づく名前変更と移動
- 03公開境界での互換性維持
- 04変更範囲に応じたビルドとテスト
- 05対象外作業の引き継ぎルール
活用シーン
- 01動作を変えずに C# シンボルを改名
- 02重複実装の統合
- 03呼び出し側の入力を保ったロジック抽出
- 04リファクタリングと動作・API 変更の切り分け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill csharp-refactoringインストール後、Claude Code で/csharp-refactoringを入力して読み込まれたか確認します。
同梱ファイルreferences/operation-catalog.md
詳細
C# のリファクタリング(動作を維持)
リファクタリングで変更するのは構造であり、観測可能な動作ではありません。バインディングを認識するツールを使って編集し、その後、ビルドと関連するテストで動作が維持されていることを確認してください。作業量は変更に見合ったものにしてください。ローカル変数名を1行で変更するだけなら、公開 API に関わるマルチターゲット変更のような手順は必要ありません。
必須ゲート:検証や編集の前に分類する
リポジトリのコンテキストは、依頼された各操作を分類できるだけの範囲で確認してください。復元、ビルド、編集を行う前に分類します。分類の優先順位は次のとおりです。
- 依頼に、動作を維持する操作が少なくとも1つ明示されている場合は、その構造的な作業を完了し、動作または契約を変更する操作のみを保留してください。停止時の応答を避けるために、構造的な作業を捏造したり推測したりしてはいけません。
- 構造的な部分と動作を変更する部分を分離できない場合は、依頼全体に対する停止時の応答を使用し、分離できない理由を述べてください。
- それ以外の場合は、依頼されたすべての操作が動作を維持するリファクタリングの範囲外である場合に限り、依頼全体に対する停止時の応答を使用してください。
依頼全体が動作を維持するリファクタリングの範囲外である場合:
- 次のように述べます:
Not a behavior-preserving refactor: <specific reason>. - 次のように述べます:
No files changed. - 次のように述べます:
Next workflow: <workflow>.その後、停止してください。手動での実装手順、代替案、そのワークフローを使わずに進める提案、追加の質問をしてはいけません。ワークフローを利用できない場合も同様です。
| 「リファクタリング」として依頼された内容 | 分類と対応 |
|---|---|
| フレームワークまたは NuGet のバージョン変更 | アップグレードです。ここではアップグレードの編集や検証を行わず、dotnet-upgrade に引き継いでください。 |
| 新しい機能、フラグ、エンドポイント、ティア、または動作 | 機能追加です。ここでは実装せず、リポジトリの機能追加ワークフローに引き継いでください。 |
| しきい値、レート、出力、またはバグの結果の変更 | 動作変更です。保留し、リポジトリのバグ修正または動作変更ワークフローに引き継いでください。明確に分離できる構造的な操作があれば、引き続き完了してください。 |
| 公開済みまたはパブリックな nullable 注釈の厳格化または緩和 | ソース契約の変更です。宣言と API レコードは変更せず、リポジトリの API 契約ワークフローに引き継いでください。 |
混在した依頼の場合、分類の後に停止してはいけません。分離できる構造的な操作を実行し、動作または契約の変更を明示的に保留してください。構造的な作業を完了した後に、No files changed. と述べてはいけません。許可されていない動作変更を維持したように見せるために、テストを変更してはいけません。
現在のワークスペース内でのみ作業する
現在のエージェントワークスペースを作業範囲としてください。Git チェックアウトの場合は、リポジトリルート(git rev-parse --show-toplevel)を解決し、その範囲内で作業してください。Git メタデータがない場合は、現在の作業ディレクトリとそのサブディレクトリを範囲とし、その中にある依頼で指定されたソリューションまたはプロジェクトを使用してください。リファクタリングに Git は必須ではありません。プロンプトで指定された相対パスは、その範囲内で解決してください。
検索と編集は、そのワークスペース内でのみ行ってください。ファイルが別の場所にも存在するからといって、ファイルシステム全体を検索したり、似た名前の別のクローン、別のワークツリー、ビルド出力、無関係な一時ディレクトリを選んだりしてはいけません。指定されたパスが存在しない場合は、別のワークスペースを推測せず、停止して不一致を報告してください。パス形式や未対応のツールルートなど、機械的な理由でツールがワークスペース内のパスを拒否した場合は、ワークスペース内の別の方法で再試行してください。拒否が権限またはポリシーによるものである場合は、回避せずに報告してください。作業範囲外を検索してはいけません。
テキストではなくバインディングに基づいて名前を変更/移動する
「名前変更」によってコードが気付かないうちに破損する最大の原因は、実際のバインディングではなく、テキスト上の一致箇所(コメント、文字列、無関係なオーバーロード)を編集することです。まずすべてのバインディング参照を見つけてから、意味に基づいて編集します。利用可能な最も強力なツールを使ってください。まず IDE/Roslyn ワークスペースのリファクタリング、次に dotnet プラグインが宣言する C# LSP(findReferences、goToDefinition、incomingCalls、rename コードアクション)、次にアナライザーのコード修正/Roslynator、最後にコンパイラーで検証する編集(実際のバインディングを編集し、再ビルドして、見落としをコンパイラーに検出させます)です。単純な検索置換を使うのは、対象範囲が確実に小さく、すべての該当箇所を検証できる場合に限ります。すべての partial 宣言を含め、生成された(*.g.cs)出力ではなく、ジェネレーターの入力を編集してください。
操作と Roslyn プロバイダーの対応関係、および参考となる PR については、 references/operation-catalog.md を参照してください。
既存の信頼できる情報源に集約する
重複を解消する際は、コードまたは依頼で示されている所有関係の方向を維持してください。B が、すでに A が所有する実装を重複している場合は、A を正として維持し、B から A に委譲するようにします。どちらの方向でもコンパイルできるというだけの理由で、依存関係を逆転させてはいけません。重複する API が公開済みの場合は、公開互換性ラッパーを維持し、安全に移行できるリポジトリ内の呼び出し元だけを移行してください。
編集内容に影響する判断
依頼された操作を機械的に適用するのではなく、最初に該当する行を使用してください。
| 状況 | 実行すること | 決して行わないこと |
|---|---|---|
| 内部向けで未公開のパススルーラッパーをインライン化する | すべてのバインディング参照を対象に移行し、ラッパーを削除してから、見落としを検出するためにコンパイルします。 | 互換性の境界が存在しないのに、「互換性のため」として不要な間接参照を残すこと。 |
| 公開済み/パブリックなラッパーをインライン化または削除する | 通常のリポジトリ内の呼び出し元を移行します。ただし、依頼で破壊的変更が明示的に許可されていない限り、[Obsolete] の転送エントリーポイントを残します。 | 現在のソース内の呼び出し元をすべて移行したという理由だけで、公開済み API を削除すること。 |
| 文字列、リフレクション、DI、または構成経由で呼び出されるメンバーの名前を変更する | バインディングに基づく呼び出し元を変更し、転送シムまたはメタデータを使って、外部から参照される名前を維持し、旧名を使う経路をテストします。 | 新しいソース名と一致させるためだけに、外部名または構成名を書き換えること。 |
| 呼び出し元ごとに異なる値を渡す重複ロジックを抽出する | アルゴリズムを抽出し、各呼び出し元が既存の入力をそのまま渡すようにします。 | 異なる入力、評価順序、丸め、または副作用を、単一の呼び出し元の実装に合わせてまとめること。 |
#if または複数の TFM でコンパイルされるコードの名前を変更する | すべてのソース分岐を更新し、各ターゲットフレームワークを個別に検証します。 | デフォルトのターゲットでビルドが成功したことを、ビルドしていない分岐の検証済みの証拠として扱うこと。 |
| ほぼ同一の型を統合する | 異なる値だけをパラメーター化し、すべての生成箇所を移行して、以前の各値を正確に維持します。以前の型が内部向け/未公開で、依頼で 1 つの型への統合が指定されている場合は、それらの宣言を削除します。 | 不要なエイリアス、静的ホルダー、ファクトリー、またはラッパー型を残して、依頼された統合を不完全にすること。また、新しい階層や動作を導入すること。 |
C# の呼び出し箇所を超えてコントラクトを維持する
コンパイルで確認できるのはバインディングの互換性であり、すべての外部コントラクトではありません。型やメンバーの名前を変更したり移動したりする前に、その名前やメタデータがシリアル化、リフレクション、依存性注入、構成バインディング、ソースジェネレーター、P/Invoke、または dynamic から参照されていないか確認してください。
| 境界 | 必要な判断 |
|---|---|
| シリアル化/構成の名前 | リポジトリに既存の仕組み(例:JsonPropertyName)を使って外部名を維持しながら、C# の呼び出し元を移行してください。対象を絞ったラウンドトリップテストまたはペイロードテストを実行してください。 |
| パブリックな null 許容注釈 | 必須の分類ゲートを適用します。変更せず、ソースコントラクトの変更として引き継いでください。 |
| 網羅されていないリフレクションまたは実行時の検索 | コンパイルが成功する名前変更なら安全だと推測してはいけません。確認された名前を維持するか、検証できていない実行時境界を報告して作業を中断してください。 |
変更に応じて検証する
編集後は、決まった一律の手順ではなく、影響範囲に応じて動作が維持されていることを確認してください。
- ローカル/プライベート(メソッド内または
privateメンバー、単一ファイル、単一のターゲットフレームワーク、パブリックなサーフェスなし、partial/生成コード/#ifなし):ツリーにすでに問題が疑われる場合を除き、個別のベースライン確認は省略します。編集後、最も対象を絞ったビルドと関連テストを一度実行してください。見落とした参照はコンパイラーで検出します。 - 境界をまたぐ変更(パブリックなシンボル/配布対象のシンボル、複数のターゲットフレームワーク、
#if/プラットフォーム分岐、またはpartial/生成コード):まずベースラインを確認し、編集後は各ターゲットフレームワークについて明示的なビルドと関連テストを実行してください(テストコマンド内で暗黙的に行われるビルドは、独立したビルドの証拠にはなりません。既定のビルドが成功しても、別の TFM での破損が隠れている可能性があります)。また、以下の危険要因チェックも実行してください。
リポジトリ独自のビルド/テスト手順が文書化されている場合(README/CONTRIBUTING、build.*、eng/、global.json、.github/workflows)は、それを使用してください。一般的なコマンドより、リポジトリの手順を優先します。
典型的なワークフロー(1 操作)
- 名前の付いたリファクタリング操作を 1 つ選び、その操作だけに絞って作業します。
- 実際のバインディング参照(
findReferences/goToDefinition/rename)を特定し、すべてのpartial宣言を含めます。 - 境界をまたぐ変更、または正常であることが確認されていないツリーの場合に限り、先にベースラインを確認します。
- 可能な限り、セマンティクスを把握できるツールを使って変更を適用します。可能であれば、機械的な検索/置換は避けてください。
- 再ビルドし、関連テストを実行します。ゲートが失敗した場合は、その失敗を報告し、自分の編集だけを修正または再評価してください。無関係な作業ツリーの変更を破棄してはいけません。
それ以外の場合:
dotnet build # 0 errors
dotnet test # stays green; same pass count as before
1 つのステップにつき操作は 1 つだけにしてください。同じステップでリファクタリングと動作変更を混在させてはいけません。失敗したら中断して報告し、無関係な作業ツリーの変更を破棄せず、自分の編集だけを修正してください。
最終回答の要件
引き継ぎ内容は簡潔にし、証拠に基づいて記述してください。
- リファクタリング: 構造上の操作と、変更したシンボル/ファイルを明記します。
- 維持したもの: 動作または互換性の境界と、それを維持した仕組みを明記します。
- 検証: 実行した正確なコマンドと確認できた結果を報告します。復元、ビルド、ターゲットフレームワーク、またはテストの実行に失敗した場合は、成功したと主張してはいけません。
- 延期: 複数の要望がある場合、意図的に実施しなかった動作/コントラクトの変更と、それに適した次のワークフローを明記します。延期がない場合は、この項目を省略します。
境界に関わる危険(境界に触れる場合のみ)
変更が公開シンボル、複数のターゲットを対象とするプロジェクト、または
partial/生成コードに触れる場合に限り、テスト失敗として現れない破壊が発生することがあります。シンボルを管理する範囲をリポジトリ内で検索してください(決めつけないでください)。公開 API のゲート(PublicApiAnalyzers の場合は PublicAPI.Shipped/Unshipped.txt、および/または ApiCompat/<EnablePackageValidation>。これらは相互に置き換えられません)、<TargetFrameworks>/#if 分岐、InternalsVisibleTo を確認してください。公開型を別のアセンブリに移動する場合は、元のアセンブリに [TypeForwardedTo] が必要です。同じアセンブリ内での移動には不要です。公開シンボルの名前変更には、フォワーダーではなく [Obsolete] の互換用シムが必要です。変更がローカル/プライベートなものだと確実に証明できる場合は、これらの確認を省略してください。
対象範囲内のリファクタリングを中止する場合
- ベースラインがすでに失敗している場合(動作を維持したと証明できません)。
- 要求された構造変更によって公開済み API が変わり、互換用シムやフォワーダーで維持できない場合。境界について報告してください。構造変更を何も完了していない場合は、ゲートの引き継ぎ形式を使用してください。分離可能な作業の後であれば、互換性のない操作を
Deferred:の行に記載してください。破壊的変更を行うよう求めてはいけません。 - 等価性が、テストでカバーされていないランタイム動作(リフレクション、DI、シリアル化、
dynamic、P/Invoke)に依存する場合。動作が維持されたと断言せず、未検証の境界について報告してください。