クラスの基本的な使い方が分かったら、次はその機能をさらに拡張し、より堅牢なプログラムを組むためのステップに進みましょう。
この記事では、クラスのコードを再利用するための継承、インスタンスを正しく初期化するためのコンストラクタ、そして変数が使える範囲を定義するスコープといった、中級者への扉を開く重要な概念を解説します。
変数の有効範囲(スコープ)と this
クラス内で変数を扱う際には、「その変数がどこからどこまで使えるのか」という**有効範囲(スコープ)**を意識することが重要です。
フィールドとローカル変数の違い
クラスの変数は、定義された場所によってスコープが異なります。
- フィールド: クラス直下で宣言される変数。そのクラス内のすべてのメソッドから利用可能です。
- ローカル変数: メソッドの中で宣言される変数。宣言されたそのメソッドの中でのみ利用可能です。
アナロジー: フィールドは会社全体の共有備品(誰でも使える)、ローカル変数はある会議でだけ使われるホワイトボード(その会議が終われば使えない)のようなものです。
コードで見る違い
class Player
{
// フィールド: クラス全体で共有される
public int level = 1;
public void LevelUp()
{
// メソッド内でフィールドの値を変更
level = level + 1;
Console.WriteLine("レベルが " + level + " に上がった!");
}
public void ShowStatus()
{
// 別のメソッドからもフィールドは利用可能
Console.WriteLine("現在のレベル: " + level);
// 下のエラーになるコードを有効にするとコンパイルエラー
// Console.WriteLine(experience); // Error: experienceはShowStatus内には存在しない
}
public void GainExperience()
{
// ローカル変数: このメソッド内でのみ有効
int experience = 50;
Console.WriteLine(experience + " の経験値を獲得した。");
}
}
this キーワードの使い方
thisは、クラスのインスタンスそのものを指す特別なキーワードです。主に、メソッドの引数名とフィールド名が同じになってしまった場合に、区別するために使います。
class Player
{
public string name;
// 引数名(name)とフィールド名(name)が同じ
public void SetName(string name)
{
// this.name は「このインスタンスのフィールドname」を指す
// 右辺の name は「引数として渡されてきたname」を指す
this.name = name;
}
}
継承とは?クラスの機能を引き継いで拡張する
継承とは、あるクラス(親クラス)の機能(フィールドやメソッド)を、別のクラス(子クラス)がそのまま引き継ぐ仕組みです。これにより、コードの再利用性が高まり、効率的な開発が可能になります。
アナロジー: 「犬」クラスは「動物」クラスを継承します。「動物」が共通して持つ「名前」や「食べる」といった機能を「犬」は引き継ぎつつ、「犬」独自の「吠える」という機能を追加するイメージです。
サンプルコード
// 親クラス (基底クラス)
class Animal
{
public string name;
public void Eat()
{
Console.WriteLine(name + "は食事をした。");
}
}
// Animalクラスを継承した子クラス (派生クラス)
class Dog : Animal
{
// Dogクラス独自のメソッド
public void Bark()
{
Console.WriteLine(name + "はワン!と吠えた。");
}
}
// --- 使い方 ---
Dog myDog = new Dog();
myDog.name = "ポチ"; // 親クラス(Animal)のフィールドを利用
myDog.Eat(); // 親クラス(Animal)のメソッドを利用
myDog.Bark(); // 子クラス(Dog)独自のメソッドを利用
実行結果
ポチは食事をした。
ポチはワン!と吠えた。
コンストラクタによる初期化
これまでは、newでインスタンスを作成した後に、フィールドへ一つずつ値を代入していました。しかしこの方法では、必須の値を設定し忘れる可能性があります。
コンストラクタは、newでインスタンスが作成されるときに自動的に呼び出される特別なメソッドです。これを使うことで、インスタンス作成時に必須の値を確実に設定させることができます。
- コンストラクタは、クラス名と同じ名前で定義します。
- 戻り値(
voidなど)は書きません。
サンプルコード
class Player
{
public string name;
public int level;
// コンストラクタ: nameとlevelを引数で受け取る
public Player(string initialName, int initialLevel)
{
Console.WriteLine("プレイヤーが生成されました。");
this.name = initialName;
this.level = initialLevel;
}
public void ShowStatus()
{
Console.WriteLine("名前: " + name + " レベル: " + level);
}
}
// --- 使い方 ---
// newキーワードでインスタンス化する際に、コンストラクタの引数を渡す
Player hero = new Player("勇者", 1);
hero.ShowStatus();
実行結果
プレイヤーが生成されました。
名前: 勇者 レベル: 1
コンストラクタを定義したことで、new Player()のように引数なしでインスタンスを作成しようとするとエラーになり、値の設定漏れを防ぐことができます。
staticキーワードとは?
クラスのメンバー(フィールドやメソッド)には、インスタンスごとに存在するインスタンスメンバーと、クラス全体で共有されるstatic(静的)メンバーの2種類があります。
アナロジー: インスタンスメンバーは「個々の生徒のテストの点数」(生徒ごとに異なる)、staticメンバーは「クラスの生徒数」(クラス全体で共通の値)のようなものです。
staticなメンバーは、インスタンスを作成せず「クラス名.メンバー名」の形で直接呼び出します。
// DateTimeというクラスに用意されている静的プロパティNowを利用
DateTime currentTime = DateTime.Now;
Console.WriteLine("現在は " + currentTime.Year + "年です。");
なぜ Main メソッドは static なのか?
プログラムを実行するとき、C#はまず Main メソッドを探して処理を開始します。このとき、プログラムはまだ始まったばかりで、Programクラスのインスタンスは一つも作られていません。
もしMainメソッドがstaticでなければ、インスタンス(new Program())を作らないと呼び出せず、プログラムの開始点がなくなってしまいます。そのため、インスタンスがなくても直接呼び出せるように、Mainメソッドはstaticと定められています。
まとめ
今回は、クラスをより深く理解するための重要な概念を学びました。
- スコープ: フィールドはクラス全体、ローカル変数はメソッド内でのみ有効。
thisはインスタンス自身を指す。 - 継承: 親クラスの機能を子クラスが引き継ぎ、コードの再利用性を高める。
- コンストラクタ: インスタンス作成時に自動で呼ばれ、オブジェクトを適切に初期化する。
- static: インスタンスに依存せず、クラス全体で共有されるメンバー。
これらの概念をマスターすることで、あなたの書くC#コードは、より構造的で、安全で、効率的なものになるでしょう。
