キャッシングValue

C#のちょっと高い山

 C#最初の山、「クラス」の説明に入りましょう。と言っても、いきなりクラスの話をしても何のためにこんなものがあるのかが分からないと思います。そこで、ここから「オブジェクト指向」プログラミングという概念も投入します。まず、オブジェクト指向プログラミングの概念を簡単に説明します。そのあと、クラスの定義の仕方とインスタンスなどの言葉の説明をしていきたいと思います。

オブジェクト指向プログラミングとは?

 環境構築の章でもチラッと言いましたが、C時代の「構造化プログラミング」全盛期にぶち当たった大規模プログラム開発の壁を越えるため、より発展的なプログラミング方式が注目を集めました。それがオブジェクト指向プログラミングです。

 オブジェクト指向プログラミングとは、できるだけ「グローバル変数」を排除し、小さな部品をいっぱい作ってそれを連携させながらプログラムを完成させていく方式です。その小さな部品を他のプログラムでも使いまわすことができるようにすれば、新しくプログラムを作る場合でも過去の部品を使って素早く組み上げることができるのです。

 以上のような発想から、オブジェクト指向プログラミングでは3つの特徴があります。

カプセル化

 オブジェクト指向では、細かなプログラムの部品をたくさん作ります。構造化プログラムでぶち当たった壁のひとつが「大規模なものを作るのが難しい」という点です。つまり、小さなプログラムであれば簡単に作れるのです。そこで小さな部品を作って、その中でプログラムを完結し、連結する先の部品から悪影響を受けないように組むことができればよいのです。それがカプセル化です。オブジェクト指向ではその小さなプログラムの部品を「オブジェクト(物)」と呼び、その中をブラックボックス化することで大規模プログラムへの対応や流用性を高めているのです。

ポリモーフィズム

 ポリモーフィズムとはギリシャ語で「多くの形」みたいな意味の言葉だそうです。いきなりですが、CDを再生するときもMDを再生するときも「再生」ボタンを押せば再生されますよね。でも、中身の機械では全く違う動きをしているはずです。オブジェクト指向のプログラミングでも、midiファイルの再生とmp3ファイルの再生が同じ方法でできたら楽チンですよね。そのように、同じインターフェイスで色々な動作をさせるようプログラムしていきます。

継承

 プログラムを書いていると、同じようでちょっと違うプログラムを何度も書くことがあります。同じようなところは前のを使いたいと思いませんか?オブジェクト指向では他のオブジェクトの性質を継承することで同じようなオブジェクトを作ることができます。この継承がオブジェクト指向プログラミングの流用性をさらに高めています。

 オブジェクト指向プログラミングは確かに素晴らしい発想です。ですが、C#を使えば勝手にこういうプログラムが組めるわけではありません。あくまでも、「以上の3点のようなことを意識して組むと、後々いいことがありますよ」というだけです。はっきり言って、こんなことをいちいち意識して組んでいたらいつまでたっても組みあがらないでしょうし、非常に面倒です。C#だからって、オブジェクト指向を完全導入する必要も無いと思います。(僕は、あまり導入していません)ここでは、オブジェクト指向という素晴らしい発想があるんだな〜くらいにとどめておいても構わないと思います。ただ、フォームにペタペタコントロールを貼り付けてプログラムを作る方式は、Microsoftの人たちが作ってくれた小さなプログラムの部品を組み合わせて作っているのでオブジェクト指向プログラミングですね。オブジェクト指向について深く語った本は何冊も出ていますので、詳しく知りたい方はそういった専門書を買って読んでみるのもいいかと思います。(あとで、参考文献リストみたいなものでも作ろうかな?)

クラスとは?

 概念導入はこれくらいにしておいて、プログラムの話に入ります。先ほども説明したとおり、オブジェクト指向プログラムは小さな部品(オブジェクト)を組み合わせてプログラムをくみ上げていく方式です。そこで、オブジェクト指向プログラム言語にはその部品を効率よく作る仕組みが備わっています。それが「クラス」です。「クラス」はプログラムの部品の設計図です。C言語には構造体と言うものがありましたが、イメージとしてはあれをさらに拡張して、構造体のメンバに変数だけでなく関数も含め、クラス内でひとつのプログラムを完結できるようにしたものといったところです。(実際には、比べ物にならないほど高機能ですが)クラス烽ノ含まれる変数を「インスタンス変数」、関数を特に「メソッド」となどと呼んだりします。C++にはCの名残から関数と言うものが存在していましたが、C#にはそれはありません。変数も関数も全て「クラス」内で定義しないといけません。ですから、C#で組む以上クラスは避けて通れないものなのです。

クラスの定義

 では早速クラスの定義方法を紹介します。クラス内には普通、変数とメソッドを定義します。

クラスの定義方法
	class クラス名 {
		//インスタンス変数の宣言
		
		アクセス修飾子 データ型 変数名1;
		アクセス修飾子 データ型 変数名2;
		アクセス修飾子 データ型 変数名3;
		………
	
		//メソッドの宣言と定義
		アクセス修飾子 戻り値の型 メソッド名1 (引数リスト) {
			//メソッド内の処理
		}
		アクセス修飾子 戻り値の型 メソッド名2 (引数リスト) {
			//メソッド内の処理
		}
		アクセス修飾子 戻り値の型 メソッド名3 (引数リスト) {
			//メソッド内の処理
		}
		………
	}

 このように、クラス内には「インスタンス変数」と「メソッド」を定義していきます。インスタンス変数とは宣言したクラス内でのみ有効な変数で(って、大嘘ですが…)、同じクラスの全メソッドから読み書きが可能な変数です。メソッドの処理が終了してもインスタンスが破棄されない限り値は保持されます。これと対比すると、メソッド内で「int a;」などと宣言した変数はローカル変数などと呼ばれ、宣言したメソッド内でのみ有効でそのメソッドの処理が終了すると値も同時に破棄されます。メソッドとは…Cでいうところの関数です。使い方もCの関数とほぼ同じです。値を返すこともできますし、引数をとることもできます。このように、クラス内でひとつのプログラムを形成することができます。アクセス修飾子については後で詳しく説明するとして、しばらくの間はアクセス修飾子に「public」を指定してインスタンス変数の宣言やメソッドの定義の仕方をもう少し詳しく説明しようと思います。

クラス定義の例

 それでは、実際にクラスの例を紹介します。ゲームで使えそうな「sentouki」(戦闘機)クラスを作っていくことにします。

クラスの例
	class sentouki {
		public int x;
		public int y;
		
		public void moveleft()
		{
			x -= 1;
		}
		public void moveright()
		{
			x += 1;
		}
		public void moveup()
		{
			y -= 1;
		}
		public void movedown()
		{
			y += 1;
		}
		
	}

 「class」の部分を「struct」と書き換えてしまえばCの構造体とほとんど変わりないですね。構造体が新しい型を定義していたように、クラスも新しい参照型を定義していることに相当します。また、このクラスを外部から見た場合、構造体と同じように中に書かれた変数やメソッド(関数)のことをメンバなどと呼んだりもします。

 このクラスはシューティングゲーム内の戦闘機のプログラム(クラス)です。変数「x」はこの戦闘機のX座標、変数「y」はY座標と考えます。そして、メソッド「moveleft」「moveright」「moveup」「movedown」は戦闘機の座標を変更し、それぞれ左右上下に移動させます。このようにクラスは関連したデータと動作をセットにしてプログラムの部品の設計図を作っていきます。ちなみに、このクラスは非常に不味い部分を含んでいます…詳しくは、コンストラクタのところで…

インスタンスの生成と使用

 何度も言いますが、クラスは参照型の定義です。あくまで、プログラムの設計図なのです。設計図から部品を作らなければ何の意味もないように、定義した型の変数を宣言しなければ、せっかのクラスは意味を成しません。そこで、先ほど定義した「sentouki」クラスを実際に宣言してみましょう。

インスタンスの生成
	sentouki jet = new sentouki();

 このようにして、先ほど定義した「sentouki」クラスのインスタンスを生成します。参照型変数の宣言法は細かな文法事項を見てください。ここでインスタンスの説明を少し詳しくしておきましょう。

 インスタンスとは、プログラムの部品のことで「オブジェクト」などとも呼びます。参照型のところで説明しましたが、参照型変数はあくまでも参照先の情報しか格納されていません。その参照先こそがインスタンスなのです。「new クラス名()」でメモリ上にインスタンスを生成し、宣言した参照変数にそのアドレスを代入しています。この「= new sentouki()」をやらないと「オブジェクト参照にインスタンスが設定されていません」とかいうエラーが出てきてしまいます。クラスから作られるのはインスタンスです。「sentouki jet」の部分は、後に生成されるインスタンスの参照先を格納するためだけの変数であることを忘れないで下さい。

 さて、インスタンスが生成できたところで、実際に作ったインスタンスを使ってみましょう。

インスタンスの使用
	sentouki jet = new sentouki();
	
	int x,y;
	jet.x = 100;
	jet.y = 100;
	x = jet.x;
	y = jet.y;
	
	jet.moveleft();
	jet.moveright();
	jet.moveup();
	jet.movedown();

 生成したインスタンスは変数「jet」に設定されているので、このインスタンスを操作するには変数「jet」を使えばいいことになります。ちなみに、この変数「jet」のことをオブジェクトと呼ぶことがあります。インスタンスの使い方は、生成もとのクラスで定義されたメンバに対してアクセスして使います。上の例にあるように、メンバ変数にアクセスするには「オブジェクト名.メンバ変数」と「.」(ドット演算子)を使うことで変数にアクセスすることができます。同じように、メソッドを呼び出すのにも「オブジェクト名.メソッド名(引数リスト);」と「.」を使うことで可能です。それと、インスタンス変数で使った変数名を他の場所で使うことができます。例のように、xという変数名はこのメソッド内でも使われていますし、sentoukiクラスのインスタンス変数の名前としても使われています。この名前は他のクラスを作った場合にも使用できます。

インスタンスの使用A
	sentouki jet1 = new sentouki();
	sentouki jet2 = new sentouki();
	
	jet1.x = 100;
	jet2.x = 100;
	
	jet1.moveleft();
	jet2.moveright();

 インスタンスについてもうひとつ重要なことは、構造体のときと同じようにそれぞれが独自のデータ領域を持つと言うことです。上のコードを実行後の「jet1」のXの値と「jet2」のXの値は異なります。jet1のxは「99」、jet2のxは「101」になっているはずです。感覚的には構造体と同じなので説明はこのくらいにしておきます。

コンストラクタ

 先ほど「非常に不味い部分を含んでいる」と言った「sentouki」クラスですが、その理由は「インスタンス変数が初期化されていない」からです。C#もC同様、変数の値はちゃんとはじめに初期化してやらないといけません。(ちなみに、C#はコンパイル時にエラーが出ます)インスタンス変数以外にも、宣言したインスタンスを生成したりなど、生成時にやりたいことは色々とあると思います。そのインスタンスの初期化を行うのがコンストラクタです。

コンストラクタをつけたsentoukiクラス
	class sentouki {
		public int x;
		public 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;
		}
		
	}

 えっと、上のコードで赤色の部分がコンストラクタです。インスタンスが生成されたとき「{}」内の処理が行われています。インスタンス生成時の「new sentouki()」の部分でコンストラクタを関数みたいに呼び出していると考えてもらえればいいと思います。ちなみに、コンストラクタは一般的に…
コンストラクタの定義
		クラス名(引数リスト) {
			処理;
		}

 といった感じです。コンストラクタも引数をとることができます。ただし、値を返すことはできません。引数を持ったコンストラクタを呼び出す場合は、「new sentouki(引数)」のようにしてインスタンスを生成します。例えば…

引数を持ったコンストラクタ
	class sentouki {
		public int x;
		public int y;
		
		public sentouki(int ix, int iy){
			x = ix;
			y = iy;
		}
		
		//省略		
	}
	
	//別メソッド内
	
	sentouki = new sentouki(100, 100);

 みたいな感じです。こうすれば、初期値を生成したインスタンスごとに変えることも出来ます。

…長かった…

 今回は、クラスの定義方法とインスタンス変数、メソッド、そしてインスタンス生成の説明をしました。今回紹介したのは、クラス全体の機能の1割か2割程度だと思います。(ちなみ、管理者が理解しているのが5割〜6割程度ですが。)専門書などでは、C#の開設の大半をクラスの説明に裂いているのですから…。クラスやメソッドなどの定義法は細かな文法事項に簡単に書いておきます。

 次回は、今回誤魔化した「アクセス修飾子」と「静的メソッド」、ちょっと便利な機能「オーバーロード」について説明します。これだけ知っておけば、ひとまずプログラムらしいプログラムは組めるようになると思います。