mcpField
T
The Standard Code CSharp

The Standard Code CSharp

github.com/hassanhabib開発2073 回インストール13 週間前に更新
リポジトリ

The Standard Code CSharp について

The Standard Code CSharpは、C#および.NETコードの生成、レビュー、リファクタリング、評価に使うスタイルスキルです。ファイル、命名、メソッド、コード構成の規約を定めています。

主な機能

  1. 01PascalCaseによるファイル命名規約
  2. 02略語を避けた明確な名前
  3. 03変数宣言のルール
  4. 04メソッド命名と非同期サフィックスの規約
  5. 05メソッド構造とコード構成の指針

活用シーン

  1. 01統一規約に沿ったC#コードの生成
  2. 02C#コードのスタイル問題のレビュー
  3. 03.NETのメソッドと宣言のリファクタリング
  4. 04命名とファイル構成の規則の適用

インストール

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

$npx skills add https://github.com/hassanhabib/the-standard-skills --skill the-standard-code-csharp

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

manifest.json
contracts/
contracts.json
examples/bad/
example_bad_methods.cs
example_bad_classes.cs
example_bad_variables.cs
examples/good/
example_methods.cs
example_variables.cs
example_classes.cs
rules/
rules.json
rules.md
templates/
service_template.cs
broker_template.cs
validations/
checklist.md
anti-patterns.md

詳細

The Standard Code CSharp

このスキルについて

このスキルは、The Standard における C# の標準的なコーディングスタイルと構成の基準です。 C# コードを生成、レビュー、リファクタリング、評価するときは、必ず使用してください。

明示的な対象範囲

このスキルは、提供されているすべての C# スタイルルールファイルを明示的に対象とします。

  • ファイル
  • 変数
  • メソッド
  • クラスとインターフェース
  • コメントとドキュメント

また、実装仕様で提示された実装プロファイルの命名規則も維持します。

使用する場面

モデル、ブローカー、サービス、コントローラー、テスト、サポートコードなど、あらゆる C# または .NET コードでこのスキルを使用してください。

必須の運用ルール

  1. これらのルールに正確に従ってください。
  2. 個人的な好みより、明確で読みやすいコードを優先してください。
  3. チーム内の慣習が異なる場合でも、Standard の命名規則を使用してください。
  4. 略語、曖昧な名前、冗長なラッパー、一貫性のないフォーマットは避けてください。
  5. 可能な限り、メソッドの詳細度をレベル 0 に保ってください。
  6. チェーンや長い宣言によって認知的負荷が高くなった場合は、コードを分割してください。
  7. モデルをインスタンス化するときは、プロパティの順序を維持してください。
  8. private フィールドには this. を使用してください。
  9. 多次元クラスでは、partial ファイルの規約を維持してください。

標準ルールセット

0 ファイル

以下は、C# ファイルの命名規則と命名のガイドラインです。

0.0 命名

ファイル名には、PascalCase とファイル拡張子 .cs を使用してください。

する
Student.cs
これもする
StudentService.cs
しない
student.cs
これもしない
studentService.cs
これもしない
Student_Service.cs
0.1 Partial クラスのファイル

Partial クラスのファイルとは、ルートファイルに対する入れ子クラスを含むファイルです。例:

  • StudentService.cs
    • StudentService.Validations.cs
    • StudentService.Exceptions.cs

Validations と Exceptions は、特定のクラスの異なる側面を多次元空間で表現するための partial クラスです。

する
StudentService.Validations.cs
これもする
StudentService.Validations.Add.cs
しない
StudentServiceValidations.cs
これもしない
StudentService_Validations.cs

0 変数

0.0 命名

変数名は簡潔で、その値が持つ、または持つ可能性のある性質と数量を表すものにしてください。

0.0.0 名前
する
var student = new Student();
しない
var s = new Student();
これもしない
var stdnt = new Student();

同じルールはラムダ式にも適用されます。

する
students.Where(student => student ... );
しない
students.Where(s => s ... );

これは、テストダブルを含む、ラムダパラメーターが使われるすべての箇所に当てはまります。モックのセットアップや検証では、意味のある名前、つまりパラメーターが表す型を使用し、1 文字だけの名前は使わないでください。

する
storageBrokerMock.Setup(broker => broker.InsertStudentAsync(student));
しない
storageBrokerMock.Setup(b => b.InsertStudentAsync(student));

0.0.1 複数形
する
var students = new List<Student>();
しない
var studentList = new List<Student>();

0.0.2 型を含む名前
する
var student = new Student();
しない
var studentModel = new Student();
これもしない
var studentObj = new Student();

0.0.3 null またはデフォルト値

変数の値が int の 0 や文字列の null などのデフォルト値であり、その値を変更する予定がない場合(たとえばテスト目的の場合)、その値がわかる名前を付けてください。

する
Student noStudent = null;
しない
Student student = null;
これもする
int noChangeCount = 0;
ただし、しない
int changeCount = 0;



0.1 宣言

変数の宣言とインスタンス化では、値が後で決まる場合でも、変数の具体的な型がわかるようにしてください。

0.1.0 明確な型

右辺から型が明らかな場合は、変数の宣言に var を使用してください。

する
var student = new Student();
しない
Student student = new Student();



0.1.1 ある程度明確な型

戻り値の型が右辺から明らかではないものの、わかっている場合は、変数の型を明示して宣言してください。

する
Student student = GetStudent();
しない
var student = GetStudent();

0.1.2 不明確な型

戻り値の型が右辺から明らかではなく、不明な場合(匿名型など)は、変数の型として var を使用できます。

する
var student = new
{
    Name = "Hassan",
    Score = 100
};



0.1.3 単一プロパティ型

プロパティが 1 つの型を宣言する場合は、プロパティに直接値を代入してください。

する
var inputStudentEvent = new StudentEvent();
inputStudentEvent.Student = inputProcessedStudent;
しない
var inputStudentEvent = new StudentEvent
{
    Student = inputProcessedStudent
};
これもする
var studentEvent = new StudentEvent
{
    Student = someStudent,
    Date = someDate
}
しない
var studentEvent = new StudentEvent();
studentEvent.Student = someStudent;
studentEvent.Date = someDate;
0.2 構成
0.2.0 分割

変数宣言が120文字を超える場合は、等号の位置から改行して分割します。

する
List<Student> washingtonSchoolsStudentsWithGrades = 
    await GetAllWashingtonSchoolsStudentsWithTheirGradesAsync();

しない
List<Student> washgintonSchoolsStudentsWithGrades = await GetAllWashingtonSchoolsStudentsWithTheirGradesAsync();

0.2.1 複数の宣言

2行以上にわたる宣言は、前後の変数宣言と区切るために、宣言の前後に改行を入れます。

する
Student student = GetStudent();

List<Student> washingtonSchoolsStudentsWithGrades = 
    await GetAllWashingtonSchoolsStudentsWithTheirGradesAsync();

School school = await GetSchoolAsync();
しない
Student student = GetStudent();
List<Student> washgintonSchoolsStudentsWithGrades = 
    await GetAllWashingtonSchoolsStudentsWithTheirGradesAsync();
School school = await GetSchoolAsync();

また、1行だけの変数宣言の間には改行を入れません。

する
Student student = GetStudent();
School school = await GetSchoolAsync();
しない
Student student = GetStudent();

School school = await GetSchoolAsync();


1 メソッド

1.0 命名

メソッド名は、そのメソッドの処理内容を要約するものにします。同期性を踏まえた操作内容を正確かつ簡潔に表す必要があります。

1.0.0 動詞

メソッド名には、実行するアクションを表す動詞を含める必要があります。

する
public List<Student> GetStudents()
{
	...
}

しない
public List<Student> Students()
{
	...
}

1.0.1 非同期性

一般に、Task や ValueTask を返すメソッドなどの非同期メソッドには、末尾に Async を付けます。

する
public async ValueTask<List<Student>> GetStudentsAsync()
{
	...
}
しない
public async ValueTask<List<Student>> GetStudents()
{
	...
}

1.0.2 入力パラメーター

入力パラメーターは、オブジェクトのどのプロパティに割り当てるのか、または検索などのアクションにどのように使用するのかを明確にします。

する
public async ValueTask<Student> GetStudentByNameAsync(string studentName)
{
	...
}
しない
public async ValueTask<Student> GetStudentByNameAsync(string text)
{
	...
}
これもしない
public async ValueTask<Student> GetStudentByNameAsync(string name)
{
	...
}

1.0.3 アクションパラメーター

特定のパラメーターを使ってメソッドがアクションを実行する場合は、そのことを明示します。

する
public async ValueTask<Student> GetStudentByIdAsync(Guid studentId)
{
	...
}

しない
public async ValueTask<Student> GetStudentAsync(Guid studentId)
{
	...
}

1.0.4 パラメーターの受け渡し

メソッドを使用する際、入力パラメーターのエイリアスが、渡す変数の名前の一部または全部と一致する場合は、エイリアスを指定する必要はありません。一致しない場合は、エイリアスを指定して値を渡します。

次のようなメソッドがあるとします。

Student GetStudentByNameAsync(string studentName);
する
string studentName = "Todd";
Student student = await GetStudentByNameAsync(studentName);

これもする
Student student = await GetStudentByNameAsync(studentName: "Todd");
これもする
Student student = await GetStudentByNameAsync(toddName);
しない
Student student = await GetStudentByNameAsync("Todd");
しない
Student student = await GetStudentByNameAsync(todd);



1.1 構成

一般に、同じロジックの複数行は独自のメソッドにまとめ、メソッドは常に詳細度レベル0に保ちます。

1.1.0 1行/複数行
1.1.0.0 1行

コードが1行だけのメソッドには、ファットアローを使用します。

する
public List<Student> GetStudents() => this.storageBroker.GetStudents();

しない
public List<Student> Students()
{
	return this.storageBroker.GetStudents();
}

1行のメソッドが120文字を超える場合は、ファットアローの後で改行し、新しい行にタブを1つ追加します。

する
public async ValueTask<List<Student>> GetAllWashingtonSchoolsStudentsAsync() => 
	await this.storageBroker.GetStudentsAsync();
しない
public async ValueTask<List<Student>> GetAllWashingtonSchoolsStudentsAsync() => await this.storageBroker.GetStudentsAsync();

1.1.0.1 複数行

ファットアロー(式本体)メソッドの本体は、矢印の後の1行に収める必要があります。本体が2行以上になる場合(連結された呼び出し、または引数が複数行に折り返される呼び出しなど)は、明示的な return を含むスコープ付きの { } 本体を必ず使用します。メソッド自体のパラメーターを複数行に折り返しても、ファットアローを使える理由にはなりません。全体が「矢印1つ+1行」として読めない場合は、スコープを使います。

する
public Student AddStudent(Student student)
{
	ValidateStudent(student);

	return this.storageBroker.InsertStudent(student);
}
これもする
public Student AddStudent(Student student)
{
	return this.storageBroker.InsertStudent(student)
		.WithLogging();
}
これもする
public Student AddStudent(
	Student student)
{
	return this.storageBroker.InsertStudent(student);
}
しない
public Student AddStudent(Student student) =>
	this.storageBroker.InsertStudent(student)
		.WithLogging();
これもしない
public Student AddStudent(
	Student student) =>
		this.storageBroker.InsertStudent(student);

引数が複数行に折り返される呼び出しも複数行の本体です。スコープを使い、ファットアローは使いません。

しない
private async ValueTask<TResult> PostAsync<TContent, TResult>(
	string relativeUrl,
	TContent content) =>
	await this.apiClient.PostContentAsync<TContent, TResult>(
		relativeUrl,
		content,
		serializationFunction: async value => Serialize(value),
		deserializationFunction: async json => Deserialize<TResult>(json));
する
private async ValueTask<TResult> PostAsync<TContent, TResult>(
	string relativeUrl,
	TContent content)
{
	return await this.apiClient.PostContentAsync<TContent, TResult>(
		relativeUrl,
		content,
		serializationFunction: async value => Serialize(value),
		deserializationFunction: async json => Deserialize<TResult>(json));
}

意図的な例外が1つあります。本体が波かっこ付きラムダを受け取る単一の呼び出しであるファットアローでは、ラムダ自体の { } をスコープとして維持するため、ファットアローのままにします。これは TryCatch サービスパターンであり、正しい書き方です。

する
public ValueTask<Student> AddStudentAsync(Student student) =>
TryCatch(async () =>
{
	ValidateStudent(student);

	return await this.storageBroker.InsertStudentAsync(student);
});
1.1.1 戻り値

複数行のメソッドでは、メソッドのロジックと最後の戻り値の行(ある場合)の間に改行を入れます。

する
public List<Student> GetStudents()
{
	StudentsClient studentsApiClient = InitializeStudentApiClient();

	return studentsApiClient.GetStudents();
}
しない
public List<Student> GetStudents()
{
	StudentsClient studentsApiClient = InitializeStudentApiClient();
	return studentsApiClient.GetStudents();
}

1.1.2 複数の呼び出し

複数のメソッドを呼び出す場合、両方の呼び出しが120文字未満であれば、最後の呼び出しがメソッドの戻り値でない限り、続けて記述できます。それ以外の場合は改行で区切ります。

する
public List<Student> GetStudents()
{
	StudentsClient studentsApiClient = InitializeStudentApiClient();
	List<Student> students = studentsApiClient.GetStudents();

	return students; 
}
しない
public List<Student> GetStudents()
{
	StudentsClient studentsApiClient = InitializeStudentApiClient();

	List<Student> students = studentsApiClient.GetStudents();

	return students; 
}
これもする
public async ValueTask<List<Student>> GetStudentsAsync()
{
	StudentsClient washingtonSchoolsStudentsApiClient = 
		await InitializeWashingtonSchoolsStudentsApiClientAsync();

	List<Student> students = studentsApiClient.GetStudents();

	return students; 
}
しない
public async ValueTask<List<Student>> GetStudentsAsync()
{
	StudentsClient washingtonSchoolsStudentsApiClient = 
		await InitializeWashingtonSchoolsStudentsApiClientAsync();
	List<Student> students = studentsApiClient.GetStudents();

	return students; 
}

1.1.3 宣言

メソッド宣言は120文字を超えないようにします。

する
public async ValueTask<List<Student>> GetAllRegisteredWashgintonSchoolsStudentsAsync(
	StudentsQuery studentsQuery)
{
	...
}
しない
public async ValueTask<List<Student>> GetAllRegisteredWashgintonSchoolsStudentsAsync(StudentsQuery studentsQuery)
{
	...
}

#### 1.1.4 複数のパラメーター 複数のパラメーターを渡し、メソッド呼び出しの長さが120文字を超える場合は、パラメーターごとに改行し、各行に**1つ**のパラメーターを記述する必要があります。 ##### 推奨 ```cs List redmondHighStudents = await QueryAllWashingtonStudentsByScoreAndSchoolAsync( MinimumScore: 130, SchoolName: "Redmond High"); ```
非推奨
List<Student> redmondHighStudents = await QueryAllWashingtonStudentsByScoreAndSchoolAsync(
	MinimumScore: 130,SchoolName: "Redmond High");
1.1.5 メソッドチェーン(醜化/美化)

メソッドによっては、ほかのメソッドを呼び出すための拡張機能が用意されています。たとえば、Where() メソッドの後に Select() メソッドを呼び出せます。このように、クエリが完成するまで続けます。

醜化して美化するプロセスに従います。メソッドチェーンの見た目を美しくするため、コードをあえて醜くします。例をいくつか示します。

推奨
	students.Where(student => student.Name is "Elbek")
		.Select(student => student.Name)
			.ToList();
非推奨
	students
		.Where(student => student.Name is "Elbek")
			.Select(student => student.Name)
				.ToList();

最初の方法では、呼び出しが増えるほどコードが醜くなるため、メソッドチェーンを簡潔にし、途中で分割することを徹底します。たとえば、次のようになります。

	students.SomeMethod(...)
		.SomeOtherMethod(...)
			.SomeOtherMethod(...)
				.SomeOtherMethod(...)
					.SomeOtherMethod(...);

醜化のプロセスでは、チェーンを短いリストに分割してから処理します。2つ目の方法(醜化しない方法)では、次のように、新しいステートメントと既存のステートメントを区別するために、さらに認知的な負荷がかかることがあります。

	student
		.Where(student => student.Name is "Elbek")
		.Select(student => student.Name)
		.OrderBy(student => student.Name)
		.ToList();
	
	ProcessStudents(students);

4 クラス

4.0 命名

Standard-Compliant アーキテクチャにおいて、サービスやブローカーを表すクラスは、命名規則でクラスの種類を示す必要があります。ただし、モデルには適用されません。

4.0.0 モデル
推奨
class Student {
	...
}
非推奨
class StudentModel {

}

4.0.1 サービス

ビジネスロジックを含むクラスには、単数形の名前を付けます。

推奨
class StudentService {
	....
}
非推奨
class StudentsService{
	...
}
こちらも非推奨
class StudentBusinessLogic {
	...
}
こちらも非推奨
class StudentBL {
	...
}

4.0.2 ブローカー

サービスと外部リソースの間を仲介するクラスには、単数形の名前を付けます。

推奨
class StudentBroker {
	....
}
非推奨
class StudentsBroker {
	...
}

4.0.3 コントローラー

ロジックを RESTful 操作で公開するエンドポイント(/api/students など)を表すため、複数形の名前を付けます。

推奨
class StudentsController {
	....
}
非推奨
class StudentController {
	...
}



4.1 フィールド

フィールドは、クラスまたは構造体内に直接宣言される、任意の型の変数です。フィールドは、それを含む型のメンバーです。

4.1.0 命名

クラスのフィールドには、キャメルケースの名前を付けます。

推奨
class StudentsController {
	private readonly string studentName;
}
非推奨
class StudentController {
	private readonly string StudentName;
}
こちらも非推奨
class StudentController {
	private readonly string _studentName;
}

変数のセクションで説明した命名規則に従います。


4.1.1 参照

クラスのプライベートフィールドを参照する場合は、this キーワードを使って、プライベートなクラスメンバーと、スコープ内のメソッドまたはコンストラクターの変数を区別します。

推奨
class StudentsController {
	private readonly string studentName;
	
	public StudentsController(string studentName) {
		this.studentName = studentName;
	}
}
非推奨
class StudentController {
	private readonly string _studentName;

	public StudentsController(string studentName) {
		_studentName = studentName;
	}
}



4.2 インスタンス化
4.2.0 入力パラメーターのエイリアス

入力変数名が入力エイリアスと一致する場合はそれらを使います。一致しない場合は、特に値を渡す際に、エイリアスを使う必要があります。

推奨
int score = 150;
string name = "Josh";

var student = new Student(name, score);

こちらも推奨
var student = new Student(name: "Josh", score: 150);

非推奨
var student = new Student("Josh", 150);

こちらも非推奨
Student student = new (...);
4.2.1 プロパティの順序を守る

クラスのインスタンスを作成する際は、プロパティの割り当て順序がクラス宣言内のプロパティの順序と一致するようにします。

推奨
public class Student
{
	public Guid Id {get; set;}
	public string Name {get; set;}
}

var student = new Student
{
	Id = Guid.NewGuid(),
	Name = "Elbek"
}
こちらも推奨
public class Student
{
	private readonly Guid id;
	private readonly string name;

	public Student(Guid id, string name)
	{
		this.id = id;
		this.name = name;
	}
}

var student = new Student (id: Guid.NewGuid(), name: "Elbek");
非推奨
public class Student
{
	public Guid Id {get; set;}
	public string Name {get; set;}
}

var student = new Student
{
	Name = "Elbek",
	Id = Guid.NewGuid()
}
こちらも非推奨
public class Student
{
	private readonly Guid id;
	private readonly string name;

	public Student(string name, Guid id)
	{
		this.id = id;
		this.name = name;
	}
}

var student = new Student (id: Guid.NewGuid(), name: "Elbek");
こちらも非推奨
public class Student
{
	private readonly Guid id;
	private readonly string name;

	public Student(Guid id, string name)
	{
		this.id = id;
		this.name = name;
	}
}

var student = new Student (name: "Elbek", id: Guid.NewGuid());

12 コメント

12.0 はじめに

コメントは、コードだけでは説明できないことを説明する場合にのみ使用できます。コードが見えるかどうかは問いません。

12.1 著作権

著作権を示すコメントは、次の形式に従います。

推奨
    // ---------------------------------------------------------------
    // Copyright (c) Coalition of the Good-Hearted Engineers
    // FREE TO USE TO CONNECT THE WORLD
    // ---------------------------------------------------------------
非推奨

    //----------------------------------------------------------------
    // <copyright file="StudentService.cs" company="OpenSource">
    //      Copyright (C) Coalition of the Good-Hearted Engineers
    // </copyright>
    //----------------------------------------------------------------

こちらも非推奨
   /* 
    * ==============================================================
    * Copyright (c) Coalition of the Good-Hearted Engineers
    * FREE TO USE TO CONNECT THE WORLD
    * ==============================================================
    */
12.2 メソッド

開発時にコードにアクセスできないメソッド、または複雑な処理を行うメソッドのドキュメントには、次の詳細を含める必要があります。

  • 目的
  • 入力
  • 出力
  • 副作用

実装プロファイルの命名に関する補足

10. Naming Conventions

ElementPatternExample
Broker interfaceI{Resource}BrokerIStorageBroker, IModernApiBroker, ILoggingBroker
Broker class{Resource}BrokerStorageBroker, ModernApiBroker, LoggingBroker
Broker method{Action}{Entity}AsyncInsertLegacyUserAsync, PostPersonAsync
Service interfaceI{Entity}ServiceILegacyUserService
Service class{Entity}ServiceLegacyUserService
Service methodAdd{Entity}AsyncAddLegacyUserAsync
Inner exception{Adjective}{Entity}ExceptionNullLegacyUserException
Outer exception{Entity}{Category}ExceptionLegacyUserValidationException
Test class{Entity}ServiceTestsLegacyUserServiceTests
Test methodShould{Action}Async / ShouldThrow{Exception}On{Action}…ShouldAddLegacyUserAsync

コメント