連載

改訂版
プロフェッショナルVB.NETプログラミング

Chapter 05 オブジェクト関連の変化

株式会社ピーデー 川俣 晶
2004/04/15
Page1 Page2 Page3 Page4


 本記事は、(株)技術評論社が発行する書籍『VB6プログラマーのための入門 Visual Basic .NET 独習講座』の一部分を許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。

引数を持つ既定のプロパティ

 既定のプロパティでも解説したように、VB 6では、既定のプロパティを指定することができる。しかし、この機能は、VB.NETではサポートされていない。サポートされない理由はいくつもあるが、ソースが曖昧で分かりにくくなるというのが大きいだろう。オブジェクト変数そのものに代入しようとしている構文が、オブジェクト変数の書き換えを意図するのか、それとも、オブジェクトのデフォルト・プロパティにアクセスしようとしているのか、コードから瞬間的に明確に読みとれない場合があるからだ。さらにVB.NETでは、値の代入と参照の代入の構文の区別がなく、両者を明瞭に区別する手段が存在しないことになり、既定のプロパティという機能を付け加えることは不可能に近い状況になっている。

 しかし、この件には例外が1つある。引数を持つプロパティに関しては、既定のプロパティを宣言することができる。考えてみれば明らかなことだが、引数を持ったプロパティへの代入と、オブジェクト変数への代入は、ソース・コードの文字の並びが明らかに異なっており、コンパイラもプログラマーも間違える危険は低い。前者は「a = b」という構文だが、後者は「a(b) = c」というような構文になるからだ。VB.NETは、このような引数付きのプロパティに関しては、デフォルト・プロパティを指定することができる。

 リスト5-46は、実際にデフォルト・プロパティを記述してみたクラスの例である。

 1: Public Class Class1
 2:   Private str(9) As String
 3:
 4:   Default Public Property test1(ByVal index As Integer) As String
 5:     Get
 6:       Return str(index)
 7:     End Get
 8:     Set(ByVal Value As String)
 9:       str(index) = Value
10:     End Set
11:   End Property
12: End Class
リスト5-46 デフォルト・プロパティを記述したプログラム

 ここでポイントになるのは、4行目に記述されたDefaultキーワードである。このキーワードが付加されたことにより、4行目で宣言されるtest1プロパティはデフォルト・プロパティになる。もちろん、ここにDefaultキーワードが記述できるのは、このプロパティが引数を持っているためである。

 このクラスを呼び出すコードはリスト5-47のように書ける。

 1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 2:   Dim a As New Class1()
 3:   Dim i As Integer
 4:   For i = 0 To 4
 5:     a(i) = i.ToString()
 6:   Next
 7:   For i = 5 To 9
 8:     a.test1(i) = i.ToString()
 9:   Next
10:   For i = 0 To 4
11:     Trace.WriteLine(a.test1(i))
12:   Next
13:   For i = 5 To 9
14:     Trace.WriteLine(a(i))
15:   Next
16: End Sub
リスト5-47 リスト5-46を利用するプログラム

 これを実行すると以下のようになる。

 1: 0
 2: 1
 3: 2
 4: 3
 5: 4
 6: 5
 7: 6
 8: 7
 9: 8
10: 9
リスト5-48 リスト5-47の実行結果

 リスト5-47の4〜6行目では、デフォルト・プロパティの機能を使って値を代入している。整数(Integer)のToString()は、数値を文字列に変換するメソッドである。そして、コードの7〜9行目は、デフォルト機能は使わずプロパティ名を明示して値を代入している。10〜12行目は、代入した値を取り出して出力しているが、デフォルト・プロパティとして代入したものを、名前を明示したプロパティとして取り出している。同様に、13〜15行目は、名前を明示したプロパティとして代入されたものを、デフォルト・プロパティとして取り出している。結果を見て分かるとおり、デフォルト・プロパティと名前を明示したプロパティは完全に等価として機能しており、どちらで代入した値であろうと、どちらの方法でも読み出せる。

 次に、デフォルト・プロパティが実際に利用可能な例を見てみよう。ここでは、フォームのLoadイベント内で、フォーム上にあるコントロールの表示位置を一覧表示するものを考えてみる。これは次のようなコードで実現できる。

1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2:   Dim i As Integer
3:   For i = 0 To Me.Controls.Count() - 1
4:     Trace.Write(Me.Controls(i).Left)
5:     Trace.Write(", ")
6:     Trace.WriteLine(Me.Controls.Item(i).Top)
7:   Next
8: End Sub
リスト5-49 フォーム上のコントロール表示位置を一覧表示するプログラム

 以下のような内容がフォームに貼り付けられているときの実行結果を示す。

●図5-50 リスト5-49実行時のフォーム上のコントロール
 
1: 8, 96
2: 8, 64
3: 8, 32
4: 8, 8
リスト5-51 リスト5-49の実行結果

 さて、ソース・コードは分かりにくいと思うので詳しく説明しよう。

 まず、3行目から見ていこう。Meは処理中のフォーム自身のインスタンスを持っている。つまり、System.Windows.Forms.Formクラスのインスタンスを持っている。System.Windows.Forms.Formクラスは、Controlsというプロパティを持っている。このプロパティのデータ型は、System.Windows.Forms.Control.ControlCollectionクラスである。このクラスの役割は、フォーム上のコントロールの一覧表(コレクション)を提供することにある。このクラスには、コントロール数を返すCount()というメソッドがある。3行目では、このメソッドを用いて、繰り返す回数を決定している。

 コードとは順番が前後するが、先に6行目を見てみよう。System.Windows.Forms.
Control.ControlCollectionクラスには、データ型としてSystem.Windows.Forms.Controlクラスを持つ、Itemというプロパティがある。これは、引数で指定した順番のコントロールにアクセスするものである。コントロールの左の座標はこのクラスのLeftプロパティで参照でき、上の座標はこのクラスのTopプロパティで参照できる。

 さて、ここからが本題である。Itemプロパティはデフォルト指定がなされているため、プロパティ名を省略することができる。ソース・コード4行目を見ていただきたい。記述している内容は6行目とほぼ同じであるが、プロパティ名が省略されていることが分かる。

 このようなデフォルト・プロパティの指定は、クラスライブラリでしばしば見かけるものであり、自分からデフォルト・プロパティを記述する意思がないプログラマーでも、知識として知っておく価値がある。

 なお、この機能は、厳密な意味では同じとはいえないが、C#言語におけるインデクサと似たような効能を持つ機能ということができる。

Disposeパターン

 オブジェクト生成と解放のタイミングの違いで述べたとおり、VB 6と異なり、VB.NETでは確保されたインスタンスが解放されるタイミングは決まっていない。ガベージ・コレクタが回収しにきた時点でデストラクタが呼ばれ、資源が解放される。しかし、いつくるか分からないガベージ・コレクタを待てない場合もある。資源を解放するタイミングを厳密に指定するために、例外処理のFinallyブロックと、IDisposableインターフェイスを使う方法がある。次のリスト5-52は、IDisposableインターフェイスを実装した例である(インターフェイスについては、インターフェイス機能の変更点を参照)。

 1: Public Class A
 2:   Implements IDisposable
 3:
 4:   Public Sub New()
 5:     Trace.WriteLine("初期化")
 6:   End Sub
 7:
 8:   Public Sub SomeProcess()
 9:     Trace.WriteLine("クラスを用いた何かの処理")
10:   End Sub
11:
12:   Sub Dispose() Implements IDisposable.Dispose
13:     Trace.WriteLine("終了処理")
14:   End Sub
15: End Class
16:
17: Public Class Form1
18:   Inherits System.Windows.Forms.Form
19:
20: …Windows フォーム デザイナで生成されたコード…
21:
22:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
23:     Dim instance As New A()
24:     Try
25:       instance.SomeProcess()
26:     Finally
27:       instance.Dispose()
28:     End Try
29:   End Sub
30: End Class
リスト5-52 IDisposableインターフェイスを実装したプログラム

 これを実行すると以下のようになる。

1: 初期化
2: クラスを用いた何かの処理
3: 終了処理
リスト5-53 リスト5-52の実行結果

 IDisposableインターフェイスを使っていると、そのクラスがいったいどのような継承関係を持った何をするクラスかに関係なく、使い終わったらDisposeメソッドを呼び出せば正しく資源の解放が行われることが分かる。実際に、MSDNライブラリで「Dispose」というキーワードで検索すると、クラスライブラリの非常に多くのクラスが、このメソッドを実装していることが分かるだろう。興味のある方は、以下のMSDN Libraryのトピックを読むとよい。

アンマネージ・リソースをクリーンアップするためのFinalizeおよびDisposeの実装
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/ msdn/library/ja/cpgenref/html/cpconfinalizedispose.asp

 そして、Finallyブロックによって、Disposeメソッドを確実に実行させることで、確実な終了処理が達成できる。

Instancingプロパティの変更

 VB 6でActiveX DLLを作成する際、クラス・モジュールのInstancingプロパティをGlobalMultiuseなどに設定することで、メインプログラムから、グローバルな関数であるかのようにメソッドを呼び出し可能にすることができる。

 実際に実例を見てみよう。まず、ActiveX DLLの新規プロジェクトを作成し、リスト5-54のようなコードをクラス・モジュールに書き込む。

1: Sub Test()
2:   MsgBox "Hello!"
3: End Sub
リスト5-54 ActiveX DLLプロジェクトのクラス・モジュールに書き込むプログラム

 書き終えたら、InstancingプロパティをGlobalMultiuseに設定する。

 できたら、[ファイル]メニューの[Project1.dllの作成]を選んで、ファイルを作成しておく。

 さて、次はこれを呼び出す側のプログラムである。標準EXEのプロジェクトを新規作成してから、[プロジェクト]メニューの[参照設定]を選び、たったいま作成したActiveX DLLへの参照を追加する。そして、フォームのLoadイベントに、リスト5-55のように書き込む。

1: Private Sub Form_Load()
2:   Test
3: End Sub
リスト5-55 ActiveX DLL内のメソッドを呼び出すプログラム

 これを実行すると以下のようになる。

●図5-56 リスト5-55の実行結果

 見てのとおり、Loadイベント中では、シンプルにTestとだけ書いているが、実際にはクラスの中のメソッドであり、本来はインスタンスを明示しなければ呼び出せないものである。この場合、インスタンスは暗黙的に作成されて、それが呼び出される。

 さて、VB.NETには、これに相当する機能は存在しない。その代わり、より汎用的なImportsステートメントというものを使って、似たような効果を得ることができる。Importsステートメントは、VB.NET言語仕様の一部であり、プロジェクトの種類に関係なく使用できる。ActiveX DLLのような特定の使い方に限定されない。以下は1つのプログラム内で完結するサンプル・プログラムである。

 まず、このようなクラスがあったとしよう(作成するプロジェクト名は「Sample001n」とする)。

1: Public Class Class1
2:   Public Shared Sub Test()
3:     MsgBox("Hello!")
4:   End Sub
5: End Class
リスト5-57 共有メソッドを含んだクラスを定義したプログラム

 2行目にSharedキーワードがあることから分かるとおり、このメソッドはインスタンスを明示しなくても呼び出せる(インスタンスを作成せずに呼び出せるメソッドを参照)。しかし、インスタンスの代わりにクラスは明示しなければならないので、ただ単にTest()と書くだけでは呼び出せない。そこで、呼び出す側でImportsステートメントを使用する。リスト5-58はImportsステートメントを記述した例である。

 1: Imports Sample001n.Class1
 2:
 3: Public Class Form1
 4:
 5:   Inherits System.Windows.Forms.Form
 6:
 7: …Windows フォーム デザイナで生成されたコード…
 8:
 9:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
10:     Test()
11:   End Sub
12: End Class
リスト5-58 Importsステートメントを使用し、リスト5-57のメソッドを呼び出すプログラム

 これを実行すると以下のようになる。

●図5-59 リスト5-58の実行結果

 見てのとおり、9行目でクラス名も指定せずにTest()と書かれていて実行されている。その秘密は1行目にある。Sample001n.Class1は、Class1の名前空間を含むフルネームに当たる(Sample001nは、プロジェクト名から作成されるデフォルトの名前空間の名前)。このフルネームのクラスに属するものは、特にクラスを明示しなくても呼び出せるようになる。そのため、9行目のTest()は、Importsステートメントの指定により、Class1に含まれているという推理が成り立ち、呼び出せるわけである。

 Importsステートメントは、クラスだけでなく、名前空間を指定することもできる。名前空間を指定する使い方については、デフォルトの名前空間を指定するImportsステートメントも参照してほしい。

 なお、Importsステートメントは複数並べて書くことも許されているので、1つに限らない。End of Article

VB6プログラマーのための入門 Visual Basic .NET 独習講座』

 本記事は、(株)技術評論社が発行する書籍『VB6 プログラマーのための 入門 Visual Basic .NET 独習講座』から
許可を得て転載したものです。

【本連載と書籍の関係について 】
 この書籍は、本フォーラムで連載した「連載 プロフェッショナルVB.NETプログラミング」を大幅に加筆修正し、発行されたものです。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。

技術評論社の解説ページ

ご注文はこちらから
 

 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 05 オブジェクト関連の変化
    1.オブジェクト生成と解放のタイミングの違い/オーバーロード/コンストラクタへの引数
    2.インターフェイス機能の変更点/プロパティ構文の変更/インスタンスを作成せずに呼び出せるメソッド
    3.共有コンストラクタ/既定のプロパティ
  4.引数を持つ既定のプロパティ/Disposeパターン/Instancingプロパティの変更
 
「改訂版 プロフェッショナルVB.NETプログラミング 」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH