プロシージャ
注意:
この文書は以前「.NETでいきまっしょい!」で公開していたものですが、公開以降メンテナンスされていません。 今や古い情報となった内容が記載されている場合があるのでご注意ください。
プロシージャの概要
プロシージャはC/C++言語で言う関数のことであり、Pascalでは値を返さない関数を手続きと呼んでいま。 VB.NETのプロシージャは「手続き」という意味ですが、値を返すか返さないかに関わらずこのように呼ばれます。 モジュールではプロシージャと呼ばれますが、クラスや構造体に属するプロシージャはメソッドと呼ばれます。 プロシージャにはよく使われる一連の処理の流れを記述します。 このようにすることで何度も同じコードを書く手間を減らすことができます。
VB.NETのプロシージャには値を返さないSubプロシージャと、値を返すFunctionプロシージャが存在します。 また、プロパティ構文を成すプロシージャをプロパティプロシージャと言います。 プロシージャは値を返すか否かに関わらず、任意の数・任意の型の引数を取ることができます。 引数はプロシージャに対して渡す情報ととらえることができます。
プロシージャの宣言方法はVB6以前とほとんど変わりありませんが、一部変更になった部分もあります。 まず、その変更点をまとめておきます(すべてVB.NETでの仕様です)。
・引数の渡し方は既定でByValとなる
・ByRefで渡されたプロパティ引数は、プロパティでの変更が反映される
・Subプロシージャを呼び出す場合、常に引数を括弧でくくる必要がある
・Returnキーワードを用いてプロシージャの呼び出し元に戻ることができる
・Returnキーワードを使用してFunctionプロシージャの戻り値を返すことができる
・Static修飾子を指定してプロシージャを宣言することはできない
宣言・呼び出し
Subプロシージャ、Functionプロシージャの宣言の例をまとめておきます。 実装は省略します。
001 002 003 004 005 006 007 008 009 010
011 012 013 014
|
Sub TestProcedure1()
End Sub
Sub TestProcedure2(ByVal arg As Integer)
End Sub
Sub TestProcedure3(ByRef arg1 As Integer, ByVal arg2 As Double)
End Sub
Function TestProcedure4() As Integer
End Function
Function TestProcedure5(ByVal arg As String) As Integer
End Function
|
これらのプロシージャを呼び出す場合は次のようにします。 VB.NETでは引き続きCallでプロシージャを呼び出すことができます。 この場合、Functionプロシージャの戻り値は破棄されます。
001 002 003 004 005 006 007 008 009
|
TestProcedure1()
TestProcedure2(15)
Call TestProcedure3(2, 148.0)
TestProcedure4()
Call TestProcedure5("Test")
|
戻り値の返却・受け取り
戻り値の返し方には2パターンあります。 Returnを用いた場合はその時点でプロシージャの処理が完了します。 また、プロシージャ名に戻り値を代入する場合はEnd Functionまで処理は継続されます。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015
|
Function FunctionProcedure1() As Integer
Return 0
' これ以降の処理は実行されない
End Function
Function FunctionProcedure2() As Integer
FunctionProcedure2 = 10
' これ以降の処理も実行される
End Function
|
Functionプロシージャの戻り値を受け取るには、戻り値の型と同じ型の変数を用意し、その変数に代入するようにします。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017
|
Sub Main()
Dim returnValue As Integer
' 戻り値の受け取り
returnValue = GetRandomValue()
Console.WriteLine("戻り値: {0}", returnValue)
End Sub
Function GetRandomValue() As Integer
' 10未満のランダムな数値を返す
Return CInt(Rnd() * 10)
End Function
|
また、式の途中で戻り値を直接使用することもできます。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017
|
Sub Main()
Dim returnValue As Integer
' 戻り値を10倍する事で 0, 10, 20 .... 90 のランダムな数値を取得する
returnValue = 10 * FunctionProcedure()
Console.WriteLine("戻り値: {0}", returnValue)
End Sub
Function FunctionProcedure() As Integer
' 10未満のランダムな数値を返す
Return CInt(Rnd() * 10)
End Function
|
プロシージャの中断
ReturnおよびExit Sub、Exit Functionを使用することでプロシージャの処理を中断することができます。 FunctionプロシージャではReturnは戻り値を返すために使用されますが、返すと同時にそれ以降の処理も中断します。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023 024 025 026 027 028 029 030
031 032
|
Sub SubProcedure()
Dim i As Integer
For i = 0 To 10000
' 半分までループしたら処理を中断します
If i = 5000 Then Exit Sub
' 上のコードと全く等価です
If i = 5000 Then Return
Next
End Sub
Function FunctionProcedure() As Integer
Dim i As Integer
For i = 0 To 10000
' 半分までループしたら処理を中断します
If i = 5000 Then Exit Function
' 上のコードとほぼ等価です
' Returnでは戻り値を返す必要があります
If i = 5000 Then Return i
Next
End Function
|
ByValとByRef
引数の渡し方には値渡しと参照渡しの二種類があります。 引数リストの宣言でByValを付加すると値渡し、ByRefを付加すると参照渡しで変数の値が渡されます。 値渡しでは引数に値のコピーが渡されるため、プロシージャの中で変更を加えても元の変数の値が変わることはありません。 逆に参照渡しでは、変数の格納されている位置を渡すため、プロシージャの中での変更は元の変数にも影響します。 次のコードは値渡しと参照渡しでの値の変化を比較するためのものです。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023 024 025 026 027
|
Sub Main()
Dim val As Integer
val = 7
ByValProcedure(val)
Console.WriteLine("ByVal Value: {0}", val)
val = 7
ByRefProcedure(val)
Console.WriteLine("ByRef Value: {0}", val)
End Sub
Sub ByValProcedure(ByVal arg As Integer)
arg = 15
End Sub
Sub ByRefProcedure(ByRef arg As Integer)
arg = 15
End Sub
|
| 出力結果 |
ByVal Value: 7
ByRef Value: 15
|
参照渡しのパラメータでも元の変数の値を変更させない場合は次のように値を括弧でくくって渡します。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016
|
Sub Main()
Dim val As Integer
val = 7
ByRefProcedure((val))
Console.WriteLine("ByRef Value: {0}", val)
End Sub
Sub ByRefProcedure(ByRef arg As Integer)
arg = 15
End Sub
|
| 出力結果 |
ByRef Value: 7
|
省略可能な引数
VB.NETではプロシージャの引数を省略可能にすることができます。 省略可能な引数は宣言部にOptionalをつけますが、Optionalをつけた引数より後ろにある引数にもOptionalをつけなければなりません。 VB6以前でも省略可能な引数を取ることができましたが、VB.NETでは省略可能な引数には必ず既定値を定めなければなりません。 そのため、IsMissing関数などは存在しません。 次の例は省略可能な引数リストを持つプロシージャとそれを呼び出した例です。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023
|
Sub Main()
' すべて指定
SubProcedure(4, 3, 2, 1)
' x を省略
SubProcedure(10, , 20, 30)
' y, z を省略
SubProcedure(5, 10, , )
' x, y, z をすべて省略
SubProcedure(0, , , )
End Sub
' wは必須、x, y, zは省略可能です
Sub SubProcedure(ByVal w As Integer, Optional ByVal x As Integer = 2, _
Optional ByVal y As Integer = 7, Optional ByVal z As Integer = 15)
Console.WriteLine("w:{0}, x:{1}, y:{2}, z:{3}", w, x, y, z)
End Sub
|
| 出力結果 |
w:4, x:3, y:2, z:1
w:10, x:2, y:20, z:30
w:5, x:10, y:7, z:15
w:0, x:2, y:7, z:15
|
任意数の引数
VB.NETではプロシージャに任意の数のパラメータを渡すこともできます。 渡される引数の数がわからないときはParamArrayを指定し、なおかつこの引数は受け取る値の型に合った配列でなければなりません。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023 024
|
Sub Main()
' 3つ指定
SubProcedure(2, 3, 5)
' 5つ指定
SubProcedure(1, 3, 5, 7, 9)
End Sub
Sub SubProcedure(ByVal ParamArray values() As Integer)
Dim value As Integer
' すべて列挙
For Each value In values
Console.Write("{0} ,", value)
Next
Console.WriteLine()
End Sub
|
| 出力結果 |
2 ,3 ,5 ,
1 ,3 ,5 ,7 ,9 ,
|
オーバーロード
VB.NETからはプロシージャをオーバーロードすることができるようになりました。 オーバーロードとは同じプロシージャの名前で異なる引数リストのプロシージャを作成することです。 たとえば、いくつかの数値型の絶対値を求めるためのプロシージャを自作するとなると、VB6では次のようなコードを組んでいました。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023 024 025 026 027 028 029
|
' Integer型のためのプロシージャ
Function AbsInt(val As Integer) As Integer
' 正の時はそのまま
If 0 <= val Then
AbsInt = val
' 負の時は符号反転
Else
AbsInt = -1 * val
End If
End Function
' Single型のためのプロシージャ
Function AbsSng(val As Single) As Single
' 実装は省略
End Function
' Double型のためのプロシージャ
Function AbsDbl(val As Double) As Double
' 実装は省略
End Function
|
実際にこれを呼び出す場合は、変数の型を考慮しながら呼び出すプロシージャを選択しなければなりませんでした。 しかし、プロシージャをオーバーロードすると同じ名前で異なる機能を持ったプロシージャを複数宣言することができます。 たとえば、この絶対値を求めるプロシージャの一群をAbsという一つのプロシージャとしてオーバーロードすると次のようになります。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016 017 018 019 020
021 022 023 024 025 026 027 028 029 030
031 032 033 034 035 036 037 038 039
|
' Integer型のためのプロシージャ
Function Abs(ByVal val As Integer) As Integer
If 0 <= val Then
' 正の時はそのまま
Return val
Else
' 負の時は符号反転
Return -1 * val
End If
End Function
' Single型のためのプロシージャ
Function Abs(ByVal val As Single) As Single
If 0.0! <= val Then
' 正の時はそのまま
Return val
Else
' 負の時は符号反転
Return -1.0! * val
End If
End Function
' Double型のためのプロシージャ
Function Abs(ByVal val As Double) As Double
If 0.0# <= val Then
' 正の時はそのまま
Return val
Else
' 負の時は符号反転
Return -1.0# * val
End If
End Function
|
これらのプロシージャを呼び出す場合は普通のプロシージャの呼び出しと何ら変わりありません。 ただ、渡されたパラメータの型に合わせてコンパイラが自動的にどれを呼び出すかを決定します。
001 002 003 004 005 006 007 008 009 010
011 012 013 014 015 016
|
Sub Main()
' Integer のバージョンが呼び出されます
Console.WriteLine(Abs(-3))
' Single のバージョンが呼び出されます
Console.WriteLine(Abs(5.0!))
' Double のバージョンが呼び出されます
Console.WriteLine(Abs(-3.14#))
' Double のバージョンが呼び出されます
' なぜならこの数値はDoubleとして扱われるからです
Console.WriteLine(Abs(2.71828))
End Sub
|
幸い、すべての型についてAbs()プロシージャを定義しなくても、Math.Abs()メソッドが提供されているのでこれを使うことができます。
オーバーロードに際して、そのプロシージャがオーバーロードされていることを明示的に示す目的でOverloadsキーワードを付加することができます。 一つのプロシージャにOverloadsキーワードを付加した場合、ほかのオーバーロードされるべきプロシージャにもこのキーワードをつけなければなりません。
001 002 003 004 005 006 007 008
|
Overloads Function Abs(ByVal val As Integer) As Integer
End Function
Overloads Function Abs(ByVal val As Single) As Single
End Function
Overloads Function Abs(ByVal val As Double) As Double
End Function
|
プロシージャをオーバーロードする場合、当然プロシージャ名は同じでなければなりません。 また、引数の数・順序・型が一つでも異なっていないとオーバーロードすることはできません。 戻り値の型や引数の名前の違いだけではオーバーロードできません。 ちなみに、プロシージャの名前と引数の数・順序・型の要素をまとめてシグネチャといいます。