Visual Basic 初級講座 |
Visual Basic 中学校 > 初級講座 >
第8回 もっと変数
今回は「変数」について掘り下げます。変数が有効である範囲、変数の寿命などを説明します。このあたりのことを理解していないといろいろと苦労しますので一読していただけると幸いです。
この回の要約 ・変数の適用範囲(スコープ) ・変数の寿命 ・同じ名前の変数がある場合は適用範囲の狭いほうの変数が対象となる。 ・文字列型の変数は値を代入しないとメソッドやプロパティが使えない。 |
変数は宣言されている場所でのみ有効です。この有効な場所のことを適用範囲(てきようはんい)、またはスコープと呼びます 。
適用範囲に関する事柄は特にり難しいことはありませんが、ちゃんと知っておかないといろいろと苦労します。この機会に適用範囲をしっかり押さえておきましょう。
まず、適用範囲にはいくつか種類があります。つまり、『変数が有効な場所』の決め方が何パターンかあるということです。初級講座の現段階では次の種類を覚えれば十分でしょう。
クラスレベル
プロシージャレベル
ブロックレベル
|
これ以外の適用範囲も存在しますが、それについては徐々に説明する予定です。VBでよく見かけるPrivateやPublicなどのキーワードが実は適用範囲を示しているのですが今回はこれらのキーワードには触れませんので、ひとまず気にしないでおいてください。
何事も順番に習得していった方が無難だとは思いますが、PrivateやPublicなども含めた適用範囲については図解基礎解説 宣言の効果でまとめているので必要に応じて参照してください。ただし、現時点までの知識では理解困難です。 |
話を戻します。以下はクラスレベル・プロシージャレベル。ブロックレベルの変数の例です。このサンプルはVB2005のものですが、VB.NET2002, VB.NET2003でも2行ほど余分なものが入る以外全く同じです。VB2005の方が構造がすっきりしてい分かりやすいので紹介しているまでです。
Public
Class Form1 Dim X As Integer '←この変数Xはクラスレベルの変数 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Y As Integer '←この変数Yはプロシージャレベルの変数
If X = Y
Then End SubEnd Class |
■リスト1
サンプルを見れば一目瞭然(いちもくりょうぜん=見れば分かること)なのですが、レベルの違いというのは単に宣言する場所が違うだけです。
それでは、一つずつ具体例をまじえて見て行きましょう。
ブロックとはIf 〜 End If、 For 〜 Next など意味のあるプログラム構造を指します。これにはまだ登場していないものも含めて次のものがあります。
ブロックを作るキーワード |
If文のIf節・ElseIf節・Else節 |
For 〜 Next |
Do 〜 Loop |
While 〜 End While |
Select Case文のCase節 |
Try文のTry節・Catch節・Finally節 |
Using 〜 End Using |
PropertyプロシージャのGet節・Set節 |
これらのブロック内で宣言した変数はブロックレベルの変数となりそのブロック内でのみ有効となり、ブロックの外で使うことはできません。複数のブロックで同じ変数を使いたい場合はプロシージャレベルの変数として宣言します。
具体的として次の図を見てください。
■図1
この図には3つのブロックが登場しています。If文が2つのブロックに分かれている点に注意してください。
変数XとFileNameブロックの外側で宣言されているのでどのブロックの中でも外でも使うことができます。一方2つ目のブロックで宣言されている変数UserNameはブロック内で宣言されているためにこのブロックでしか使えません。
なお、紛らわしいのですが、異なるブロックで同じ名前の変数を宣言した場合、それぞれの変数はたとえ名前が同じであっても違う変数とみなされます。これは変数の適用範囲の一般的な効果です。言い換えると「適用範囲が違うなら名前が同じでも違う変数」となります。間違いの元となりますのでブロックレベルではあまり同じ名前の変数を付けないことをお勧めします。
ブロックレベルの適用範囲が理解できればプロシージャレベルの適用範囲も同じように理解できます。
つまり、プロシージャ内で宣言した変数はそのプロシージャ内でのみ有効で、他のプロシージャで使うことはできません。複数のプロシージャで同じ変数を使いたい場合はクラスレベルの変数として宣言します。
「プロシージャ」に関しては図解基礎解説 コード編集の基礎用語に画像付きで説明していますのでそちらをご覧下さい。
ここでも簡単に触れておきますと、プロシージャとは次のコードによって囲まれた範囲を指します。
VB.NET2003以降ではプロシージャとプロシージャの間には区切り線が入るので見た目で分かりやすくなっています。
プロシージャを作るキーワード |
Sub 〜 End Sub |
Function 〜 End Function |
Property 〜 End Property |
クラスレベルの適用範囲は他のレベルの適用範囲と考え方が少し違います。
クラスレベルの変数の場合はDim以外にPrivate, Protected, Public, Friendで宣言することができます。
この場合DimとPrivateは同じ意味で、これらで宣言した変数はそのクラス内でのみ使用できて他のクラスで使うことはできません。ですからブロックレベルの適用範囲やプロシージャレベルの適用範囲と同じ考え方です。
Protected、Public、Friendで宣言した場合はそのクラス内はもちろん他のクラスからもその変数を使うことができます。この場合の詳しい説明はまたの機会に譲ります。
なお、ここでは「クラス」と表現していますが構造体でも事情は同じです。
また、まだ登場していませんが標準モジュールという例外があります。これもまたの機会に説明します。
適用範囲の重要な効果に変数の寿命があります。変数はプログラムの実行が適用範囲を抜けた時点でいったん消滅し、再び適用範囲に入った時点でまた再生成されます。
次の例で考えて見ましょう。
Private
Sub Button1_Click(ByVal
sender As System.Object,
ByVal e As System.EventArgs)
Handles Button1.Click Dim Count As Integer Count += 1 MsgBox(Count) End Sub |
■リスト2
これの例ではButton1がクリックされると変数Countに1を足して、その値を表示します。Count += 1 とはCountに1を足す計算を表していて、これは Count = Count + 1 と書いても同じです。
さて、このプログラムは何度Button1をクリックしても必ず 1 を表示されます。Countに1を足していくのですから、表示される値は1, ,2 3, …と増えていっても良さそうな気もしますが、そこが変数の寿命です。
変数Countは適用範囲を抜けた時点で消滅して、再びButton1がクリックされた時点でまた新しく作成されているのです。
この例ではせっかく 1 を足しても End Sub の所で適用範囲が終了しますので変数Countは消滅します。
もし、Countを毎回1, ,2 3…と増やたいならば、Countの宣言をクラスレベルに変更しましょう。たとえば、次のようにすると変数Countの値は毎回1, ,2 3…と増えていきます。
Public Class Form1 Inherits System.Windows.Forms.Form
Dim Count As IntegerPrivate Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.ClickCount += 1 MsgBox(Count) End Sub End Class |
■リスト3
Public
Class Form1 Dim Count As Integer Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Count += 1 MsgBox(Count) End Sub End Class |
■リスト4
実はプロシージャレベルでもキーワードStatic(読み方:Static=スタティック)を使うことにより変数の寿命を変更することができます。Staticで宣言された変数はクラスが消滅するまで有効です。
Private
Sub
Button1_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
Button1.Click Static Count As Integer Count += 1 MsgBox(Count) End Sub |
■リスト5
プログラム中で同じ名前の変数が存在することはありえます。たとえば、適用範囲がまったく異なっている場合は前にも説明したように同じな名前の変数でもまったく別の変数として扱われます。 このことを「適用範囲によるシャドウ」と呼ぶことがあります。
適用範囲がまったく異なる場合はそれほど困らないのですが、適用範囲が重なっている場合は戸惑う場合があります。
たとえば、クラスレベルでPointという変数を宣言しているのに、プロシージャレベルでもPointという変数を宣言した場合、プロシージャ内でPoint += 100と書いた場合、どちらの変数に100が加算されるのでしょうか?
VBでは同じ名前の変数がある場合、より適用範囲が狭いほうの変数が対象となります。
VB2005の例で説明します。繰り返しますが、VB.NET2002でもVB.NET2003でも余計な行が2行増えるだけで後はまったく同じです。
Public
Class Form1 Dim Point As Integer 'クラスレベル Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Point As Integer 'プロシージャレベル Point += 100 MsgBox(Point) End Sub End Class |
■リスト6
この例では値に 100 が加算されるのは プロシージャレベルの Point変数です。
その証拠に何度Button1をクリックしても 100 を表示されます。
このケースでクラスレベルのPoint変数の方に100を加算したければ次のようにMe.をつけて書きます。
Public
Class Form1 Dim Point As Integer 'クラスレベル Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Point As Integer 'プロシージャレベル Me.Point += 100 MsgBox(Me.Point) End Sub End Class |
■リスト7
この例では MsgBoxの行にもMe.が付いていることに注意してください。ここにMe.を付け忘れるといつも 0 を表示されてしまいます。
ともかく紛らわしいのでできるだけ違う名前の変数を付けたほうが良いでしょう。
なお、ブロックレベルの変数はプロシージャレベルの変数と同じ名前にすることはできません。
最後に文字列型の注意点を説明します。文字列型は他の型と違って何かの値を代入しない限り、メソッドやプロパティを使うことができません。
以前からもこの点は注意してきたのですが、ここではちゃんと説明しましょう。
実は、文字列型は他の型と違う特別な型なのです。何が違うかというと他のIntegerやDecimalやBooleanといった型と違って文字列型は「クラス」なのです。
そのため、宣言しただけでは実体化(インスタンシング)されていないため使うことができません。
たとえば、以前にも登場した例ですが、次の例はエラーになります。
Dim
St As
String MsgBox(Len(St)) MsgBox(St.Length) 'この行でエラーになります。 |
■リスト8
この例では文字列型の変数Stには値が何も代入されていないのに、3行目では文字列型のプロパティ Length を使用しています。そこでエラーとなります。
この現象を回避するためには、自分でしっかり文字列型に値が代入されてからメソッドやプロパティを使うようにするか、メソッドやプロパティを使わないか、宣言と同時に空文字を代入するか、メソッドやプロパティを使う前に値が代入されているかチェックするかします。
宣言と同時に空文字を代入するのはわかりやすいですね。次の例はエラーになりません。
Dim
St As
String
= "" MsgBox(St.Length) |
■リスト9
メソッドやプロパティを使う前に値が代入されているかチェックするには次の例のようにIsNothing(読み方:IsNothing = イズナッシング)を使います。この例はVB2005では警告扱いになるのであまり気持ちよくありません。なお、この例を実行した場合は何も表示されないのが正常です。
Dim
St As
String
If IsNothing(St) =
False
Then
MsgBox(St.Length) End If |
■リスト10
そもそもメソッドやプロパティを使わなければ良いという発想に立つと次のように書くことができます。私はこの方法を採用しています。
Dim
St As
String MsgBox(Len(St)) |
■リスト11