this
基本@で定義方法は最低限説明し終わりました。しばらくの間は、メソッドとインスタンス変数の定義、インスタンスの生成とコンストラクタの定義で済むと思います。ただ、今までの説明でなんとなく感じていただいたかと思いますが、クラス内に定義されたメソッドやインスタンス変数はインスタンスを生成しないと使用することができません。ですが、前回の「move〜」メソッド内で「x -= 1;」のようにインスタンス変数を使用していました。このように、同じクラス内のメンバを呼び出すことができます。そのときに必要なのが、「this」という特別なオブジェクトです。
thisの例class sentouki { private int x; private int y; public sentouki(){ this.x = 100; this.y = 100; } public void moveleft() { this.x -= 1; } public void moveright() { this.x += 1; } public void moveleft3() { this.moveleft(); this.moveleft(); this.moveleft(); } |
このように、「this.」の後に同じクラスのメンバを指定することで、同じインスタンスのメンバにアクセスすることができます。もちろん、インスタンス変数以外にメソッドを呼び出すことも可能です。thisの感覚としては、生成されたインスタンス自体が自分自身を指し示しているような感じですね。
さて、これでは最初の問題の解決になっていませんね。なぜ、「x -= 1;」のような記述が可能かというと、実は「this.」は省略可能なのです。つまり、同じメンバならそのメンバの名前を直接書けばアクセスできます。明示的に自分のメンバにアクセスしたい場合はthisをつけると分かりやすいと思います。
アクセス修飾子
さてさて、結構重要な「アクセス修飾子」を完全に無視して話を進めてきてしまいましたが、ここでしっかりと説明しておきましょう。
基本@の説明で、クラスからインスタンスを生成して、その機能を使いながらプログラムを組んでいくと言うことは何となく感じてもらえたと思います。ですが、オブジェクト指向の中の重要な特徴として、「カプセル化」と言うものがありました。クラスが増えてきて、なおかつ大きくなると、全てのメンバにアクセスできてしまうことは不便になります。スイッチの多い機械が扱いにくいのと同じことですね。そこで、特に外部からアクセスする必要の無いメンバを制限するのがアクセス修飾子です。実はクラス自体にもアクセス修飾子をつけることができるのですが、ひたすらにややこしくなるのでここではインスタンス変数用のアクセス修飾子とメソッド用のアクセス修飾子の一部を紹介したいと思います。
インスタンス変数用、アクセス修飾子
インスタンス変数用のアクセス修飾子は4つ(5つ?)ほどあるのですが、大きく特徴の異なる2つを説明しようと思います。
修飾子 | 機能 |
---|---|
public | 外部からのアクセスを許可 |
private もしくは省略 | 同じクラスのメンバからのアクセスのみ許可 |
英単語から、その機能は何となく推測はつくと思います。@では全て「public」なメンバとして定義していました。そのため、生成したインスタンスで「jet.x」などのようにメンバに直接アクセスできたわけですが…
前回のsentoukiクラスにおいて、その他の「move〜」メソッドを使えば「x,y」の値を操作できるため、jetを移動させる目的だけならば、わざわざ外部からのアクセスを許可する必要は無いですね。もし、コードのどこかで誤ってxの値を変更していたらバグが発生してしまうかもしれません。そういった場合に、アクセス修飾子に「private」を指定します。
privateアクセス修飾子の例class sentouki { private int x; private int y; public sentouki(){ x = 100; y = 100; } public void moveleft() { x -= 1; } public void moveright() { x += 1; } public void moveup() { y -= 1; } public void movedown() { y += 1; } } |
上の例のようにすると、生成したインスタンス外部から「jet.x」などのようにアクセスすることができなくなります。ただ、同じクラスのメンバからは参照することができます。上の例で言うと、「moveup()」メソッドは変数「y」にアクセスして書き換えていますね。例のクラスは規模が小さいのであまり有難みを感じないとは思いますが、規模が大きくなると、外部に公開する必要のないメンバが多くなります。そういった場合には、アクセス修飾子を使ってできるだけミスを防ぐようにできます。
ちなみに、このように必要のない部分(もしくは、触れられては困る部分)を外からアクセスできないように「ブラックボックス化」していくと、後々使いやすいクラスを作れるそうです。これが@のところで説明した「カプセル化」の技術のひとつです。オブジェクト指向的には、しっかり設計して、厳密に定義し、インスタンス変数は「public」以外のアクセス修飾子を指定するのが好ましいそうなのですが、面倒なので最初のうちは全て「public」でもいい気がします。コードが多少汚くても、先ず動くプログラムを書くことが先決ですから。
メソッド用、アクセス修飾子
上の説明で、アクセス修飾子の意義と大体の機能は分かってもらえたと思います。これと同じことがメソッドにも適応できます。ただ、メソッドの場合はアクセス許可、拒否だけではなく、上書き専用(継承が出てきたときに詳しくやりましょう)やら色々と複雑なものもあり、全部で10種類を超えてしまいます。ただ、複雑なもののほとんどが「継承」の際に重要となってくるものなので、今回はインスタンス変数のときと同じ二つだけを紹介しておきます。
修飾子 | 機能 |
---|---|
public | 外部からのアクセスを許可 |
private もしくは省略 | 同じクラスのメンバからのアクセスのみ許可 |
まぁ、説明は同じです。外部からアクセスする必要の無いメソッドは「private」を指定してください。
静的という奴
もうこれで、最低限のクラスの定義はできます。が、ひとつ気になることはありませんか?Cを思い出してみてください。普通サンプルってひとつの動くプログラムが書かれているはずです。ですが、この講座のサンプルはそうしていません(一般のC#解説書では、大体ひとつのプログラムがサンプルになっていますが)。それはなぜかと言うと、今の知識ではひとつのプログラムが書けないからです。考えてみてください、C#のプログラムは一体どこから始まるのかを。C言語では「main」関数から始まりますが、C#に関数はありません。そもそも、インスタンスを生成しないとメソッドが呼び出せないのに、そのインスタンスを生成するメソッドは何なのか?
答えは、今から説明する「静的」メソッドという奴です。クラス内には、インスタンスを生成しないと呼び出せないメソッドのほかに、インスタンスを生成せずに呼び出すことが出来る「静的メソッド」というものが存在します。これとインスタンスの違いを説明していると、また長くなってしまいますので、ここではアプリケーションのエントリーポイント(最初に始まるところ)の定義法を紹介してさらっと流します。
エントリーポイントclass sentouki { public static void Main() { //エントリーポイント } private int x; private int y; public sentouki(){ x = 100; y = 100; } //〜〜省略〜〜 } |
このように、アクセス修飾子の後に「static」とつけると「静的メソッド」になります。一応「sentouki」クラスの中で定義されていますが、ことMainメソッドに関してはあまり意味はありません。このプログラムをコンパイルして実行するとMainから処理が開始されます。
Mainメソッド以外にも静的メソッドは定義できますが、普通のインスタンスを生成しないと使えないメソッドとは全くの別物だと思っておいてもしばらくは差し支えはないと思います。とりあえず、「インスタンス生成しなくても呼び出せるメソッドがあるんだ〜」程度でいいと思います。
ちなみに、静的メソッドのほかに、静的変数というものもあります。クラス内で「static int a;」などと宣言するとインスタンスを生成しなくても使える変数が宣言できます。こういった変数は静的メソッドから呼び出す際に使います。静的〜はいまいち使い道が思いつきませんが、そういうものもあるのです。Cの「static」キーワードとは随分違うので多少注意が必要かもしれませんね。
オーバーロード
今までの説明で、とりあえず動くプログラムは組めます。というか、自分で全部作るのであれば、これだけの知識でほぼ全てのプログラムが何とかなるでしょう。と、言うことで、クラス(基本A)の最後にちょっと知っていると便利な「オーバーロード」という機能の説明をします。
先ほどの「アクセス修飾子」のところで「カプセル化」の機能の一部を紹介しました。そこで、今度は3大特徴のひとつ「ポリモーフィズム」の機能の一部を紹介します。C言語では、同じ関数名は2度と使うことはできませんでした。ですが、C#は何と同じ名前のメソッドが同じクラス内に複数定義することができます。
オーバーロードの例class sentouki { private int x; private int y; public sentouki(){ x = 100; y = 100; } public void moveleft() { x -= 1; } public void moveright() { x += 1; } public void moveleft(int m) { x -= m; } public void moveright(int m) { x += m; } //〜〜省略〜〜 } |
このように、同じ名前のメソッド(コンストラクタも可能)を定義することを「オーバーロード」と言います。ただ、何でもかんでもオーバーロードできるわけではありません。上の例では赤い文字の方の「moveleft」メソッドの引数リストが「(int m)」となっています。このほかにも、戻り値の型やアクセス修飾子が違えばオーバーロードすることができます。一般的には、これらの違いを「シグニチャ」が違うなんて言い方をするそうです。
上の例は、「jet.moveleft()」と呼び出すと左へ1移動し、「jet.moveleft(3)」と呼び出すと左へ3移動します。移動に使うキーワードは「moveleft」と同じものを使えばいいので扱いやすいクラスが定義できます。オーバーロードの使い道としてはこんな感じのものが多いと思います。
とりあえずひと段落。
クラスの説明はいつまでたっても終わらないので、ここら辺で区切りをつけます。クラス基本@Aの知識は、VC#で最初に作られているフォームコードを読むために最低限必要な知識です。この知識を使って、次回はその最初のフォームコードを解析し、VC#のWindowsアプリケーションがどのように動作しているのかを追っていこうと思います。実は、結構大変なことをいきなりやっているのですが、それを知っておかないとゲームプログラムに必須の高速なループ構造が作れませんので、なんとなくでいいので仕組みを見てください。