MOS VBAエキスパート対策 | プロシージャ | MOS Excel VBAエキスパート対策です



最終更新日:2018-03-09

プロシージャ


・引数の意味
・引数の宣言
・値渡しと参照渡し


【ここでのポイント】

ここで覚えることは、

Call
Sub
Function
ByVal
ByRef
Optional


たったこれだけですが、VBAの基本です。
ここがあやふやではマクロ作成などできません。


ですが、試験問題としては、
Functionの戻り値と、ByVal、ByRefでの動作違い、これにOptionalが関係してくるくらいでしょう。
逆に言えば、しっかり点を稼いでおく場所になると思います。


引数の意味

プロシージャーは適切な機能単位で作ります。
適切な機能単位で作られた複数のプロシージャを連続実行することで複雑な処理を実現します。

他のプロシージャーを呼び出すには、Callステートメントを使用します。

Callステートメント
[Call] name [argumentlist]

Callは、
省略可能なキーワードです。
指定するときは、引数リスト (引数 argumentlist) をかっこで囲む必要があります。

nameは、
必ず指定します。
呼び出すプロシージャの名前を指定します。

argumentlistは、
省略可能です。
呼び出すプロシージャーにデータを渡すための引数です。


Callステートメントのargumentlist、これが引数と呼ばれるものです。

引数は、呼び出し先のプロシージャーに渡すデータを指定するものです。
呼び出されたプロシージャーは、この引数を受け取って処理を実行します。
Callする時の引数が変化すれば、プロシージャーの結果が変わってきます。

制御先のプロシージャーが終了した後は、Callの次のステートメントに制御が移ります

Sub sample()
  Dim i As Long
  i = 1
  Call sample2(i)
  i = 2
  Call sample2(2)
End Sub

Sub sample2(i)
  Cells(i, 1) = i
End Sub

上記では、

i = 1 ・・・ sample
sample2へiを渡す ・・・ sample
引数iを受け取る ・・・ sample2
Cells(i, 1) = i ・・・ sample2
i = 2 ・・・ sample
sample2へiを渡す ・・・ sample
引数iを受け取る ・・・ sample2
Cells(i, 1) = i ・・・ sample2

この順に処理されます。
実行結果は、
A1セルに1、A2セルに2が入ります。


引数の宣言

呼び出されるプロシージャーには、
SubプロシージャーFunctionプロシージャーがあります・
それぞれ、
SubステートメントFunctionステートメントをになります。

SubプロシードャーとFunctionプロシージャーとの違いは、値を返すか返さないかの違いです。
Subプロシージャーは値を返しません。
Functionプロシージャーは値を返します。

Functionプロシージャーが返した値は、呼び出し元のプロシージャーが受け取ることが出来ます。
Functionプロシージャは、独自の関数を作るものだと理解してください。


Subステートメント
[Private | Public] Sub name [(arglist)]
[statements]
[Exit Sub]
[statements]


[Private | Public]は、スコープの指定です。
スコープと聞いて直ぐに理解できない場合は、ベーシックの変数と定数を見直してください。

[(arglist)]
これが引数です。
引数が無い場合や、あっても省略できる場合があります。

Functionステートメント
[Public | Private] [Static] Function name [(arglist)] [As type]
[statements]
[name = expression]
[Exit Function]
[statements]
[name = expression]

  ・・・
End Function

Exit Functionについては、ステートメントの章で解説します。

[As type]
戻り値のデータ型を指定できます。
省略した時は、Variant型になります。

使用例.
Sub sample1()
  Dim vrt1
  vrt1 = func1
  MsgBox vrt1
End Sub
Function func1()
  func1 = "func1の回答"
End Function

Functionの戻り型は省略しています。
Functionで値を戻す場合は、Functionのnameに対して値を代入します。
つまり、Functionプロシージャー名に値を入れる事で値を戻します。
上記のsample1を実行すると、"func1の回答"がメッセージ表示されます。

また、上記sample1は、
MsgBox func1
と1行で書く事ができます。
上記では、動作を理解しやすいように一度変数に入れています。

Sub sample2()
  MsgBox func2("Functionの", "サンプルです")
End Sub
Function func2(arg1, arg2) As String
  func2 = arg1 & arg2
End Function

Functionの戻り型をStringで指定しています。
上記のsample2を実行すると、"Functionのサンプルです"とメッセージ表示されます。

Sub sample3()
  MsgBox func3(100, 200)
End Sub
Function func3(arg1 As Long, arg2 As Long) As Long
  func3 = arg1 + arg2
End Function

Functionの戻り型をLong、引数の型もLongで指定しています。
上記のsample3を実行すると、"300"とメッセージ表示されます。

引数の構文
プロシージャの構文に書かれているarglistは次の形式で指定します。

[Optional] [ByVal | ByRef] [ParamArray] varname[( )] [As type] [= defaultvalue]

Optional

省略可能です。
指定した引数が省略可能であることを示します。
このキーワードを指定した場合、引数 arglist のそれ以降の引数も省略可能でなければならず、すべてキーワード Optional を付けて宣言する必要があります。
キーワード ParamArray を使った場合は、どの引数に対してもキーワード Optional は指定できません。

ByVal

省略可能です。
その引数が、値渡しで渡されることを示します。

ByRef

省略可能です。
その引数が、参照渡しで渡されることを示します。
既定値は ByRef です。

ParamArray

省略可能です。
引数 arglist の最後の引数でのみ使用できます。
その引数がバリアント型 (Variant) の要素を持つ省略可能 (Optional) な 配列であることを示します。
キーワード ParamArray を使うと、任意の数の引数を渡すことができます。
ByVal、ByRef、Optional の各キーワードと共に使うことはできません。

varname

必ず指定します。
引数を表す変数名を指定します。
変数の標準的な名前付け規則に従って指定します。

type

省略可能です。
プロシージャに渡す引数のデータ型を指定します。
バイト型 (Byte)、ブール型 (Boolean)、整数型 (Integer)、長整数型 (Long)、通貨型 (Currency)、単精度浮動小数点数型 (Single)、倍精度浮動小数点数型 (Double)、日付型 (Date)、文字列型 (String) 、オブジェクト型 (Object)、バリアント型 (Variant) のいずれかを指定できます。
パラメータにキーワード Optional が指定されていない場合は、ユーザー定義型またはオブジェクトの種類を指定することもできます。

defaultvalue

省略可能です。
任意の定数または定数式を指定します。
キーワード Optional を指定したパラメータに対してのみ有効です。
データ型がオブジェクト型 (Object) の場合、明示的な既定値は Nothing だけです。


ParamArrayは配列の引数になりますが、これについては解説を省略します。
実務ではほとんど使う事もありませんし、試験として出題されることもないはずです。

ByValは値渡し
ByRefは参照渡し
これらについては、次の段落で説明します。

Optional
Optionalがついていない引数は、Call時に省略できません、必ず指定する必要があります。



省略可能な引数を定義するには、Optionalキーワードを使います。

Optionalが指定された引数には、省略時の初期値を指定することが出来ます。

Sub sample()
  Call sample2("引数")
  Call sample2
  Call sample3
End Sub

Sub sample2(Optional sMsg = "初期値")
  MsgBox sMsg
End Sub

Sub sample3(Optional sMsg)
  If IsMissing(sMsg) Then
    sMsg = "省略"
  End If
  MsgBox sMsg
End Sub


sample2では、初期値を指定していますので、
Callで省略されたときは、その初期値を受け取ることになります。

sample3では、初期値を指定していないので、
IsMissing関数で、引数が省略されているかどうかを判定しています。

上記の実行結果は、
引数
初期値
省略
この順に、メッセージボッスに出力されます。

初期値を省略したOptionalは、実務では使う意味は特にありません。
特定の初期値を指定しておけば、それで判定できるからです。

引数に関するエラー
Callする側と呼び出される側で引数のデータ型が違う場合
ByValとByRefでエラーとなる状況が全く違ったものとなります。

ByValでデータ型が違う場合
呼び出される側のデータ型に型変換が行われます。

その時、データ型変換できないデータの場合は、エラーになります。


ByRefでデータ型が違う場合
文法エラーとなります。



配列全体を引数で渡す場合
配列全体を引数に指定する場合は、ByRefで宣言します。
ByValでは文法エラーとなります。

Sub sample(ByVal ary()) ・・・ エラー
Sub sample(ByRef ary()) ・・・ OK

ただし、以下の記述ならOKです。
Sub sample2(ByVal ary)
これなら、Callの引数に配列を指定できますので、
結果として配列をByValで渡していることになります。
実際には配列としてではなく、Variant(何でも入れられるので配列も入れられる)で受け取っているという事です。
ただし、さすがに試験には出ないと思います。

配列の個々の要素(インデックスを指定)はByValで渡すことが出来ます。
個々の要素は、Dimで宣言した1つの変数と同じものです。


値渡しと参照渡し

ByValが値渡し、ByRefが参照渡しです。

ByValの値渡しとは、
変数の中のデータを渡すもので、呼出側の変数は影響を受けません。
つまり、値渡しでは、呼び出し先で引数の値を変更しても、呼び出し元の引数は変更されません。

ByRefの参照渡しとは、
変数そのものを渡すもので、呼出側の変数が影響を受けます。
つまり、参照渡しでは、呼び出し先で引数の値を変更すると、呼び出し元の引数も変更されます。

ByVal、ByRefを省略した時は、ByRefとなります。


使用例を通じて、理解して下さい。

Sub sample1()
  Dim i, j
  i = 1
  j = 1
  Call sub1(i, j)
  MsgBox i
  MsgBox j
End Sub
Sub sub1(arg1, arg2)
  arg1 = arg1 + 1
  arg2 = arg2 + 2
End Sub

上記のsample1を実行すると、2,3の順にメッセージ表示されます。
ByValもByRef指定していないので、ByRefとなっています。
この場合、参照渡しとなり、Call元のプロシージャーの引数も変更されます。


Sub sample2()
  Dim i, j
  i = 1
  j = 1
  Call sub2(i, j)
  MsgBox i
  MsgBox j
End Sub
Sub sub2(ByVal arg1, ByVal arg2)
  arg1 = arg1 + 1
  arg2 = arg2 + 2
End Sub

上記のsample2を実行すると、1,1の順にメッセージ表示されます。
ByValを指定しているので、値渡しとなっています。
この場合、Call元のプロシージャーの引数は変更されません。

値渡し、参照渡しについては、理屈は難しいものではありません。
実際のVBAコードを見たときに、挙動を直ぐに理解できるかどうかが問題です。
サンプルコードを自分で書いて、ステップ実行しながら、変数の変化をしっかりと目で確認してください。
VBAコードを見るだけで、動作結果がわかるようになるまで繰り返し練習したほうが良いでしょう。


ByRefのひっかけ問題
Sub sample1()
  Dim i As Long
  i = 1
  sample2 (i)
  MsgBox i
End Sub

Sub sample2(ByRef i As Long)
  i = 2
End Sub

上記のMsgBoxの結果は、どうなるでしょうか?

答えは、
1
になります。

sample2 (i)
この括弧は、括弧内の式や変数を評価し、値を取得しています。
つまり、(i)は数値定数と同じことになります。
つまり、
sample2 1
を実行していることになります。
例えByRefでも、定数は変更されません。

さすがに、こんな問題は出ないとは思いますが、
実務では、稀に見かけるバグになります。

引数にオブジェクトを指定した時の注意
引数にオブジェクトを指定した場合には注意が必要です。

Sub sample()
  Dim MyRange As Range
  Set MyRange = Range("A1")
  Call sample2(MyRange)
End Sub
Sub sample2(ByVal MyRange As Range)
  MyRange.Value = 1
End Sub

上記の結果は、A1セルに1が入ります。

当然の結果です。
つまり、
ByValで渡されたオブジェクトに変更を加えれば、呼出元でもオブジェクトは変更されています。
Range以外の他のオブジェクトでも同じことです。

オブジェクトを引数とした場合、ByValとByRefの違いは何かについてですが、
呼び出されたプロシージャー内で、
引数の変数に対して、Setステートメントで新たなオブジェクトを入れた場合に、
その新たなオブジェクトが、呼び出し元に影響を与えるかどうかになります。
ByValなら呼び出し元は影響を受けず、ByRefなら呼び出し元は影響を受けます。

引数にオブジェクトを指定する問題は、試験にも出る可能性は十分にありますが、
その時の、値渡し、参照渡しの違いについてまでは出題されないと思います。


【業務改善の実務】

実務で、一定規模以上のマクロを開発する時は、
プロシージャーを適切な機能単位で作成することが肝になります。
そして、その時の引数の使い方こそ、VBAスキルが現れてくる部分になってきます。

引数の変更をしていないのに、ByRefで定義したり省略していたりするのは、
VBAの質としては、良くないものという事になります。
マクロを書く上では、省略して全てByRef扱いで作成することも可能ですが、
それは、動作結果の正否の問題ではなく、
そのプロシージャーが何をしているかを伝える事を怠っていることで、読みづらいVBAという事になります。

ByVal、ByRefを適切に宣言することは、プロシージャの質を高めることになります。


【本サイト内の関連ページ】





同じテーマ「MOS VBAエキスパート対策」の記事

デバッグデの基礎
マクロの実行
VBAベーシック試験対策まとめ
プロシージャ
イベント
ステートメント(スタンダード)
関数
エラーへの対処
APIとOLEオートメーション
変数と配列
レジストリの操作

新着記事 ・・・新着記事一覧を見る

エクセルVBAでのシート指定方法|VBA技術解説(9月8日)
VBAのクラスとは(Class,Property,Get,Let,Set)|VBA技術解説(8月28日)
VBAこれだけは覚えておきたい必須基本例文10|VBA技術解説(8月22日)
VBAの省略可能な記述について|ExcelマクロVBA技術解説(8月11日)
複数条件判定を行う時のコツ|ExcelマクロVBA技術解説(7月11日)
For Next の使い方いろいろ|VBA技術解説(6月14日)
VBAを定型文で覚えよう|ExcelマクロVBA技術解説(3月26日)
VBAスタンダード試験対策まとめ|MOS VBAエキスパート対策(3月16日)
ユーザーフォームとメニューの操作|MOS VBAエキスパート対策(3月14日)
ファイルの操作|MOS VBAエキスパート対策(3月14日)

アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.変数とデータ型(Dim)|ExcelマクロVBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.RangeとCellsの使い方|ExcelマクロVBA入門
6.定数と型宣言文字(Const)|ExcelマクロVBA入門
7.マクロって何?VBAって何?|ExcelマクロVBA入門
8.とにかく書いて見よう(Sub,End Sub)|VBA入門
9.繰り返し処理(For Next)|ExcelマクロVBA入門
10.ひらがな⇔カタカナの変換|エクセル基本操作




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
なお、掲載のVBAコードは自己責任で使ってください。万一データ破損等の損害が発生しても責任は負いません。





このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。

本文下部へ

↑ PAGE TOP