コードの品質を上げ、メンテナンスしやすいコードを書くためには「再代入を避ける」ことが重要です。
本記事では、再代入がもたらすデメリットと、それを避けるためのコード改善方法についてC#のサンプルコードで詳しく解説します。
紹介するテクニックは、C#に限らず他のプログラミング言語にも応用可能です。シンプルで効果的な改善を目指しましょう!
再代入のデメリットとは?
プログラムで変数に再代入を行うと、コードの可読性が下がり、バグが発生しやすくなります。
特に複雑な処理やロジックの中で変数が複数回再代入されると、意図しない動作を引き起こしやすく、デバッグも難しくなります。
そこで、「再代入を避ける」ためのテクニックを取り入れることが、コードの保守性と品質を向上させる重要なポイントになります。
再代入を避けるための基本的なテクニック
再代入を避けるための基本的なテクニックとして、以下のような方針があります。
- 定数化(readonlyまたはconst):値が変更されない変数には、readonlyやconstを使いましょう。
- 計算や操作の結果を新しい変数に保持:処理の中で新たな値が必要な場合は、新しい変数を定義して使用するようにします。
特にC#では、readonly
を活用することで再代入を防ぎ、コードの意図を明確にすることができます。
悪い例と良い例のコード解説
以下に、RPGゲームのキャラクターが経験値を獲得し、レベルアップするコードの例を示します。
public class Character
{
public int ExperiencePoints;
public int Level;
public void GainExperience(int points)
{
ExperiencePoints += points;
if (ExperiencePoints >= 100)
{
ExperiencePoints -= 100;
Level += 1;
}
}
}
この例では、ExperiencePoints
が複数回再代入されており、経験値の変動が複雑で分かりにくいコードになっています。また、Level
も加算される度に再代入され、どの時点でレベルアップしたのか把握しづらくなっています。
public class Character
{
private readonly int _levelUpThreshold = 100;
public int ExperiencePoints { get; private set; }
public int Level { get; private set; }
public void GainExperience(int points)
{
int newExperience = ExperiencePoints + points;
if (newExperience >= _levelUpThreshold)
{
Level += 1;
ExperiencePoints = newExperience - _levelUpThreshold;
}
else
{
ExperiencePoints = newExperience;
}
}
}
このコードでは、再代入のリスクを減らすためにreadonly
フィールド(_levelUpThreshold
)を使用しています。
また、GainExperience
メソッドでは新しい変数newExperience
を定義して計算を行い、元のExperiencePoints
に再代入する回数を最小限にしています。
このように、コードの可読性と保守性が大幅に向上しています。
C#で再代入を防ぐ手法
C#では、変数に再代入を防ぐために、readonly
と const
キーワードを使用できます。これらのキーワードを使うことで、コード内での値の変更を制限し、再代入を防ぐ効果があります。ここではそれぞれの特徴と使用例について解説します。
readonly キーワード
readonly
キーワードは、フィールド(クラス内の変数)の初期化後に再代入されることを防ぐために使用されます。readonly
フィールドは、クラスのコンストラクタ内または宣言時にのみ初期化が可能です。そのため、オブジェクトが生成された後はそのフィールドの値を変更できなくなり、再代入を防ぐことができます。
public class Character
{
public readonly int MaxHealth; // 初期化後に変更できないフィールド
public Character(int maxHealth)
{
MaxHealth = maxHealth; // コンストラクタ内でのみ初期化可能
}
public void DisplayHealth()
{
Console.WriteLine("Max Health: " + MaxHealth);
}
}
この例では、MaxHealth
は readonly
フィールドであり、コンストラクタでのみ初期化されています。生成後にこの値を変更することはできないため、再代入のリスクを防ぐことができます。
特徴
- クラスのコンストラクタ内または宣言時に初期化する必要があります。
- 初期化後に再代入できないため、不変の値として利用でき、変更不可の定数値にしたい場合に便利です。
- オブジェクトが生成されるたびに異なる値を持つことができるため、インスタンスごとに設定可能です。
const キーワード
const
キーワードは、完全に不変の定数を宣言するために使用されます。const
フィールドは、プログラムがコンパイルされるときに確定するため、値は変更不可です。
const
はクラスのどのインスタンスにも共通であり、定数として利用できるため、クラス内で同一の固定値を参照する際に使用します。
public class GameSettings
{
public const int MaxLevel = 100; // クラス全体で共通の定数
public void DisplaySettings()
{
Console.WriteLine("Max Level: " + MaxLevel);
}
}
この例では、MaxLevel
は const
フィールドであり、常に100という固定の値を持ちます。この値はプログラムが実行中に変更することができず、すべてのインスタンスで同じ値を共有します。
特徴
- 宣言時にのみ初期化でき、値を変更することはできません。
- クラス全体で共有されるため、複数のインスタンスで同じ定数を利用する場合に適しています。
readonly
と異なり、インスタンスの生成に依存せず、クラス全体で一度設定された値を使用します。
readonly
と const
の使い分け
const
は、プログラム全体で一貫して変わらない固定値に使用します。例として、物理的な定数や構成設定など、どのインスタンスでも共通の値を保持する場合に便利です。readonly
は、インスタンスごとに異なる初期値を設定し、オブジェクト生成後に再代入を防ぎたいフィールドに使用します。例えば、キャラクターの最大HPのようにインスタンスごとに異なる値でありながら、生成後は変更させたくない場合に適しています。
まとめ
readonly
とconst
の違い:const
はクラス全体で共通の不変の値に、readonly
はインスタンスごとに異なる初期値を設定でき、生成後は変更不可となる値に使用します。- 再代入を防ぐ効果:
readonly
とconst
を使うことで、意図しない再代入を防ぎ、コードの可読性と信頼性が向上します。 - 使用場面に応じた使い分け:
プロジェクトでの設定値やインスタンス固有の値に応じて、readonly
とconst
を適切に使い分けることで、バグの防止やメンテナンス性の向上に貢献します。
再代入を防ぐことで、コードはより直感的で信頼性の高いものになります。readonly
や const
を活用したコーディングは、長期的に見て保守やデバッグを容易にし、開発の質を上げるための基本的な手法です。
これらの小さな工夫が、大規模なプロジェクトや長期的な開発で大きな差を生み出します。日常の開発にぜひ取り入れて、堅牢なコードを目指していきましょう。
コメント