-
データ入力を行うフォームを作成する時に、システム標準のTextBox等でできればいいのですが、 より細かな入力制限などを行いたい場合に市販されているコントロールを利用するのも手だと思います。
GrapeCity かたはいろんなコントロールが販売されていて、フォーム上の入力を楽にするものの一つとして InputMan があります。 InputMan には日付、文字列、数値、コンボボックスなどの拡張入力コントロールが揃っています。 一からコントロールを作成するよりは買った方が早いのでこれを使用しています。 (私は特に GrapeCity の回し者でもありませんが)
InputManのコントロールですが、一つ気をつけないといけない点があります。
例として数値入力のコントロールである GcNumber を取り上げてみます。 GcNumber は数値入力ですので、値を設定・取得する場合のプロパティとして Value が利用します。 この Value のデータ型がNULLを許すNullable型のDecimalとして定義されています。
この例を示すために、 フォーム上に GcNumber を1個と、ボタンを2個貼り付けます。GcNumber の Value について
Public Class frmDataNullable 'フォームロード時イベント Private Sub frmDataNullable_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'GcNumber1に数値を削除した時にNULLを許可 Me.GcNumber1.AllowDeleteToNull = True End Sub 'Nullableな結果を通常の変数で受ける Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Try 'GcNumber1の値を受ける変数 Dim decNrm As Decimal '通常のDecimal型 decNrm = Me.GcNumber1.Value Dim strMsg As String = String.Format("GcNumber1.Value = {0}", decNrm.ToString) MsgBox(strMsg, MsgBoxStyle.OkOnly, "通常のDecimal型") Catch ex As Exception MsgBox(ex.Message) End Try End Sub 'Nullableな結果をNullableな変数で受ける Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Try 'GcNumber1の値を受ける変数 Dim decNul As Decimal? 'NullableなDecimal型 decNul = Me.GcNumber1.Value Dim strMsg As String '値を表示 strMsg = String.Format("GcNumber1.Value = {0}", decNul.ToString) MsgBox(strMsg, MsgBoxStyle.OkOnly, "NullableなDecimal型") '値の有効状態を表示 strMsg = String.Format("GcNumber1.HasValue = {0}" & vbCrLf & "GcNumber1.Valus IsNothing = {1}", decNul.HasValue, Not decNul Is Nothing) MsgBox(strMsg, MsgBoxStyle.OkOnly, "NullableなDecimal型") Catch ex As Exception MsgBox(ex.Message) End Try End Sub End Class
結果これを実行し、「通常の変数」(Button1)を押下すると以下の様になります。
この時の処理は、通常のDecimal型の変数を用意し、その変数に GcNumber の Value を設定しています。 コントロールに数値が入っているときは問題無く動作します。
GcNumber の内容を全て削除して空白にしてから、「通常の変数」(Button1)を押下すると以下の様になります。
GcNumber の Value がNullになる様で、変数の代入時にエラーが発生し、Catch でエラーメッセージが表示されます。
このエラーを回避するには当然なのですが、GcNumber の Value と同じデータ型の変数に代入することです。 「Nullableの変数」(Button2)を押下すると以下の様になります。
Nullableの変数を宣言する時にはデータ型の最後に ? (クエスチョンマーク)を付加します。
この変数は、状態として数値が入っている場合と、NULLの場合がありますので、 この変数をそのまま計算式に使う場合には注意が必要です。 少し面倒ですが、変数がNULLかどうかで計算を分けてやる必要があります。
ただ、データベースのテーブルでは数値項目でもNULLが許可されている場合には、 Nullableの変数は使えると思います。テーブルのカラムに値を設定するSQL文を作成する時には 変数がNULLかどうかで、値にNULLとするのか、それとも値そのものを設定するのかの処理を分けることが 必要かと思います。
今回は GcNumber を例にとりましたが、日付入力コントロールの GcDate も同様に Value プロパティを持っていて、このデータ型が Date? となっていますので 変数で受ける場合はそれに合わせる必要があります。
PR -
プログラム実行中のEXEファイルを削除しようとすると以下の様にシステムから、 「BlogTestによってファイルは開かれているため、操作を完了できません。」というエラーが返されます。
EXEファイルの例として 「SerialPortコントロールの使い方その2」 の実行を行いました。
プログラム実行中はそのEXEファイルが、システムによってロックが掛けられていて削除できないと思います。
しかし、削除は出来ないのですが、ファイル名の変更は可能なのです。 プログラム実行中にできるとは驚きなのですが、この機能を使うことでプログラム実行中にファイルの更新ができるのです。
プログラムの更新手順は以下の様に行えると思います。- 実行中のEXEファイルを別の名前に変更する。
- 実行ファイルの更新版をコピーする。
- 別の名前に変更されたファイルを削除する。(もしくは別のフォルダにコピーする。)
このEXEファイルのファイル名変更ですが、DLLファイルでも可能な様です。
Windowsのシステムアップデートなどもこの機能が無いとできない気がします...
-
SerialPortコントロールを使い方を以下の記事で紹介しましたが、 今回は外部装置との簡単な送受信ができるクラスの紹介を行います。
⇒SerialPortコントロールの使用方法:[SerialPort,Invoke]
クラスの概要は以下の通りです。- 外部装置との通信は調歩同期とし、Shift-JIS文字列での送受信を対象とする。
- 通信ポート設定は「ボーレート:9600BPS、データビット:8、パリティビット:無し、ストップビット:1」とする。
- 送受信文字列の終端文字は「CR:0xD」とする。
- 外部装置からのデータ受信は、最初にコマンド文字列を送信し、その後データ受信する手順とする。
- コマンド文字列送信後、約5秒以内にデータ受信できない場合はエラーとする。
- 受信文字列は4096バイトのバッファで行う。
SerialPortコントロールの使い方その2(通信用クラス)
Imports System.IO.Ports ''' ----------------------------------------------------------------------- '''
''' シリアル通信クラス ''' ''' ----------------------------------------------------------------------- Public Class ClsSerial '''''' シリアルポートクラス ''' '''Private SerialPort As SerialPort ''' ''' 読込データ格納先 ''' '''データ受信イベントで蓄えられる Private RxBuf() As Byte '''''' コマンド応答受信フラグ ''' '''''' コマンドを送信した後での応答を受信したフラグ。送受信文字列のターミネータ[CR](0x0d)を発見したとにオンする ''' Private fRxResponse As Boolean = False '''''' 送受信文字列のターミネータ(0x0d) ''' Private Const CMD_TERM As Byte = &HD ''' ----------------------------------------------------------------------- '''''' コンストラクタ ''' ''' <param name="SerialPort">シリアルポートコントロール</param> ''' ----------------------------------------------------------------------- Sub New(ByVal SerialPort As SerialPort) 'シリアルポートの退避 Me.SerialPort = SerialPort '受信イベントのハンドラ設定 AddHandler Me.SerialPort.DataReceived, AddressOf DataReceivedHandler End Sub ''' ----------------------------------------------------------------------- '''''' ポートオープン ''' '''True:正常終了, False:エラー ''' ----------------------------------------------------------------------- Function Open() As Boolean '戻り値初期化 Open = False Try 'ポートチェック If Me.SerialPort.IsOpen = False Then '未オープンならば、オープンする 'ポート設定(ここは通信相手に合わせる) Me.SerialPort.PortName = "COM7" Me.SerialPort.BaudRate = 9600 Me.SerialPort.DataBits = 8 Me.SerialPort.Parity = Parity.None Me.SerialPort.StopBits = StopBits.One 'オープン Me.SerialPort.Open() End If '結果を返す Open = Me.SerialPort.IsOpen Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' ポートクローズ ''' '''True:正常終了, False:エラー ''' ----------------------------------------------------------------------- Function Close() As Boolean '戻り値初期化 Close = False Try 'ポートチェック If Me.SerialPort.IsOpen = True Then 'オープン済みならば、クローズする Me.SerialPort.Close() End If '結果を返す Close = Not (Me.SerialPort.IsOpen) Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' データ受信ハンドラ ''' ''' ----------------------------------------------------------------------- Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs) Try Dim SP As SerialPort = CType(sender, SerialPort) 'バッファのバイト数チェック Dim DataLen As Integer = SP.BytesToRead If DataLen = 0 Then Exit Sub End If 'データ長チェック If DataLen > 4096 Then DataLen = 4096 End If '読込バッファの確保 Dim Buf(DataLen) As Byte '読込 SP.Read(Buf, 0, DataLen) ' If RxBuf Is Nothing Then '格納バッファの領域確保 RxBuf = Array.CreateInstance(GetType(Byte), DataLen) 'コピー Array.Copy(Buf, RxBuf, DataLen) Else '後ろに追加する Dim Length As Integer = RxBuf.Length ReDim Preserve RxBuf(Length + DataLen - 1) Buf.CopyTo(RxBuf, Length) End If '格納先の最後の文字が「CR」(0x0d)か確認する If RxBuf(UBound(RxBuf)) = CMD_TERM Then 'コマンド応答受信フラグON Me.fRxResponse = True End If Catch ex As Exception 'エラー処理 End Try End Sub ''' ----------------------------------------------------------------------- '''''' コマンド送信・レスポンス受信処理 ''' ''' <param name="strCmd">コマンド文字列</param> ''' <param name="strRsv">レスポンス文字列</param> '''応答受信結果(True:OK, False:NG) ''' ----------------------------------------------------------------------- Public Function SendCmdGetRsv(ByVal strCmd As String, ByRef strRsv As String) As Boolean '戻り値初期化 SendCmdGetRsv = False Try 'コマンド送信 If Me.SendData(strCmd) = False Then Return False End If 'コマンド受信 Dim strRX As String = "" If Me.ReceiveData(strRX) = False Then Return False End If strRsv = strRX '正常を返す Return True Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' コマンド送信 ''' ''' <param name="strSend">送信文字列</param> '''送信結果(True:OK, False:NG) ''' ----------------------------------------------------------------------- Private Function SendData(ByVal strSend As String) As Boolean '戻り値初期化 SendData = False Try 'ポートオープンチェック If Me.SerialPort.IsOpen = False Then Return False End If 'Shift JISとしてバイト型配列に変換 Dim bytesData As Byte() bytesData = System.Text.Encoding.GetEncoding(932).GetBytes(strSend) 'CRの1バイト分領域拡張 Dim nIdx As Integer = UBound(bytesData) ReDim Preserve bytesData(nIdx + 1) bytesData(nIdx + 1) = CMD_TERM 'コマンド応答受信フラグOFF Me.fRxResponse = False 'コマンド送信 Me.RxBuf = Nothing '受信バッファのクリア Me.SerialPort.Write(bytesData, 0, bytesData.Length) '正常 Return True Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' レスポンス受信処理 ''' ''' <param name="RcvRes">受信レスポンス</param> '''受信結果(True:OK, False:NG) ''' ----------------------------------------------------------------------- Private Function ReceiveData(ByRef RcvRes As String) As Boolean '戻り値初期化 ReceiveData = False Try 'リトライ件数 Dim nRetry As Integer = 50 '50×100msec=5sec While nRetry > 0 System.Threading.Thread.Sleep(100) Application.DoEvents() If Me.fRxResponse = True Then '受信フラグONの場合、ループを抜ける Exit While End If 'リトライカウンタ-- nRetry -= 1 End While '----- 'タイムアウトチェック '----- If nRetry = 0 Then 'タイムアウトエラー Return False End If '受信バイトを文字列変換 Dim strRx As String = System.Text.Encoding.GetEncoding(932).GetString(Me.RxBuf) strRx = strRx.Substring(0, strRx.Length - 1) '最後の「CR」は省く '----- '受信データ処理 '----- '以下の処理などが在れば記述 '・BCC等のチェック処理 '・レスポンスチェック処理(送信側のエラーメッセージ等) '----- '受信レスポンスを返す RcvRes = strRx '結果正常 Return True Catch ex As Exception 'エラー処理 End Try End Function End Class
突っ込みどころが満載なクラスですが、簡単なコマンドを送信し、レスポンスを受信するぐらいであれば使用できると思います。
尚、受信イベントでの処理で、受信バッファを4096に限定していますので、この辺りを改良してみて下さい。
また、レスポンスの受信データをそのまま返していますが、エラー処理などは通信先の装置に合わせて下さい。
このクラスを使用した例を以下のソースに示します。
SerialPortコントロールの使い方その2(通信用クラスの使用例)
Public Class frmSerial2 'シリアル通信クラス Private mclsSerial As ClsSerial '''''' フォームクローズイベント ''' Private Sub frmSerial2_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed 'シリアル通信のクローズ mclsSerial.Close() End Sub '''''' フォームロードイベント ''' Private Sub frmSerial2_Load(sender As Object, e As EventArgs) Handles Me.Load 'シリアル通信クラスの生成 mclsSerial = New ClsSerial(Me.SerialPort1) 'シリアル通信のオープン If mclsSerial.Open() = False Then 'オープンエラー(必要があれば処理する) End If End Sub '''''' [コマンド送信]クリックイベント ''' Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'レスポンス表示クリア Me.TextBox2.Text = "" 'コマンド取得 Dim strCmd As String = Me.TextBox1.Text.Trim If strCmd <> "" Then Dim strRsv As String = "" If Me.mclsSerial.SendCmdGetRsv(strCmd, strRsv) = True Then '正常受信、レスポンス表示 Me.TextBox2.Text = strRsv Else 'エラー Me.TextBox2.Text = "エラー発生" End If End If End Sub End Class
このフォームは、送信用の文字列を入力する TextBox と、受信結果を表示する TextBox を画面に張り付け、 通信の処理をさせるボタンを張り付けてあります。
実行の様子を下図に示します。
送信する文字列を 送信コマンド: TextBox に入力し、「コマンド送信」ボタンを押下します。
「RS232Cテストツール」側でコマンドの受信を確認して、「TEXT送信」ボタンを押下しますと、 受信レスポンス: TextBox にデータが表示されます。
(尚、通信相手のシミュレーションとしてフリーソフトの「RS232Cテストツール」を使用しています。)
尚、通信相手用の仮想シリアルポートとしてcom0com というドライバを使用し、 2個のシリアルポートを作成し、ループバックで接続しています。 今回のプログラムをテストするためには外部装置か、もう一台別のPCが必要になりますが、 そんなことをしなくてもデバッグができるソフト(ドライバ)が在ります。
com0com というドライバで、PC上に仮想シリアルポートを2個作成し、 その2個をループバックできる様に仮想的に接続できるという優れものです。 以下のリンク先でソフトをダウンロードしてインストールしてみて下さい。 (尚、もう一台のPCなどがあれば必要ありませんが)
⇒「com0com ドライバ」
COM8側を通信相手側(外部装置)とするのですが、通信テスト用に以下のフリーソフトを使いました。
「のん」さんといわれる方が作成された「RS232Cテストツール」です。
以下のサイトにアクセスすれば、ダウンロードできます。
⇒「RS232Cテストツール」USB ⇒ RS232C 変換ケーブル
シリアル通信に関連してですが、 最近のPC、特にノートPCの場合ではRS232Cのコネクタがついていることは殆んどありません。
そのため、以下の様なUSBを使ったRS232Cの変換ケーブルが使えそうです。
⇒iBUFFALO USBシリアルケーブル(USBtypeA to D-sub9ピン)1.0m ブラックスケルトン BSUSRC0610BS
⇒サンワサプライ USB-RS232Cコンバータ USB-CVRS9
⇒Plugable USB‐9ピンRS232シリアルアダプター (Prolific社製 PL2303HX Rev Dチップセット採用)
関連する記事
⇒SerialPortコントロールの使用方法 :[SerialPort,Invoke]
⇒SerialPortコントロールの使い方その3(外部装置からの垂れ流しデータ受信)
⇒SerialPortコントロールの使い方その4(データ受信時にイベントを発生させる)
⇒SerialPortコントロールの使い方その5(データ受信時及び、信号変化時にイベントを発生させる)
⇒SerialPortコントロールの使い方その6(ハンドシェィクによるデータ送受信)
おすすめ本
-
あるフォームから別のフォームを表示し、その子フォームでの情報を親のフォームからアクセスしたい場合に、 子フォームにプロパティを持ち、それを介してデータのやり取りを行う方法は以下のソースの様になることを以前紹介しました。
プロパティを持つ子フォームのクラス
'''
''' プロパティを持つ子フォーム ''' Public Class frmProperty 'チェック状態退避値 Private mblnChkStatus As Boolean = False '''''' チェック状態プロパティ ''' '''チェック状態の設定値 '''チェック状態を返す Public Property ChkStatus As Boolean Set(value As Boolean) mblnChkStatus = value '退避値に設定 End Set Get Return mblnChkStatus '退避値を返す End Get End Property '''''' ボタンクリックイベント ''' Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'チェックボックスの状態を退避 Me.mblnChkStatus = Me.CheckBox1.Checked '自分を閉じる Me.Close() End Sub '''''' フォームロードイベント ''' Private Sub fomProperty_Load(sender As Object, e As EventArgs) Handles Me.Load 'チェックボックスの初期状態設定 Me.CheckBox1.Checked = Me.mblnChkStatus End Sub End Classこの子フォームを呼出す親フォームの処理が以下の様になります。
子フォームを呼出す親フォームの処理
'''
''' 子フォームを呼出す親フォーム ''' Public Class frmPropMain '''''' ボタンクリックイベント ''' Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'プロパティテストウインドウ生成 Dim frmProp As New frmProperty 'プロパティに自分のチェックボックスの値を設定 frmProp.ChkStatus = Me.CheckBox1.Checked 'ウインドウ表示 frmProp.ShowDialog() 'プロパティ値を自分のチェックボックスの値に設定 Me.CheckBox1.Checked = frmProp.ChkStatus 'プロパティテストウインドウ廃棄 frmProp.Dispose() frmProp = Nothing End Sub End Classこの例では子フォームを生成した後で、子フォームのプロパティを設定しています。二回に分けて処理をしますので少し面倒ですね。
この面倒さを解消するには、子フォームクラスの生成に引数として渡せればできるのです。
実際にどの様にするのかを、上記のソースを土台にして変えていきます。 尚、今回の例ではフォームクラスの名前を frmConstructor と変えています。
ソースは上記の frmProperty を全てコピーしてきて、その後でクラスの先頭辺りで Sub New とキー入力します。 そうすると、以下の様な感じで Sub New() の関数が自動で表示されます。
コンストラクタを明示する子フォームのクラス
'''
''' コンストラクタを明示する子フォーム ''' Public Class frmConstructor Sub New() ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 End Sub 'チェック状態退避値 Private mblnChkStatus As Boolean = False '''''' チェック状態プロパティ ''' '''チェック状態の設定値 '''チェック状態を返す Public Property ChkStatus As Boolean Set(value As Boolean) mblnChkStatus = value '退避値に設定 End Set Get Return mblnChkStatus '退避値を返す End Get End Property '''''' ボタンクリックイベント ''' Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'チェックボックスの状態を退避 Me.mblnChkStatus = Me.CheckBox1.Checked '自分を閉じる Me.Close() End Sub '''''' フォームロードイベント ''' Private Sub fomProperty_Load(sender As Object, e As EventArgs) Handles Me.Load 'チェックボックスの初期状態設定 Me.CheckBox1.Checked = Me.mblnChkStatus End Sub End Class
この後で Sub New() に引数を宣言し、フォーム生成時にプロパティが設定できる様に以下の様にソースを変更します。
コンストラクタ(引数設定)を明示する子フォームのクラス
'''
''' コンストラクタを明示する子フォーム ''' Public Class frmConstructor Sub New(ByVal blnChk As Boolean) ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 'プロパティを設定する Me.ChkStatus = blnChk End Sub 'チェック状態退避値 Private mblnChkStatus As Boolean = False '''''' チェック状態プロパティ ''' '''チェック状態の設定値 '''チェック状態を返す Public Property ChkStatus As Boolean Set(value As Boolean) mblnChkStatus = value '退避値に設定 End Set Get Return mblnChkStatus '退避値を返す End Get End Property '''''' ボタンクリックイベント ''' Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'チェックボックスの状態を退避 Me.mblnChkStatus = Me.CheckBox1.Checked '自分を閉じる Me.Close() End Sub '''''' フォームロードイベント ''' Private Sub fomProperty_Load(sender As Object, e As EventArgs) Handles Me.Load 'チェックボックスの初期状態設定 Me.CheckBox1.Checked = Me.mblnChkStatus End Sub End Class子フォームの生成時に引数を設定し処理する例として以下のソースに示します。
引数を持って子フォームを呼出す親フォームの処理
'''
''' 引数を持って子フォームを呼出す親フォーム ''' Public Class frmConstructorMain '''''' ボタンクリックイベント ''' Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'プロパティテストウインドウ生成(自分のチェックボックスの値を引数) Dim frmConst As New frmConstructor(Me.CheckBox1.Checked) 'ウインドウ表示 frmConst.ShowDialog() 'プロパティ値を自分のチェックボックスの値に設定 Me.CheckBox1.Checked = frmConst.ChkStatus 'プロパティテストウインドウ廃棄 frmConst.Dispose() frmConst = Nothing End Sub End Classもし、引数在りでも無しでも両方の子フォームのコンストラクタを持ちたい場合には、 以下の様に引数無しの Sub New() をもう1個宣言すればできます。
この方法は オーバーロード と言われるもので、引数や戻り値が異なるが名前が同じの関数を複数定義する方法です。
(この場合は Sub の宣言ですが、 Function の場合でも同じ様にできます。)
コンストラクタ(引数設定)を明示する子フォームのクラス
'''
''' コンストラクタを明示する子フォーム ''' Public Class frmConstructor Sub New(ByVal blnChk As Boolean) ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 'プロパティを設定する Me.ChkStatus = blnChk End Sub Sub New() ' この呼び出しはデザイナーで必要です。 InitializeComponent() End Sub '以下のソースは省略..... End Class関連する記事
⇒フォームクラス生成時に設定値を同時に渡す方法
⇒フォーム上の複数のコントロール(TextBox)の入力変化を確認する方法について
⇒フォームのマウスカーソルを待機状態する方法について
⇒フォームクラスの継承の方法について
-
関数の戻り値として、関数内でクラスのインスタンスを New で生成しその参照を戻す場合があります。 この場合、戻されたその参照の実体はどこにあるのでしょうか。これなのですがVB.NETシステムのヒープ領域のメモリ上に在る様です。 このメモリ上の参照しているのが New で生成された結果の値を保持する変数になります。
以下のソースにその例を示します。
frmRetval と名付けたフォーム上に1個のボタンを張り付けてあります。 このフォームクラス内に、戻り値テスト用のクラスとして clsRetTest を宣言しています。 このクラスは2個の内部変数とその内容を文字列で返すメソッド、及びコンストラクタの簡単な処理から構成します。
更にクラス clsRetTest を戻す関数として GetRetVal 関数を定義しています。戻り値テスト用
Public Class frmRetval '戻り値テスト用のクラス Private Class clsRetTest Private _strTest As String Private _intTest As Integer 'コンストラクタ Sub New(ByVal strTest As String, ByVal intTest As Integer) Me._strTest = strTest Me._intTest = intTest End Sub '内部データの表示用の文字列取得 Function GetDataString() As String Return "strTest=" & Me._strTest & vbCrLf & "intTest=" & Me._intTest.ToString End Function End Class 'クラスを戻す関数をテストする処理 Private Sub RetrunTest_Click(sender As Object, e As EventArgs) Handles RetrunTest.Click 'クラスを生成する関数コール Dim clsTest As clsRetTest = GetRetVal() '内部データの表示 MsgBox(clsTest.GetDataString) End Sub 'クラス(clsRetTest)を戻す関数 Private Function GetRetVal() As clsRetTest 'クラスの生成 Dim clsRet As New clsRetTest("123", 100) 'クラスを戻す Return clsRet End Function End Classクラスを戻す関数をテストするのは、ボタンクリックイベントで行っています。 このイベントでは、クラスを生成する関数(GetRetVal)をコールし結果を clsTest 変数に入れています。 GetRetVal の中ではクラス clsRetTest がインスタンスがヒープ上に生成されその参照を戻り値として戻します。 イベント処理内ではその参照を使い、そのメソッドをコールすることができます。
このイベント処理内の変数 clsTest はこの処理を抜けると使用できなくなります。 (自動変数はそれ自身を含むブロックを超えては参照が出来ません。)
clsTest は使用できなくなるのは分かるのですが、先ほどのヒープ上の clsRetTest クラスの領域はどうなるのでしょうか。 少し心配になりますが、実はシステム側である時点が来た時にきれいに掃除をしてくれます。 (このことをガーベージコレクションと言うらしい。)
ボタンクリックが何度も押されると RetrunTest_Click が何度も実行されることになります。 この時はその度に、ヒープ上に別のインスタンスが生成され、そのインスタンスの参照が返されます。 そうすると使わないインスタンスがどんどん溜まっていくのですが、これもそのうちガーベージコレクションが行われて無くなる様です。 (ガーベージコレクションがいつ行われるかは神のみぞ知る的な感じな様ですが)
イベント処理内の clsTest がイベント処理が終わった後でも参照したい場合には、 その変数を以下のソースの様に、イベント処理内からフォームクラスの静的変数として関数の外で宣言すればできます。
クラス参照を静的変数で宣言
Public Class frmRetval '戻り値テスト用のクラス Private Class clsRetTest Private _strTest As String Private _intTest As Integer 'コンストラクタ Sub New(ByVal strTest As String, ByVal intTest As Integer) Me._strTest = strTest Me._intTest = intTest End Sub '内部データの表示用の文字列取得 Function GetDataString() As String Return "strTest=" & Me._strTest & vbCrLf & "intTest=" & Me._intTest.ToString End Function End Class 'クラス参照宣言 Dim clsTest As clsRetTest 'クラスを戻す関数をテストする処理 Private Sub RetrunTest_Click(sender As Object, e As EventArgs) Handles RetrunTest.Click 'クラスを生成する関数コール clsTest = GetRetVal() '内部データの表示 MsgBox(clsTest.GetDataString) End Sub 'クラス(clsRetTest)を戻す関数 Private Function GetRetVal() As clsRetTest 'クラスの生成 Dim clsRet As New clsRetTest("123", 100) 'クラスを戻す Return clsRet End Function 'クラスのメソッドのみをコールするイベント Private Sub DisplayClass_Click(sender As Object, e As EventArgs) Handles DisplayClass.Click '内部データの表示 MsgBox(clsTest.GetDataString) End Sub End ClassclsTest をフォームクラスの静的変数として宣言すれば、そこに設定されたヒープへの参照が残りますので、 それを使ってクラスの処理ができます。今回の例では、DisplayClass_Click がそれに当たります。
但し、このソースには問題が有ります。 RetrunTest_Click を行う前に DisplayClass_Click を行うと例外が発生してしまいます。 これは、clsTest が設定される前(値としては Nothing)にクラスのメソッドを使用するからです。 この問題を解消するには DisplayClass_Click 内で clsTest が Nothing では無いことを確認し 使用します。
尚、この clsTest ですが何時廃棄されるのかと言いますと、フォームクラスが廃棄される時点で行われ、 その後ガーベージコレクションが行われると思います。関連する記事
⇒関数の引数がクラスオブジェクトの場合の注意点について