Unity を検索

コードを美しく保つ:独自の C# コードスタイルを作成する方法

2022年9月7日 カテゴリ: Engine & platform | 15 分 で読めます
How to create your own C# code style, hero image
How to create your own C# code style, hero image
シェア

Is this article helpful for you?

Thank you for your feedback!

Unity C# のコードフォーマットの方法は複数ありますが、プロジェクトで一貫したコードスタイルを取り決めておくことで、チームはクリーンで読みやすく、スケーラブルなコードベースを作り上げることができます。このブログでは、独自のコードスタイルガイドを作り、それを維持するうえで使えるガイドラインと例をいくつか紹介します。

なおこの記事で紹介する内容は、Microsoft が公開している推奨事項に則ったものであることをお断りしておきます。この記事を読んだことを機会に、皆さんのチームにとって最適な方法を見つけてください。

Unity の C# スタイルガイドを出発点として使う

1 つの Unity プロジェクトに何人の開発者が携わっていても、1 人の開発者によって開発されたように感じられるのが理想的です。スタイルガイドは、より一貫性のあるコードベースを作成するアプローチを一本化するための助けとなります。

できるだけ業界標準に準拠し、独自のスタイルガイドを作成するときは、出発点として既存のスタイルガイドを参照することをお勧めします。社内外の Unity のエキスパートと協力して、Microsoft の包括的な C# スタイルをベースに、着想を得るために役立つ新しい e ブック「Create a C# style guide: Write cleaner code that scales」を制作しました。

Google の C# スタイルガイド も、命名規則、書式規則、コメント規則に関するガイドラインを定義するための素晴らしいリソースです。繰り返しになりますが、フォーマットの方法論に正解はありません。ただ、私たちのガイドを作るうえで、Microsoft の標準に従ったということです。

私たちの e ブックは、Github でサンプル の C# ファイルとともに、無料で入手できます。これらのリソースは両方、Unity での開発中に目にする最も一般的なコーディング規約に焦点を当てています。 ここに示されている内容はすべて基本的に Microsoft のフレームワークデザインのガイドラインのサブセットであり、この記事で紹介するものを以外にも、非常に多くのベストプラクティスが含まれています。

正解も不正解もない

スタイルガイドに記載されているガイドラインは、チームの好みに合わせてカスタマイズすることをお勧めします。皆さんの選択が、私たちの提案や Microsoft のフレームワークデザインのガイドラインと矛盾している場合、皆さんの選択が優先されるべきです。

スタイルガイドの作成には先行投資が必要ですが、後に大きな利益をもたらします。例えば、標準のセットを一元管理することで、開発者が別のプロジェクトに移った場合の立ち上げにかかる時間を短縮することができます。

もちろん、一貫性は重要です。これらの提案に従い、将来スタイルガイドを修正する必要がある場合、いくつかの検索と置換操作により、コードベースを素早く移行することができます。

C# コードのスタイルガイドに含まれるべき内容

日常的なユースケースの大半をカバーし、ニーズに合った実用的なスタイルガイドを作成することに集中しましょう。最初からすべてのエッジケースを想定して、スタイルガイドを作りこみ過ぎないようにしてください。このガイドは、プロジェクトごとにチームが繰り返し洗練を重ねることで、時間の経過とともに有機的に進化していきます。 

ほとんどのスタイルガイドには、基本的なフォーマットのルールが記載されています。一方、具体的な命名規則、名前空間の使用方針、クラスの戦略などは、時間をかけて改良していくことになる、やや抽象的な領域です。

ここでは、スタイルガイドで考慮すべき一般的な書式と命名規則について見ていきましょう。

フォーマットのルール

C# で一般的なインデント形式は、開き波かっこで改行するオールマンスタイル(BSD Unix に由来する BSD 形式とも呼ばれる)と、開き波かっこを直前のヘッダーと同じ行に置く K&R スタイル(one true brace style; 真の波かっこスタイル)の 2 種類があります。

読みやすさを追求するため、Microsoft のフレームワークデザインのガイドラインに基づき、オールマンスタイルを採用しました。

// EXAMPLE: Allman or BSD style puts opening brace on a new line.

void DisplayMouseCursor(bool showMouse) 
{
     if (!showMouse)
     {
          Cursor.lockState = CursorLockMode.Locked;
          Cursor.visible = false;
     }
}

// EXAMPLE: K&R style puts opening brace on the previous line.

void DisplayMouseCursor(bool showMouse){
     if (!showMouse) {
          Cursor.lockState = CursorLockMode.Locked;
          Cursor.visible = false;
     }
}

どのようなスタイルを選んだ場合でも、チームのプログラマー全員に選んだスタイルを順守してもらうようにしてください。

ネストされた複数行にわたる文に波かっこを入れるかどうかも、ガイドに記載しておくべきでしょう。以下に示した例で波かっこを削除してもエラーにはなりませんが、読むときに混乱する可能性があります。そのため私たちのガイドでは、必須とはしていませんが、わかりやすいように波かっこをつけることを推奨しています。

// EXAMPLE: Keep braces for clarity.

for (int i = 0; i < 10; i++)
{
	for (int j = 0; j < 10; j++)
     {
		ExampleAction();
     }
}
// AVOID: Removing braces from nested multiline statements

for (int i = 0; i < 10; i++)
	for (int j = 0; j < 10; j++)
		ExampleAction();

読みやすさを向上させるために

水平方向にスペースを入れるだけで、画面上でのコードの見栄えを良くすることができます。フォーマットの好みは人それぞれですが、ここでは私たちのスタイルガイドから、全体的な読みやすさを向上させるための推奨事項をいくつか紹介します。

  • スペースを入れてコードの密度を下げましょう。空白を余分に入れることで、行のパーツが視覚的に分離されたように感じさせることができます。
// EXAMPLE: Add spaces to make lines easier to read.
for (int i = 0; i < 100; i++) { DoSomething(i); }

// AVOID: No spaces
for(int i=0;i<100;i++){DoSomething(i);}
  • 関数の引数を区切るときは、カンマの後に半角のスペースを入れます。
// EXAMPLE: Single space after comma between arguments
CollectItem(myObject, 0, 1);

// AVOID:
CollectItem(myObject,0,1);
  • かっこや関数の引数の後にはスペースを入れません。
// EXAMPLE: No space after the parenthesis and function arguments 
DropPowerUp(myPrefab, 0, 1);

// AVOID:
DropPowerUp( myPrefab, 0, 1 );
  • 関数名とかっこの間にはスペースを入れません。
// EXAMPLE: Omit spaces between a function name and parenthesis.
DoSomething()

// AVOID:
DoSomething ()
  • 角かっこの中にスペースは入れないようにします。
// EXAMPLE: Omit spaces inside brackets.
x = dataArray[index];

// AVOID:
x = dataArray[ index ];
  • フロー制御の条件の前には半角スペースを入れましょう。また、フロー比較演算子とかっこの間にはスペースを入れます。
// EXAMPLE: Space before condition; separate parentheses with a space
while (x == y)

// AVOID:
while(x==y)
  • 比較演算子の前後には半角スペースを入れます。
// EXAMPLE: Space before condition; separate parentheses with a space
if (x == y)

// AVOID:
if (x==y)

命名規則

変数は通常、状態を表すので、明確で説明的な名詞を使って名前をつけるようにします。そして、真偽値を示すブーリアン変数の場合は、変数名の前に動詞 をつけるとよいでしょう。ブーリアン変数の値が、例えば「プレイヤーは走っているのか」「ゲームオーバーになったか」という問いに対する答えになるようにすることが多いです。意味を明確にするような動詞を前に置きましょう。このやり方に従うと、説明や条件と組み合わさった名前になることが多いです。例えば、isPlayerDead、isWalking、hasDamageMultiplier などのようになります。

メソッドはアクションを実行するので、経験則では、メソッド名は動詞で始め、必要に応じて文脈を追加していくという名づけ方が良いとされます。例えば、GetDirection、FindTarget など、戻り値の型に応じた名前にします。メソッドの戻り値の型が bool の場合は、メソッド名を質問の形にするのもよいでしょう。

ブーリアン変数と同じように、メソッドが真偽の条件を返す場合は動詞を前に置き、IsGameOver や HasStartedTurn のような質問の形になった名前にします。

イベントとイベントハンドルの命名には、いくつかの規則があります。私たちのスタイルガイドでは、 メソッドと似た形で、動詞句を使ってイベントの名前を付けることとしています。状態変化を的確に伝える名前を選びましょう。 現在分詞や過去分詞を使って、イベントが「前」なのか「後」なのかを表現しましょう。例えば、ドアを開ける前のイベントには OpeningDoor、開けた後のイベントには DoorOpened という名前を付けます。

また、名前に短縮形を使わないことをお勧めします。数文字削ったことで短期的に生産性が上がったように感じるかもしれませんが、今の自分にとって当たり前のことが、1 年後のチームメイトにとって当たり前でなくなっているかもしれません。変数名は、その意図を明らかにし、発音しやすいものでなければなりません。ループや数式では 1 文字の変数名でも問題ありませんが、それ以外の場所では短縮形を避けた方がよいでしょう。母音をいくつか省略することで節約できる時間よりも、明瞭さが重要なのです。

同時に、変数の宣言は 1 行に 1 つにしてください。コンパクトではなくなりますが、エラーが起きにくく、読みやすさも上がります。冗長な名前は避けましょう。Player というクラスであれば、PlayerScore や PlayerTarget というメンバー変数を作成する必要はありません。Score や Target としてよいでしょう。

また、接頭辞や特殊な記号化を多用することは避けてください。 私たちのガイドでは、ローカル変数と区別するために、非公開のメンバー変数の前にアンダースコア(_)を付けることを推奨しています。スタイルガイドによっては、非公開のメンバー変数(m_)、定数(k_)、静的変数(s_)に接頭辞をつけ、名前から変数についてより詳しくわかるようにしているものもあります。

ただし、インターフェース名の前に大文字の「I」を付け、その後に機能を説明する形容詞を付けることは良いプラクティスとされます。イベント発生させるメソッドの前に(サブジェクトの中で)「On」を付けるのもよいでしょう。イベントを起動させるサブジェクトは、通常「OnOpeningDoor」や「OnDoorOpened」など、接頭辞「On」を持つメソッドからイベントを起動します。

// EXAMPLE: Prefix interface names with a capital “I”
public interface IDamageable<T>
{
    void Damage(T damageTaken);
}


// EXAMPLE: Raising an event if you have subscribers
public void OnDoorOpened()
{
    DoorOpened?.Invoke();
}

ケーシングの用語

キャメルケースやパスカルケースは、スネークケースやケバブケース、ハンガリアン記法に比べ、一般的に使用されている規格です。私たちのガイドでは、公開のフィールド、列挙型、クラス、メソッドにはパスカルケースを、非公開の変数にはキャメルケースを推奨していますが、これは Unity での一般的なプラクティスです。

すべてをフォーマット化してはいけない

ここで紹介した以外にも、考慮すべきルールはたくさんあります。ガイドのサンプルと、新しい e ブック「Create a C# style guide: Write cleaner code that scales」には、よりよくコードを構成するためのヒントが多数掲載されています。

クリーンコードのコンセプトは、開発標準セットに準拠することで、よりスケーラブルな開発を実現することを目的としています。スタイルガイドがあれば、開発者が自分が従うべき規則について当て推量をする労力をほとんど削減できるはずです。最終的にこのガイドは、皆さんのチームがコードベースに関するコンセンサスを確立し、皆さんのプロジェクトを商業規模の作品に成長させるうえで役立つものになるでしょう。

スタイルガイドをどの程度包括的にするかは、状況によって変わります。ガイドに、より抽象的で形のない概念に関するルールを盛り込むかどうかは、チーム次第です。これには、名前空間の使用、クラスの細分化、#region ディレクティブのようなディレクティブを実装する(または実装しない)ことなどに関するルールが含まれることがあります。#region は、C# ファイルのコードのセクションを折りたたんだり隠したりして、大きなファイルを管理しやすくするのに役立ちますが、多くの開発者がコードの臭いやアンチパターンと考えているものの一例でもあります。したがって、コードのスタイリングのこれらの側面について、厳格な基準を設定することは避けた方がよいかもしれません。すべてをガイドに記載する必要はなく、チームで話し合って決めるだけでも十分な場合があります。

何よりも明瞭さが大事

このガイドの作成に協力してくれた専門家に話を聞いていたとき、よくもらったアドバイスは、何よりもコードの読みやすさが大事だというものでした。そのためのポイントをご紹介します。

  • 引数を少なくしましょう。引数が多くなると、メソッドの複雑さが増す可能性があります。引数の数を減らすことで、メソッドを読みやすく、テストしやすくすることができます。
  • 過度のオーバーロードを避けましょう。メソッドのオーバーロードは無限に作れてしまいます。どのようにメソッドを呼び出すかを反映したものをいくつか選び、それらを実装します。メソッドをオーバーロードする場合は、各メソッドのシグネチャがそれぞれ異なる数の引数を持つようにし、混乱を防いでください。
  • 副作用を避けましょう。メソッドは、その名前が表していることを行うだけでよいのです。そのスコープ外にあるものを変更することは避けてください。可能な限り、参照ではなく値で引数を渡しましょう。そのため、out や ref キーワードを使って結果を送り返す場合は、返されるものがそのメソッドによって得られると意図したものであることを確認してください。 副作用はある種の作業には有効ですが、意図しない結果を招くことがあります。予期せぬ動作を減らすために、副作用のないメソッドを書きましょう。

コードスタイルガイドと e ブックをダウンロードしよう

このブログが、皆さん独自のスタイルガイドを作成するきっかけになれば幸いです。サンプルの C# ファイルと新しい e ブックをご覧いただき、私たちの提案するルールを確認して、皆さんのチームの好みに合わせてカスタマイズすることもできます。

個別のルールの詳細よりも、全員が一貫してルールに従うことに同意することのほうが重要です。迷ったときは、皆さんのチームが独自に発展させているさなかのガイドをよりどころとして、スタイルの不一致を解決してください。何しろ、これはグループでの取り組みなのです。

Create a C# style guide: Write cleaner code that scales e-book
2022年9月7日 カテゴリ: Engine & platform | 15 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

関連する投稿