【C#】クラスの作成とインスタンス化

目次

概要

ここからはオブジェクト指向の話になる。
PCソフトやアプリに触れていると似たような構造のものがいくつも存在する。

例えば、RPGのステータス。みんな以下のようなステータスを持っている。

  • 名前
  • HP
  • レベル
  • 攻撃力
  • 防御力 etc…

違うと言えば、各キャラクターが固有スキルを持っていたりする。
ここでもし新たに「属性耐性」などを加えようとしたらどうなるだろう?
各キャラクターすべてのデータを修正するためにある。

もし、それらのステータスをまとめて設定できて、そのまとめたものを呼び出すということをすれば?
そう、そのまとめたもの一箇所を変えるだけで全てを一気に修正できる。

オブジェクト指向はとても便利なのだ。
プログラミングの本などではクラスというのは「役割分担」「設計書」と例えられることもある。
今回の例の場合、その各キャラクターのステータスの設計書を作るということだ。

ソースコード

public class PlayerStatus {
    public String playerName;
    public int level;
    public int hp;
    public int attack;
    public int defense;
    public List<String> items;

    public PlayerStatus(String playerName, int level, int hp, int attack, int defense) {
        this.playerName = playerName;
        this.level = level;
        this.hp = hp;
        this.attack = attack;
        this.defense = defense;
        this.items = new List<String>();
    }

    public String getPlayerName() {
        return playerName;
    }

    public void outputPlayerStatus() {
        Console.WriteLine("プレイヤー名: "+ playerName);
        Console.WriteLine("Lv: "+ level);
        Console.WriteLine("HP: "+ hp);
        Console.WriteLine("攻撃力: "+ attack);
        Console.WriteLine("防御力: "+ defense);
        foreach(String item in items) {
            Console.WriteLine("所持アイテム: "+ item);
        }
    }
}
PlayerStatus playerStatus = new PlayerStatus("あなた", 1, 100, 100, 0);
Console.WriteLine("PlayerName: " + playerStatus.getPlayerName());
playerStatus.outputPlayerStatus();

実行結果

PlayerName: あなた
プレイヤー名: あなた
HP: 100
攻撃力: 100
防御力: 0

詳細

まず冒頭でクラスは「設計書」と言った。
そのクラスの書き方は以下の通りだ。

/* クラスのアクセス修飾子 */ class /* クラス名 */ {
    // メンバ定義
    /* メンバのアクセス修飾子 */ /* 型名 */ /* 変数名 */;
    /* メンバのアクセス修飾子 */ /* 型名 */ /* 変数名 */;

    // コンストラクタ
    /* コンストラクタのアクセス修飾子 */ /* クラス名 */(/* 引数定義 */) {
        /* インスタンス生成時に行われる処理 */
    }

    // メソッド定義
    /* メソッドのアクセス修飾子 */ /* 戻り値の型名 */ /*メソッド名*/(/* 引数定義 */) {
        /* メソッドの処理内容 */
    }
}

今回の例だと以下のようになっている。
ひとつひとつ説明していこう。

クラス

項目内容
クラスのアクセス修飾子public
クラス名PlayerStatus
メンバpublic String playerName;
public int level;
public int hp;
public int attack;
public int defense;
public List<String> items;

アクセス修飾子

こちらに関しては別ページで説明する。
勉強したての段階では「publicにしておけば必ず使える」程度に思ってもらっていい。(仕事にするとなると、ここはきちんとわかっておく必要があるが…)

クラス名

クラス名は基本的にファイル名と同じ名前にしておく。
また、命名規則がある。その規則は以下の二つ

  • 何を表すクラスかわかること
  • 英単語の各単語の頭文字を大文字にして繋げる

例えば今回の場合、プレイヤーのステータスをまとめているクラスなのでクラス名は「Player」「Status」を含んだ名前がいいだろう。
そして、それらの単語を並べて各単語の頭文字を大文字にして命名する。
このため、クラス名が「PlayerStatus」になっている。

メンバ

これはクラス内で定義した変数のこと。
後に説明するが、「クラスのインスタンス生成を行った時に使える変数」と今は思ってもらえればいいと思う。

コンストラクタ

項目内容
コンストラクタのアクセス修飾子public
クラス名PlayerStatus
引数定義String playerName,
int level,
int hp,
int attack,
int defense
インスタンス生成時に行われる処理this.playerName = playerName;
this.level = level;
this.hp = hp;
this.attack = attack;
this.defense = defense;
this.items = new List<String>();

コンストラクタのアクセス修飾子とクラス名はクラス名の通り。

そもそもコンストラクタとは?

先ほど、「クラスのインスタンス生成を行った時に使える変数」と説明した。
クラスというのは所謂「設計書」。家の図面だけがあっても住めないように、クラスも設計書だけでは使うことはできない。
家の図面を元に建築して初めて家に住めるようになるのと同じように、クラスも設計書を元に実体化して初めて使えるようになる。

コンストラクタは、この実体化した時に行われる処理を設定することができる。
その実体化した時に行われる処理が例で挙げた「インスタンス生成時に行われる処理」のこと。
実体する方法は後述。

引数定義

コンストラクタには引数を設定できる。通常は引数がないのだが、独自で引数を設定できる。
引数はコンストラクタやメソッドの処理を行う時に、「この値を使って処理をして欲しい」という時に設定する値だ。

インスタンス生成時に行われる処理

先ほどでも説明したが、実体化した時にこの処理が行われる。
そして、引数定義が設定されていれば、この設定された引数に応じて処理が行われる。
今回のように、プレイヤーのHPは実体化する時に設定したいし、プレイヤーと言ってもパーティーメンバーの場合もあるし、オンラインなら自分ではないだれかのステータスかもしれない。
こんな時、数字が違うからという理由でいちいちいくつもプログラムを書くわけにはいかない。そんな事態を防ぐために引数定義がある。

今回の例では、この処理はメンバ変数に引数の値を代入したり、メンバ変数の初期化をおこなっていたりする。

メソッド

項目内容
メソッドのアクセス修飾子public
戻り値の型void
引数定義なし
メソッドの処理内容Console.WriteLine(“プレイヤー名: “+ playerName);
Console.WriteLine(“Lv: “+ level);
Console.WriteLine(“HP: “+ hp);
Console.WriteLine(“攻撃力: “+ attack);
Console.WriteLine(“防御力: “+ defense);
foreach(String item in items) {
Console.WriteLine(“所持アイテム: “+ item);
}

メソッドとは

メソッドはひとまとまりの処理。
ドラクエを思い出して欲しい。以下のようなメッセージが表示されるはずだ。

テリーの攻撃
スライムに10ダメージを与えた
ハッサンの攻撃
スライムに15ダメージを与えた

これをよく見てみると、以下のような法則性がわかるはずだ。

  • 〇〇の攻撃
  • □□にXXダメージを与えた

キャラクター、ダメージが変わるたびにいちいちこの処理を書くのは面倒だろう。
そこで、先ほどの引数を使用して、これらのメッセージをひとまとまりの処理としてしまえばどうだろう?

攻撃するキャラクターの名前、ダメージを受けたキャラクターの名前、ダメージの値を設定してこのひとまとまりの処理を呼び出せばどうだろう。
戦闘に関するメッセージ処理のほとんどが賄えてしまう。

このひとまとまりの処理が「メソッド」だ。

メソッドの処理内容

こここそがひとまとまりの処理の実体。今回の例では、ひたすら画面に文字列を表示する処理を行っている。

戻り値の型

メソッドを呼び出すと、戻り値を設定できる。
これは、メソッドを呼び出す際、呼び出し元で変数のように扱うことができる。
それが以下の例だ。

Console.WriteLine("PlayerName: " + playerStatus.getPlayerName());

まるで、「playerStatus.getPlayerName()」が変数のように扱われているのがわかるだろう。
その時、呼び出し元で変数として扱う際、その時の変数の型を「戻り値の型」という。

変数として扱うことは分かったと思うが、では具体的にどんな値が設定されているのだろう。
その設定されている箇所がここだ。

return playerName;

この「retrun」の後ろにある値を「戻り値」と言い、先ほどの設定されている値が、この戻り値になる。

だから、出力結果でインスタンス化した時に設定していた「”あなた”」が出力されている。

クラスを元に実体化する

クラスを実体化したものを一般に「オブジェクト」と言う。
実体化する際は以下のように書く。

/* クラス名 */ /* オブジェクト名 */ = new /* クラス名 */(/* コンストラクタの引数 */);

今回は以下のようになっている。

PlayerStatus playerStatus = new PlayerStatus("あなた", 1, 100, 100, 0);
項目内容
クラス名PlayerStatus
オブジェクト名playerStatus
コンストラクタの引数“あなた”
1
100
100
0

こうすることで、先ほど説明したメソッドや変数を呼び出すことができる。
呼び出し方は以下のように呼ぶ。

/* オブジェクト名 */./* メンバ変数名 or メソッド名 */;

今回は以下のように呼び出している。

playerStatus.hp  // メンバを呼び出す場合
playerStatus.outputPlayerStatus() // メソッドを呼び出す場合

「.」を「の中の」と訳せばわかりやすい思う。

playerStatusの中のhpのデータを見せて
playerStatusの中のoutputPlayerStatusというメソッドを呼び出して

という感じだ。

練習問題

敵キャラのステータスを表すクラスを作ってみよう。

そして、敵キャラのステータスを表示するメソッドを作ってみよう。

参考ページ

  • 「」
  • 「」