mcpField
D
dotnet-csharp-code-smells

dotnet-csharp-code-smells

github.com/wshaddixテスト・品質86169 回インストール35 週間前に更新
リポジトリ

dotnet-csharp-code-smells について

C# の開発者やレビュアー向けに、コードスメルとアンチパターンをまとめたガイドです。リソース管理、LINQ、イベント、設計、例外処理などの問題と修正案、関連する CA ルールを示します。

主な機能

  1. 01リソース破棄の問題を特定
  2. 02LINQ のアンチパターンを確認
  3. 03イベント処理のリークを検出
  4. 04例外処理の不備を確認
  5. 05関連する CA ルールを参照

活用シーン

  1. 01C# コードの一般的なスメルをレビュー
  2. 02リソースリークやイベント購読の問題を調査
  3. 03CA ルールに沿ってコードを改善

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-csharp-code-smells

インストール後、Claude Code で/dotnet-csharp-code-smellsを入力して読み込まれたか確認します。

同梱ファイルdetails.md

詳細

dotnet-csharp-code-smells

C# コードのコードスメルとアンチパターンを積極的に検出します。このスキルは、計画、実装、レビューのすべてのワークフローモードで起動します。各項目では、コードスメルを特定し、それが有害な理由を説明し、適切な修正方法を示し、関連する CA ルールまたは相互参照を記載します。

相互参照:非同期処理の注意点については[スキル:dotnet-csharp-async-patterns]、命名とスタイルについては[スキル:dotnet-csharp-coding-standards]、DI のライフタイムの誤用については[スキル:dotnet-csharp-dependency-injection]、NRT の注釈ミスについては[スキル:dotnet-csharp-nullable-reference-types]を参照してください。

対象外: LLM 固有の生成ミス(誤った NuGet パッケージ、不適切なプロジェクト構成、MSBuild エラー)は[スキル:dotnet-agent-gotchas]で扱います。このスキルでは、人間か AI かを問わず、あらゆる開発者が避けるべき一般的な .NET のコードスメルを扱います。


1. リソース管理(IDisposable の誤用)

コードスメル有害な理由修正方法ルール
IDisposable なローカル変数に using がないアンマネージドハンドル(ソケット、ファイル、DB 接続)がリークしますusing 宣言または using ブロックで囲みますCA2000
IDisposable フィールドを破棄しないクラスが破棄可能なリソースを保持しているのに、破棄しませんIDisposable を実装し、Dispose() 内でフィールドを破棄しますCA2213
誤った Dispose パターン(ファイナライザーガードがない)二重破棄、または GC のファイナライズ時にクリーンアップが漏れます標準的な Dispose(bool) パターンに従い、GC.SuppressFinalize(this) を呼び出しますCA1816
あるメソッドで作成した破棄可能オブジェクトをフィールドに格納する所有権が不明確になり、破棄を忘れやすくなります所有権を明記し、包含するクラスを IDisposable にしますCA2000
所有していないリソースに using を使う共有リソース(例:注入された HttpClient)を早期に破棄してしまいます自分で作成したリソースのみを破棄し、注入されたサービスは DI に管理させます--

各パターンのコード例については、details.md を参照してください。


2. 警告抑制のハック

コードスメル有害な理由修正方法ルール
CS0067 を抑制するためにイベントを null で呼び出す誤解を招く実行時の動作を生み、実際のバグを隠します#pragma warning disable CS0067 または明示的なイベントアクセサー { add {} remove {} } を使いますCS0067
CS0219 を抑制するためのダミー変数への代入読み手を混乱させるデッドコードです_ = expression; による破棄、または #pragma warning disable を使いますCS0219
復元を伴わない一括 #pragma warning disableファイルの残りの部分にあるすべての警告を抑制します必ず #pragma warning restore と組み合わせ、特定のコードのみを抑制します--
根拠の記載がない [SuppressMessage]将来の保守担当者が、抑制が依然として妥当かどうかを判断できません必ず Justification = "reason" を含めますCA1303

CS0067 の背景となる例(悪いパターンから適切な修正への変更)については、details.md を参照してください。


3. LINQ のアンチパターン

コードスメル有害な理由修正方法ルール
チェーンの途中で早まって .ToList() を呼び出すすべての要素を強制的に実体化し、メモリを無駄に使いますチェーンを遅延評価のまま保ち、最後にのみ実体化しますCA1851
IEnumerable<T> を複数回列挙する列挙のたびにクエリまたは DB 呼び出しが再実行されます.ToList() で一度実体化してから再利用しますCA1851
EF Core でクライアント側評価を行うテーブル全体をメモリに読み込み、気づきにくい性能上の問題を引き起こします変換可能な LINQ にクエリを書き換えるか、明示的な意図を持って AsAsyncEnumerable() を使います--
.Any() の代わりに .Count() > 0 を使う短絡評価せず、コレクション全体を列挙します存在確認には .Any() を使いますCA1827
.Join() または .GroupJoin() の代わりにネストした foreach を使うO(n+m) で処理できるところを O(n*m) で処理しますLINQ の結合操作または Dictionary による検索を使います--
.First(predicate) の代わりに .Where().First() を使う不要な中間イテレーターを作成します述語を .First() または .FirstOrDefault() に直接渡しますCA1826

4. イベント処理の漏れ

問題点有害な理由修正方法ルール
イベントの購読を解除しないメモリリーク:発行元が購読者への参照を保持しますDispose() で購読を解除するか、弱いイベントパターンを使用します--
コンストラクターでイベントを発生させる購読者がまだ登録されていない可能性があり、派生クラスの構築も完了していません完全に初期化されたインスタンスからのみイベントを発生させますCA2214
イベントハンドラーでの async void の誤用async void はイベントハンドラーで有効なシグネチャですが、例外を捕捉できません本体を try/catch で囲み、例外を明示的に記録して処理します--
イベントハンドラーで null を確認しない購読者がいない場合に NullReferenceException が発生しますevent?.Invoke() の null 条件付きパターンを使用します--
静的イベントのクリーンアップを行わないルート参照によって、アプリケーションの実行中は GC が参照を回収できませんインスタンスイベントを優先するか、WeakEventManager を使用します--

関連項目:[skill:dotnet-csharp-async-patterns] では、async void のファイア・アンド・フォーゲットパターンを詳しく説明しています。


5. 設計上の問題点

問題点しきい値有害な理由修正方法
ゴッドクラス500 行超責務が多すぎて、テストや保守が困難になりますSRP に従って、まとまりのあるクラスを切り出します
長すぎるメソッド30 行超理解、テスト、レビューが困難になります説明的な名前を付けたヘルパーメソッドを切り出します
長すぎるパラメーターリスト5 個超抽象化が不足していることを示しますパラメーターオブジェクトまたはビルダーを導入します
フィーチャー・エンヴィメソッドが自身のクラスよりも別のクラスのデータを多く使用する責務の配置が不適切で、結合度が高くなりますメソッドを、そのメソッドが依存しているクラスに移動します
プリミティブ型偏重ドメイン概念を生の string/int で表現する型安全性がなく、検証が分散します値オブジェクトまたは強く型付けされた ID を導入します
深いネストインデントが 3 階層超制御フローを追いにくくなりますガード節(早期リターン)を使い、メソッドを切り出します

6. 例外処理の不足

問題点有害な理由修正方法ルール
空の catch ブロックエラーを黙って握りつぶし、バグを隠します最低限、例外を記録します。例外を伝播させることを優先しますCA1031
基底クラスの Exception を捕捉するOutOfMemoryException、StackOverflowException なども捕捉します具体的な例外型を捕捉しますCA1031
記録して握りつぶす(catch { log; })呼び出し元が操作の失敗を把握できません記録後に再スローするか、エラー結果を返します--
finally で例外をスローする元の例外が新しい例外で隠されますfinally 内で try/catch を使用し、finally から例外をスローしないでください--
throw; ではなく throw ex; を使うスタックトレースがリセットされ、元の失敗箇所が隠されますスタックトレースを保持するため、引数なしの throw; を使用しますCA2200
内部例外を含めない例外をラップした際に原因の連鎖が失われます元の例外を innerException パラメーターに渡します--

関連項目:[skill:dotnet-csharp-async-patterns] では、ファイア・アンド・フォーゲットおよび async void のシナリオにおける例外処理を説明しています。


クイックリファレンス:CA ルール

ルール説明
CA1031一般的な例外型を捕捉しない
CA1816GC.SuppressFinalize を正しく呼び出す
CA1826インデックス可能なコレクションに Enumerable メソッドを使用しない
CA1827Any() が使える場合に Count()/LongCount() を使用しない
CA1851IEnumerable コレクションが複数回列挙される可能性がある
CA2000スコープを抜ける前にオブジェクトを破棄する
CA2200スタックの詳細を保持するために再スローする
CA2213破棄可能なフィールドを破棄する
CA2214コンストラクターでオーバーライド可能なメソッドを呼び出さない

プロジェクトで <AnalysisLevel>latest-all</AnalysisLevel> を設定すると、これらを有効にできます。アナライザーの構成については、[skill:dotnet-csharp-coding-standards] を参照してください。


参考資料

コメント