プロシージャとはC/C++言語で言う関数のことです。 プロシージャには「手続き」という意味があり、VBでは一連の処理をまとめたものという意味合いで使われます。
VBではモジュールに属するものは特にプロシージャと呼ばれ、一方クラスや構造体に属するものはメソッドと呼ばれ、両者は区別される事が多いようです。 なお、値を返さないSubプロシージャはサブルーチン、値を返すFunctionプロシージャは関数(ファンクション)という用語で区別されることもあるようです。 VBのキーワードSub
とFunction
はこれに由来します。
プロシージャにはよく使われる一連の処理の流れを記述します。 これにより何度も同じコードを書く手間を減らすことができます。
VB.NETでのプロシージャとVB6からの変更点
VB.NETのプロシージャには値を返さないSubプロシージャと、値を返すFunctionプロシージャが存在します。 両者の違いは値を返すかどうかだけです。 また、プロパティ構文を成すプロシージャをプロパティプロシージャと言います。
プロシージャは値を返すか否かに関わらず、任意の数・任意の型の引数を取ることができます。 引数はプロシージャに対して渡す情報ととらえることができます。
プロシージャの宣言方法はVB6以前とほとんど変わりありませんが、一部変更になった部分もあります。 まず、その変更点をまとめておきます(すべてVB.NETでの仕様です)。
- 引数の渡し方は既定でByVal(値渡し)となる
- ByRef(参照渡し)で渡されたプロパティ引数は、プロパティプロシージャ内での変更が反映される
- Subプロシージャを呼び出す場合、常に引数を括弧でくくる必要がある
- Returnキーワードを用いてSubプロシージャの呼び出し元に戻ることができる
- Returnキーワードを用いてFunctionプロシージャの戻り値を返すことができる
- Static修飾子を指定してプロシージャを宣言することはできない
宣言・呼び出し
Subプロシージャ、Functionプロシージャの宣言の例をまとめておきます。
これらのプロシージャを呼び出す場合は次のようにします。 VB.NETでは引き続きCall
でプロシージャを呼び出すこともできますが、一般的にはあまり使われていないようです。 CallでFunctionプロシージャを呼び出した場合は、戻り値は特に代入されず破棄されます。
戻り値の返却・受け取り
Functionプロシージャでの戻り値の返し方には2パターンあります。 1つはReturn
で戻り値を返すパターン、もう1つはプロシージャ名に戻り値を代入するパターンです。
Return
を用いた場合はその時点でプロシージャの処理が完了します。 逆に、プロシージャ名に戻り値を代入する場合はEnd Function
まで処理は継続されます。
Functionプロシージャの戻り値を受け取るには、戻り値の型と同じ型の変数を用意し、その変数に代入するようにします。
また、式の途中にFunctionプロシージャの呼び出しを含めることで、その戻り値を直接使用することもできます。
プロシージャの中断
Return
およびExit
ステートメント(Exit SubまたはExit Function)を使用することでプロシージャの処理を中断することができます。 FunctionプロシージャではReturnは戻り値を返すために使用されますが、戻り値を返すと同時にそれ以降の処理も中断します。
ExitステートメントでFunctionプロシージャの処理を中断した場合、プロシージャ名に戻り値を代入している場合はその値、代入していない場合はデフォルトの値(参照型ではNothing、値型では0など)が戻り値として返されます。
参照型・値型のデフォルト値については型の種類・サイズ・精度・値域 §.型のデフォルト値を参照してください。
ByValとByRef
引数の渡し方には値渡しと参照渡しの二種類があります。 引数リストの宣言でByVal
を指定すると値渡し、ByRef
を指定すると参照渡しで変数の値が渡されます。
値渡しでは引数に値のコピーが渡されるため、プロシージャの中で変更を加えても元の変数の値が変わることはありません。 逆に参照渡しでは、変数に格納されている値への参照を渡すため、プロシージャの中での変更は元の変数にも影響します。
次のコードは値渡しと参照渡しでの値の変化を比較するためのものです。
参照渡しのパラメータでも元の変数の値を変更させたくない場合は、次のように値を括弧( )
でくくって渡すようにします。
なお、値のコピーとインスタンスのコピーを混同しないようにしてください。 値型では値のコピーとインスタンスのコピーは同じですが、参照型では値のコピーとは同じインスタンスを参照するようにすることで、インスタンスのコピーとは1つのインスタンスからその複製を作ることです。 参照型の値渡し(ByVal)で渡されるのはどのインスタンスを参照するかという情報のコピーであり、参照渡し(ByRef)で変化するのは変数が参照するインスタンスです。
値型と参照型の違いについてはクラスで解説しています。
省略可能な引数
VB.NETではプロシージャの引数を省略可能にすることができます。 Optional
キーワードを付けることで引数を省略可能とすることができますが、Optional
をつけた引数より後ろにある引数にもOptional
を付ける必要があります。
VB6以前でも省略可能な引数を取ることができましたが、VB.NETでは省略可能な引数には必ず既定値を定めなければなりません。 そのため、引数が設定されているかを調べるIsMissing関数などはVB.NETには存在しません。
次の例は省略可能な引数リストを持つプロシージャとそれを呼び出した例です。
可変長の引数
VB.NETではプロシージャに任意の数の値をパラメータとして渡すこともできます。 渡される引数の数がわからないときや任意の数を指定することを許可する場合には、ParamArray
を指定することで引数を可変長にすることができます。 なお、ParamArray
を指定する可変長引数はすべて同じ型でなければならないため、引数を配列として受け取るようにする必要があります。
可変長引数を取るプロシージャを呼び出す際、任意個の値を指定できるだけでなく、全く値を指定しないで呼び出すこともできます。
オーバーロード
VB.NETからはプロシージャをオーバーロードすることができるようになりました。 オーバーロードとは、同じ名前で引数リストだけが異なるプロシージャを作成することです。
たとえば、いくつかの数値型の絶対値を求めるためのプロシージャを自作するとなると、VB6では次のようなコードを書く必要がありました。
さらに、実際にこれを呼び出す場合は、変数の型を考慮しながら呼び出すプロシージャを選択しなければなりませんでした。 しかし、VB.NETではプロシージャをオーバーロードすることで、この問題を回避することができます。
たとえば、この絶対値を求めるプロシージャの一群をAbsという一つのプロシージャとしてオーバーロードすると次のようになります。
これらのプロシージャを呼び出す場合は普通のプロシージャの呼び出しと何ら変わりありません。 渡されたパラメータの型に合わせて、最適なものを呼び出すようコンパイラが自動的に判断します。
幸い、この例のようにすべての型についてAbsプロシージャを定義しなくても、すでにMath.Absメソッドが提供されているのでこれを使うことができます。
オーバーロードに際して、そのプロシージャがオーバーロードされていることを明示的に示す目的でOverloads
キーワードを付加することができます。 一つのプロシージャにOverloadsキーワードを付加した場合、ほかのオーバーロードされるべきプロシージャにもこのキーワードをつけなければなりません。
プロシージャをオーバーロードする場合、すべてのプロシージャ名は同じでなければなりません。 また、オーバーロードする場合は引数の数・順序・型のいずれか一つが他と異なっている必要があります。 つまり戻り値の型や引数の名前の違いだけではオーバーロードできません。 ちなみに、プロシージャの名前と引数の数・順序・型の要素をまとめてシグネチャ(もしくはシグニチャ、signature)といいます。
引数の数・順序・型が同じで、戻り値の型だけがことなるプロシージャを複数作成したい場合は、名前を変えて別名のプロシージャとして作成する必要があります。 なお、そういったプロシージャではオーバーロードではなくジェネリクスを使った実装が適している場合もあります。
外部DLL、Win32 APIの呼び出し
VB6以前では、Declareステートメントを使って外部DLLやWin32 APIを呼び出すためのプロシージャを宣言することが出来ました。 VB.NETでも引き続きDeclareステートメントを使うことが出来ますが、これとは別にDllImport属性を使うことでも外部DLLの関数を呼び出すためのプロシージャを宣言することが出来ます。 次の例では、いずれもWin32 APIのLockWorkStation関数を呼び出すためのプロシージャを宣言しています。
DllImport属性や外部DLLの呼び出しとプロシージャの宣言については詳しく解説しないので、必要に応じて他のドキュメントや属性とメタデータなどを参照してください。
その他のプロシージャ
VB.NETではSub
やFunction
以外にも特別な役割を与えられるプロシージャが存在します。 例えば、プロパティ構文となるプロパティプロシージャや、演算子のオーバーロードを実現する演算子プロシージャなどです。 これらのプロシージャの構文や使い方・実装方法についてはプロパティ、演算子のオーバーロードを参照してください。