変数のスコープ

これまで Dim というステートメントを厳密に理解することなく使ってきた。今回のプログラムでは Dim がプログラムのいろいろな場所で使われているが、場所によってDimの機能が若干異なる。 Dimの場所による機能の違いについて理解しよう。 これまでもDimの場所によって、変数の機能が少し異なることに気が付いていたと思う。

たとえば、練習問題1のプログラムで、1行 Dim xを追加してみよう。以下の赤字の部分が追加したDim xである。

 

Public Class Form1
    Private Sub Form1_Load( _
            ByVal sender As System.Object, _
            ByVal e As System.EventArgs _
            ) Handles MyBase.Load
        ' Pictureboxの作成と初期化
        Dim Picturebox1 As PictureBox
        Picturebox1 = New PictureBox
        Picturebox1.Dock = DockStyle.Fill 'ウインドウ全体にする
        Picturebox1.BackColor = Color.White ' 白にする
        Me.Controls.Add(Picturebox1) 'Form1と接続する
        ' Timerの作成と初期化
        Dim Timer1 As Timer
        Timer1 = New Timer
        Timer1.Interval = 10
        Timer1.Enabled = True
        AddHandler Timer1.Tick, AddressOf Me.Timer1_Tick
        ' box1の作成と初期化
        Dim box1 As MyBox
        
        box1 = New MyBox(Picturebox1, Timer1) '作成する
        box1.Location(0, 100)
        box1.Speed(1, 0)
        box1.Size(20, 20)
    End Sub
    Private Sub Timer1_Tick( _
            ByVal sender As System.Object, _
            ByVal e As System.EventArgs _
            )
        ' 1Tick毎に表示を更新
        Me.Refresh()
    End Sub
End Class
Public Class MyBox
    Dim x  ' x座標
    Dim y  ' y座標
    Dim w  ' 幅
    Dim h  ' 高さ
    Dim vx ' 1Tickあたりのx方向移動量
    Dim vy ' 1Tickあたりのy方向移動量
    Public Sub New(ByVal p1 As PictureBox, ByVal t1 As Timer)
        AddHandler p1.Paint, AddressOf Paint
        AddHandler t1.Tick, AddressOf Tick
    End Sub
    Public Sub Location(ByVal x1, ByVal y1)
        ' 位置(x,y)を指定された値(x1,y1)にする
        x = x1
        y = y1
    End Sub
    Public Sub Size(ByVal w1, ByVal h1)
        ' サイズ(w,h)を指定された値(w1,h1)にする
        w = w1
        h = h1
    End Sub
    Public Sub Speed(ByVal vx1, ByVal vy1)
        ' 速度(vx,vy)を指定された値(vx1,vy1)にする
        vx = vx1
        vy = vy1
    End Sub
    Public Sub Tick(ByVal sender As System.Object, _
    ByVal e As System.EventArgs)
        ' 指定された速度で移動する
        Dim x
        x = (x + vx + 300) Mod 300
        y = (y + vy + 300) Mod 300
    End Sub
    Public Sub Paint(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.PaintEventArgs)
        Dim g As Graphics
        Dim pen As System.Drawing.Pen
        g = e.Graphics
        pen = New System.Drawing.Pen( _
           Color.FromArgb(&HFFFF0000))
        g.DrawRectangle(pen, x, y, w, h)
    End Sub
End Class
        

すると x 方向には動かなくなってしまう。なぜだろうか。

フォームにButton1とLabel1を置いて、以下のプログラムを実行してみよう。

Public Class Form1
    Dim x
    Private Sub proc1()
        Dim x
        x = 3
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
        x = 10
        proc1()
        Label1.Text = x
    End Sub
End Class

このプログラムをこのまま実行すると、Label1には10が表示される。赤字のDim xを 'Dim xとしてコメントに変更してから実行すると、Label1には3が表示される。

Debugモードで変数の動きをみてみよう。

Dim xを残した場合

Sub proc1が終了するとxの値が10に戻る。

Dim x をコメントにした場合

Sub proc1が終了してもxの値は3のままだ。

Dim x と変数の関係を図に示す。

Private Sub proc1 の中に Dim xがあると、01行目と03行目では名前はxで同じだが、別々の変数が作られる。そのため、04行目でxに3が代入されても、01行目で宣言した方のxの値は変わらない。

Dim xをコメントに書き換えると以下のようになる。

このようなルールのことを、「スコープ(視界)」と言う。スコープについて、すべてのルールをあげるとかなり複雑であるが、ここでは特によく使用するスコープについて簡単にまとめておく。

定義する場所 スコープ 同一の変数として参照される範囲 同一の値を保持する範囲
クラス定義よりも外側 名前空間スコープ そのプログラム内 そのプログラム内
クラス定義の内側でサブプロシージャの外側 モジュールスコープ そのクラス内 そのクラスのインスタンス内
サブプロシージャの最初の部分 プロシージャスコープ そのプロシージャ内 そのプロシージャの実行が開始してから終了するまで

Visual Studio のコードエディタを使用している場合は、スコープはだいたい同じインデントの範囲と覚えておけばよい。

最初に戻って、なぜ赤色のDim xを元の命令に戻すと、アニメーションが止まったかといえば、Dim xによって新しい変数が定義され、xに関する式ではx座標ではなく関係のない変数xを使って計算や代入が実行されたからだ。

さらに詳しく知りたい場合は、Visual StudioのHELPから以下の

「オブジェクトによるプログラミング: クラスを使用する」

を見よう。

 - 開発ツールと言語ドキュメント
 |  - Visual Studio ドキュメント
 |  |  + Visual Studioの紹介
 |  |  + Visual Studioの統合開発環境
 |  |    使用する技術とツールの決定
 |  |  + Windowsベースのアプリケーション、コンポーネント、サービス
 |  |  + Visual Studioでの.NET Framework プログラミング
 |  |  - VIsual Basic
 |  |  |  + 概要
 |  |  |  - Visual Basic ガイドツアー
 |  |  |  |  + Visual Basic Expressの概要
 |  |  |  |  + プログラムの外観の作成: Windowsフォームの概要
 |  |  |  |  + 問題の確認 : デバッグを行ってエラーを検出し、修正する方法
 |  |  |  |  + レコードの管理 : プログラムでデータを使用する
 |  |  |  |  - オブジェクトによるプログラミング : クラスを使用する