|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 05 オブジェクト関連の変化
株式会社ピーデー
川俣 晶
2004/04/15 |
|
|
VB 6には、継承はなくてもインターフェイスの機能があり、簡単なポリモーフィズムぐらいなら記述することができた。しかし、このインターフェイス関係の機能が、VB 6とVB.NETでは大幅に変化している。その実例を見てみよう。
ここでは、クラス1個、インターフェイス1個を持つサンプル・プログラムを記述してから、インターフェイス経由で、クラス内に記述したメソッドを呼び出してみる。まずは、インターフェイス「Class2」を定義したソースである。
1: Public Sub OutputMessage(ByVal msg As String)
2: End Sub
|
|
リスト5-18 インターフェイス「Class2」を定義したプログラム
|
次に、このインターフェイスを実装するクラス「Class1」を記述してみた。
1: Implements Class2
2: Public Sub Class2_OutputMessage(ByVal msg As String)
3: Debug.Print msg
4: End Sub
|
|
リスト5-19 リスト5-18のインターフェイスを実装したクラス「Class1」
|
これらを利用した、インターフェイス経由でクラスのメソッドを呼び出すソース・コードとして以下のようなものを書いてみた。
1: Private Sub Form_Load()
2: Dim obj As Class1
3: Set obj = New Class1
4: Dim interf As Class2
5: Set interf = obj
6: interf.OutputMessage "Hello!"
7: End Sub
|
|
リスト5-20 インターフェイス経由でクラスのメソッドを呼び出すプログラム
|
これを実行すると以下のような出力が得られる。そのことに疑問はないと思う。
|
リスト5-21 リスト5-20の実行結果
|
さて、このソースをVB.NET用に書き換えるにはどうすればよいのだろうか。1つずつソースを置き換えてみよう。まず、インターフェイスの定義は以下のようになる。
1: Public Interface Class2
2: Sub OutputMessage(ByVal msg As String)
3: End Interface
|
|
リスト5-22 インターフェイスを定義しているリスト5-18をVB.NETで書き換えたプログラム
|
見てのとおり、相当違っていることが分かるだろう。まず、VB 6ではインターフェイスはクラスの一種として作成したが、VB.NETではインターフェイスはインターフェイスという機能として扱われている。そのため、1行目のように“Interface”で始めて、3行目のように“End Interface”で終わる必要がある。そして、肝心の中身の定義も大きく異なっている。VB 6では中身のないSubを記述することでインターフェイスのメンバを定義したが、VB.NETでは最初から中身がないものとして扱われる。これは、最初に1行目でインターフェイスであることを明示していることにより、中身が存在しないことが明らかだからである。その結果、中身の終わりを示すEnd Subを記述する必要はなく、実際、上記のソースにはEnd Subが含まれない。
もう1点の相違は、Subの手前に付いていたPublicキーワードが、VB.NETでは不要になったことだ。インターフェイスでは、Privateにすることに意味がないので、Publicキーワードをわざわざ付ける意味はない。もし、インターフェイスにPrivateな定義が含まれていたとしても、それをほかのクラスなどから参照できないため、実装することができないのである。
なお、ここではVB 6のソースに合わせて「Class2」という名前でインターフェイスを作成しているが、これはVB.NETらしいネーミングとしてはそぐわない。例えば、「Interface2」というような名前の方が適切だろう。
さて、次は、このインターフェイスを実装するクラスのサンプル・プログラムである(リスト5-23)。
1: Public Class Class1
2: Implements Class2
3: Public Sub OutputMessage(ByVal msg As String) Implements Class2.OutputMessage
4: Trace.WriteLine(msg)
5: End Sub
6: End Class
|
|
リスト5-23 リスト5-22のインターフェイスを実装するプログラム
|
注目してもらいたいのは3行目で、ここでインターフェイスで定義されたメソッドの内容を実装している。その際、VB 6では、クラス名とメソッド名を含む独特の名前を用いて、インターフェイスの実装であることを示していた。今回のサンプルなら、インターフェイスのクラス名がClass2で、メソッド名がOutputMessageであるため、Class2_OutputMessageという名前のメソッドを記述していた。しかし、VB.NETではそのようなネーミングを使用せず、その代わりにImplementsキーワードを使う。Implementsキーワードの後に、インターフェイス名とメソッド名をピリオド区切りで記述する。これは、名前空間名を含む長い名前であってもよい。これにより、どのインターフェイスのどのメソッドを実装するのかが明示される。
最後に、これらのソースを呼び出す側のコードを以下に示す(リスト5-24)。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim obj As Class1
3: obj = New Class1()
4: Dim interf As Class2
5: interf = obj
6: interf.OutputMessage("Hello!")
7: End Sub
|
|
リスト5-24 リスト5-23を利用するプログラム
|
これを実行すると以下のようになる。
|
リスト5-25 リスト5-24の実行結果
|
インターフェイスの定義と実装は変わったが、利用する方法は変わってはいないことが分かるだろう。
VB 6からVB.NETで変化したものは多いが、プロパティの構文は、特に大きな変更があったポイントの1つといえるだろう。事前の知識がなければ、どう書き換えればよいのか、予測もつかないはずだ。それぐらい劇的に変化している。では、最初にVB 6でプロパティを記述した例を以下に示そう。まず、2つのプロパティを持つクラスのコードから(リスト5-26)。
1: Private str As String
2: Private obj As Object
3:
4: Public Property Get test1() As String
5: test1 = "Hello World!"
6: End Property
7:
8: Public Property Let test1(s As String)
9: str = s
10: End Property
11:
12: Public Property Get test2() As Object
13: Set test2 = obj
14: End Property
15:
16: Public Property Set test2(o As Object)
17: Set obj = o
18: End Property
|
|
リスト5-26 2つのプロパティを持つクラスを定義したプログラム
|
これを呼び出すコードとして、以下のようなコードを記述してみた(リスト5-27)。
1: Private Sub Form_Load()
2: Dim obj As New Class1
3:
4: obj.test1 = "Hello!"
5: Debug.Print obj.test1
6:
7: Set obj.test2 = Form1
8: Debug.Print obj.test2.Caption
9: End Sub
|
|
リスト5-27 リスト5-26を呼び出すプログラム
|
これを実行すると以下のようになる。
|
リスト5-28 リスト5-27の実行結果
|
では、これとほぼ同等の機能をVB.NETで記述してみた例を次に示す。まずは、プロパティを持つクラスのソースから(リスト5-29)。
1: Public Class Class1
2: Private str As String
3: Private obj As Object
4:
5: Public Property test1() As String
6: Get
7: Return str
8: End Get
9: Set(ByVal Value As String)
10: str = Value
11: End Set
12: End Property
13:
14: Public Property test2() As Object
15: Get
16: Return obj
17: End Get
18: Set(ByVal Value As Object)
19: obj = Value
20: End Set
21: End Property
22: End Class
|
|
リスト5-29 リスト5-26とほぼ同等の機能を持つVB.NETのプログラム
|
見てのとおり、激変である。まず、Property Set、Property Let、Property Getの3つの構文は姿を消した。その代わり、5行目や14行目のようなProperty宣言を用いて、プロパティの宣言を行う。そして、Property宣言の内部に、“Get〜End Get”と“Set〜End Set”を書き込んで、その中に実際にSetやGetの処理を書き込む。ここで注意すべきことは、VB 6では、Set、Let、Getと3つの種類の操作があったのに、VB.NETには、SetとGetしか存在しないことである。これは、VB.NETではLetとSetの区別がなくなったことにより、プロパティでも、LetとSetの区別が不要になったことによる変化である。
実際にプロパティの動作を記述する方法も変化している。まず、7行目や16行目のReturnは、プロパティの値を設定する機能を持っている。Returnを使うのは、メソッドの戻り値を指定するためのVB.NETの新しい構文と共通のものである(プロシージャ脱出とReturnステートメント、関数の戻り値とReturnステートメントを参照)。なお、従来どおり、プロパティ名への代入で行うこともできる。
これを呼び出すコードは、リスト5-30のようになる。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim obj As New Class1()
3:
4: obj.test1 = "Hello!"
5: Trace.WriteLine(obj.test1)
6:
7: obj.test2 = sender
8: Trace.WriteLine(obj.test2.Text)
9: End Sub
|
|
リスト5-30 リスト5-29を呼び出すプログラム
|
ほとんど同じだが、Setキーワードが不要になり、SetとLetの区別が不要になったことが、このソースからも読みとれるだろう。なお、7行目だけ別のコードに書き変わっているが、VB 6のForm1はフォームのインスタンスの名前であるのに対して、VB.NETではForm1はクラス名であることにより、Form1を代入することはできない。このイベントが発生したフォームのインスタンスへの参照は、Loadイベントの引数のsenderに含まれているので、これを使えば等価の結果となる。さらに、8行目のTextプロパティは、VB 6のCaptionプロパティの名前が変わったものである。
これを実行すると、以下のようになる。
|
リスト5-31 リスト5-30の実行結果
|
結果は同じだが、構文は大きく変わったので注意しよう。
VB 6には、厳密にぴったり対応する機能が見あたらないのだが(しいていえば、Moduleに含まれるメソッドなどに近い)、VB.NETには、インスタンスを作成せずに呼び出せるメソッドを記述できる。リスト5-32はそれを記述したサンプル・プログラムである。
1: Public Class Class1
2: Public Shared Sub TestShare()
3: Trace.WriteLine("TestShare called")
4: End Sub
5:
6: Public Sub TestNonShare()
7: Trace.WriteLine("TestNonShare called")
8: End Sub
9: End Class
|
|
リスト5-32 Sharedキーワードを使用してメソッドを定義したプログラム
|
このソース・コードの2行目を見ていただきたい。Subキーワードの手前に、Sharedというキーワードが見えると思うが、これがインスタンスを作成せずに呼び出せるメソッドであることを示している。これを共有メソッドという。対比のために、6〜8行目に、通常のメソッドも記述してみた。
このクラスを利用するサンプル・プログラムを以下のように記述してみた(リスト5-33)。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Class1.TestShare()
3: Dim instance As New Class1()
4: instance.TestNonShare()
5: instance.TestShare()
6: End Sub
|
|
リスト5-33 リスト5-32を利用するプログラム
|
これを実行すると以下のようになる。
1: TestShare called
2: TestNonShare called
3: TestShare called
|
|
リスト5-34 リスト5-33の実行結果
|
見てのとおり、3行目のDim文でインスタンスを作成する前に、2行目でClass1のTestShareメソッドが呼び出されている。Class1.TestShare()という記述で分かるとおり、このような場合には、インスタンスではなくクラス名を明示する。
しかし5行目のように、インスタンスの名前を明示して、それを経由して共有メソッドを呼び出すこともできる。
なお、当たり前のことではあるが、共有メソッドはインスタンスを作成せずに呼び出せることから分かるとおり、インスタンスに属するメンバ変数などにアクセスすることはできない。
Sharedキーワードはメソッドだけでなく、メンバ変数など他のクラスの構成要素にも付加することができる。コンストラクタに付けた場合は、共有コンストラクタで解説している。