忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

関数の戻り値がクラス(オブジェクト)の場合について

関数の戻り値として、関数内でクラスのインスタンスを 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 Class

clsTest をフォームクラスの静的変数として宣言すれば、そこに設定されたヒープへの参照が残りますので、 それを使ってクラスの処理ができます。今回の例では、DisplayClass_Click がそれに当たります。

但し、このソースには問題が有ります。 RetrunTest_Click を行う前に DisplayClass_Click を行うと例外が発生してしまいます。 これは、clsTest が設定される前(値としては Nothing)にクラスのメソッドを使用するからです。 この問題を解消するには DisplayClass_Click 内で clsTestNothing では無いことを確認し 使用します。

尚、この clsTest ですが何時廃棄されるのかと言いますと、フォームクラスが廃棄される時点で行われ、 その後ガーベージコレクションが行われると思います。

PR

コメント

コメントを書く