インデクサとプロパティは、うまく使えばプログラムを簡潔に記述することができて便利なのだが、一歩使い方を間違うと、トラブルのもとにもなる。
まず、List 12-9のサンプル・ソースを見ていただきたい。22〜25行目を見て、結果を容易に推測できるだろうか?
1: using System;
2:
3: namespace Sample008
4: {
5: class Sum
6: {
7: private int sum = 0;
8: public int Add
9: {
10: set { sum += value; }
11: }
12: public int Result
13: {
14: get { return sum; }
15: }
16: }
17: class Class1
18: {
19: [STAThread]
20: static void Main(string[] args)
21: {
22: Sum sum = new Sum();
23: sum.Add = 1;
24: sum.Add = 2;
25: Console.WriteLine( sum.Result );
26: }
27: }
28: }
このプログラムを実行するとFig.12-9のようになる。
つまり、結果は3になる。23行目の1と24行目の2が足されて3である。しかし、プロパティとは変数に見せかけた処理であるとすれば、この結果は直感に反する。つまり、24行目でプロパティに2を代入しているのだから、23行目で代入した1は消えてなくなって、2がオブジェクトの中に入ると考えるのが自然である。決して、24行目のコードから足し算が行われるとは読み取れない。確かに、Addというキーワードが足し算を連想させる言葉かもしれないが、誰もがそれだけで足し算を行うと連想できるわけではない。しかも足し算だと推理したところで、何と何を足すのかは、23〜24行目からは読み取れない。これらの問題から、プロパティをこのように使うのは好ましいことではないといえる。
これはList 12-10のように書くほうがベターといえる。
1: using System;
2:
3: namespace Sample009
4: {
5: class Sum
6: {
7: private int sum = 0;
8: public void Add( int number )
9: {
10: sum += number;
11: }
12: public int Value
13: {
14: set { sum = value; }
15: get { return sum; }
16: }
17: }
18: class Class1
19: {
20: [STAThread]
21: static void Main(string[] args)
22: {
23: Sum sum1 = new Sum();
24: sum1.Add( 1 );
25: sum1.Add( 2 );
26: Console.WriteLine( sum1.Value );
27:
28: Sum sum2 = new Sum();
29: sum2.Value += 1;
30: sum2.Value += 2;
31: Console.WriteLine( sum2.Value );
32: }
33: }
34: }
このプログラムを実行するとFig.12-10のようになる。
ここでは、Addをプロパティではなくメソッドに変更している。メソッド呼び出しであれば、誰も変数のように振る舞うという予断を持たないので、メソッドの処理内容を確認するまでは、結果を推理しないだろう。一方、Valueのほうは、単なる内部変数の読み書きを行うだけなので、プロパティとして実装しても混乱することはない。むしろ、メソッドにするよりも、プロパティのほうが分かりやすくて好ましい。読み書き両用のプロパティValueがあれば、29〜30行目のような記述も可能になる。これは、誰が見ても処理内容が直感的に理解でき、非常に好ましい。
このような問題はプロパティだけでなくインデクサにも起こる。List 12-11がその例である。このプログラムがどんな数値を出力するか、ソース・コードから容易に読み取れるだろうか?
1: using System;
2:
3: namespace Sample010
4: {
5: class Class2
6: {
7: private int a = 0;
8: public int this[int index]
9: {
10: get
11: {
12: return a + index;
13: }
14: set
15: {
16: a = index + value;
17: }
18: }
19: }
20: class Class1
21: {
22: [STAThread]
23: static void Main(string[] args)
24: {
25: Class2 t = new Class2();
26: t[1] = 2;
27: Console.WriteLine( t[3] );
28: }
29: }
30: }
このプログラムを実行するとFig.12-11のようになる。
List 12-11の中のインデクサは、配列型のように振る舞わない。このようなインデクサは、ソース・コードを読む者に混乱を与えるだけなので、記述すべきではない。定義をすべて読まないと挙動が推理できないとすれば、インデクサの価値はない。素直にメソッドを使うべきだろう。
『新プログラミング環境 C#がわかる+使える』
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て一部分を転載したものです。
【本連載と書籍の関係について 】
この書籍は、本フォーラムで連載した「C#入門」を大幅に加筆修正し、発行されたものです。連載時はベータ版のVS.NETをベースとしていましたが、書籍ではVS.NET製品版を使ってプログラムの検証などが実施されています。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。
→技術評論社の解説ページ
ご注文はこちらから
Copyright© Digital Advantage Corp. All Rights Reserved.