今回の記事ではクラスについて解説していきます。
クラスについては複数回に分けて解説します。今回はクラスとインスタンスの基本的な使い方について解説します。
ちなみに構造体(struct)も大体クラスと同じものになり、ここで解説する用法は構造体でも使えます。
前回の記事:
クラスとは
クラスは変数やメソッドなど関連性のある機能をまとめたものになります。
C#はクラスを組み合わせてプログラムを作成していくオブジェクト指向型プログラミング言語になります。
クラスの中で宣言されたものをメンバと呼びます。変数ならメンバ変数、メソッドならメンバメソッドと呼ばれたりします。クラスの中で宣言された変数はフィールド(Field)と呼ばれます。
次のものはクラスの定義の書き方になります。メンバ宣言にはpublic
などアクセス修飾子をつけることができますがこれについての詳細はこれ以降の記事で解説します。
class <クラス名> { <メンバ宣言>... }
1 2 3 4 5 6 7 |
//Sampleという名前を持つクラスを定義する。 class Sample { public int Value = 0; public void Method() { // ... } } |
クラスとインスタンスの違い
クラスを定義しただけではあまり意味はありません。クラスを利用する際にはインスタンスを作成する必要があります。
それぞれの役割を例えると、クラスは設計図になり、インスタンスは設計図をもとに作成された生成物になります。
インスタンスを作成するときはnew演算子を使用します。書き方は以下のように先にnew
を書いてからクラス名を続けます。クラス名の後にはコンストラクタ(※)への引数を書きます。
(※コンストラクタについてはこの後に解説します。)
new <クラス名>(<引数>...)
一度作成したインスタンスにはクラス定義にあるメンバが存在しています。それらメンバを使用したい、アクセスしたいときは.
演算子を使用してください。
<インスタンス> . <メンバ>
クラスは参照型になります。インスタンスがオブジェクトの属するメンバであるのに対し、クラスのメンバ変数はクラスに属しています。
インスタンスメンバはインスタンスそれぞれが独立した実体(値)を持ちますが、クラスメンバ変数はクラスで一つしか存在できません。全てのインスタンスでそのメンバ変数が共有されます。
1 2 3 4 5 6 7 8 9 |
class A { public int Value = 0; } //インスタンスを作成するときはnew演算子を使用する。 A instance = new A(); //メンバにアクセスするときは'.'演算子を使用する。 instance.Value += 10; |
コンストラクタについて
インスタンスを作成する時には必ずコンストラクタが呼び出されます。コンストラクタはインスタンスの初期化を行う特殊なメソッドになります。
書き方はメソッドと似ており、戻り値を省略したものになります。
<コンストラクタ名>(<引数>...) { <処理> ... }
また、コンストラクタはクラスの中に複数定義できます。その際は引数が異なるようにコンストラクタを定義してください。
さらにコンストラクタを実行する前に他のコンストラクタを一つだけ実行することもできます。そのためコンストラクタで行う処理を共通化することが可能になります。
他のコンストラクタを呼び出したいときは以下のように引数と鉤括弧の間に: this(<引数>)
と書いてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class A { public int Value = 0; //引数なしのコンストラクタ // 一つのintをとるコンストラクタを呼び出している。 public A() : this(10) { } //引数付きのコンストラクタ // 引数についてはメソッドと同じになります。 public A(int a) { Value = a; } } var inst = new A(); // <- OK var inst2 = new A(10); // <- OK |
上記コードのpublic A() : this(10) は同じクラス内で別のコンストラクタを実行し、引数として10を与えよという意味になります。
このコードの場合、引数なしでクラスAを呼び出した場合、引数10を与えた初期化コードとして処理されるわけですね。
コンストラクタの注意点
もし、コンストラクタを定義しなかった場合はC#側で自動的に引数なしのコンストラクタが定義されます。
またそれに合わせて注意点として、コンストラクタを一つ以上定義した場合には引数なしのコンストラクタは自動生成されません。そのためインスタンスを作成するときは必ず定義したコンストラクタのいずれかの引数と一致するように値を渡す必要があります。
1 2 3 4 5 6 7 8 9 10 11 |
class A { //コンストラクタを定義しなければ、C#によって引数なしのコンストラクタが自動的に定義される。 } var a = new A(); // <- OK class B { //一つでもコンストラクタを定義すると、引数なしのコンストラクタは自動生成されない。 public B(int a) { ... } } var b = new B(); // <- NG var b2 = new B(10); // <- OK |
静的なコンストラクタ
コンストラクタにはstaticキーワードをつけた静的コンストラクタというものが存在します。
静的コンストラクタはこれを定義したクラスが初めて使われるタイミングで自動的に実行されます。主な用途として静的メンバ(※)の初期化などに使われます。
使う場合は
- static修飾子を付けること
- 他の修飾子は付けられない
- 引数も指定できない(のちの記事で出てくるオーバーロードもできない)
という特徴を持ちます。
(※静的メンバはプログラムのどこからでもアクセスできるメンバのことです。詳しくは他の記事で解説します。)
1 2 3 4 5 |
class A { static A() { // 処理 } } |
privateなコンストラクタ
privateなコンストラクタとはコンストラクタにprivateというアクセス修飾子(※)をつけたものになります。
このコンストラクタは同じクラスの中からしか呼び出すことができないと指定したコンストラクタになります。そのため、他のクラスの中からインスタンスを作成しようとしてもコンパイルエラーになります。
一見意味のない機能に見えますが以下の用途があります。
- 静的メンバのみを持つクラスのインスタンスを生成させないようにする
(※アクセス修飾子についてはこれ以降の記事で解説します。)
1 2 3 4 5 6 7 |
class A { private A() { // 処理 } } var a = new A(); // <- NG |
【実践】Unityでクラスを実際に定義してみよう!
それでは実際にUnityでクラスを定義してみましょう!
これまで何回も書いてきましたがコンポーネントもクラスになるので既にクラス定義については経験済みです。
ただコンポーネントではコンストラクタは必要ではないので、今回のサンプルではコンストラクタ付きクラスをコンポーネントの中に定義して使ってみましょう!
ちなみにクラスの中に定義されたクラスを子クラスなどと呼んだりします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using UnityEngine; public class Sample : MonoBehaviour { public class Data { public float Speed = 0f; public Data(float speed) { Speed = speed; } public Vector3 GetMove() { var move = Vector3.zero; move.x = Speed * Time.deltaTime; return move; } } public float Speed = 0; public Data data; void Start() { data = new Data(Speed); } void Update() { transform.position += data.GetMove(); } } |
上のサンプルコードではSampleコンポーネントの中でDataというクラスを定義しています。Start()メソッドの中でDataクラスのインスタンスを作成し、Update()メソッドの中で作成したインスタンスからGetMove()メソッドを呼び出しGameObjectを移動させています。
Start()メソッド内でDataクラスのコンストラクタの引数にInspectorから設定した値を渡しています。そのため、再生後にSpeedの値を変更してもGameObjectの移動速度は変わりません。
上のサンプルコードではコンストラクタを使用したかったため、少々回りくどい手順を踏んでいますが、実はクラスのメンバもInspector上から設定することができます。
クラスのメンバをInspectorから設定できるようにする方法
上のサンプルコードで定義したクラスのメンバをInspector上から設定するときは以下のように書いてください。
- クラス定義の際に
System.SerializableAttribute
を指定すること。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using UnityEngine; public class Sample : MonoBehaviour { [System.Serializable] public class Data { public float Speed = 0f; public Data(float speed) { Speed = speed; } public Vector3 GetMove() { var move = Vector3.zero; move.x = Speed * Time.deltaTime; return move; } } public Data data; void Update() { transform.position += data.GetMove(); } } |
System.SerializableAttribute
を指定するとそのクラスがコンポーネントのメンバにある場合Unityエディタが自動的にInspectorから設定してくれます。
System.SerializableAttribute
を指定しておけば、そのクラスに関してはクラス型のメンバが入れ子で定義されていてもその全てInspector上から設定することができます(※)。
(※クラス定義によっては無限に入れ子を作ることができますが、入れ子の上限は存在しています。)
また、コンストラクタ自体もUnity側から呼び出されるため、わざわざStart()メソッドの中でインスタンス生成する必要はなくなりますので、削除しています。
また、Inspectorから直接Dataクラスのメンバを変更しているのでこちらは再生後にSpeedの値を変更するとGameObjectの移動速度に反映されます。
まとめ
今回の記事をまとめると以下になります。
- クラスは関連する変数、メソッドなどをまとめたもの。
- コンポーネントもクラスである。
- インスタンスはクラス定義から作成されたもの。
new
演算子を使ってインスタンスを作成する。- インスタンス作成の時にはコンストラクタという特殊なメソッドが自動的に呼び出される。
- コンストラクタは他のコンストラクタの処理を一つだけ先に実行することができる。
- 何もコンストラクタを定義しなかった場合は引数なしのコンストラクタがC#によって作成される。
- コンストラクタを一つ以上定義した場合はインスタンス生成時に必ず定義したコンストラクタと一致した引数を渡さないといけない。
- 静的コンストラクタはアプリ実行中一度だけ自動的に呼び出されるコンストラクタ。
- privateなコンストラクタはそのクラスのインスタンスを生成させないためのコンストラクタ。
System.SerializableAttribute
をクラスに指定するとInspector上からそのクラスのメンバの値を設定することができる。
それでは次の記事に行ってみましょう!
次回の記事:
コメント