Sub と Function の分け方(引数 ByVal・ByRef、自作関数をシートで使う)
長くなったマクロを部品に分けるための Sub と Function の使い分け、引数の ByVal と ByRef の違い、自作関数をワークシートの数式から使う方法と制限を、実務の例で解説します。
どんな場面で役に立つか
最初は数十行だったマクロも、機能を追加するうちに1つの Sub が数百行になり、どこで何をしているか分からなくなりがちです。同じ「消費税込みの金額を計算する」処理が何か所にもコピーされていると、税率や端数処理のルールが変わったときに修正漏れが起きます。
繰り返し使う処理や、意味のまとまりがある処理を Sub や Function に切り出しておくと、修正は1か所で済み、テストもしやすくなります。この記事では、その分け方の基本を説明します。
Sub と Function の違い
Sub は「処理を実行する」ための入れ物で、値を返しません。Function は「計算して結果を返す」ための入れ物で、関数名に値を代入するとそれが戻り値になります。目安として、シートへの書き込みやファイル操作など何かを行う処理は Sub、入力から結果を計算するだけの処理は Function にすると、役割がはっきりします。
次の例では、税込金額の計算を Function に、明細への書き込みを Sub に分けています。税率や端数処理を変えるときは Function だけを直せば済みます。
Option Explicit
' 税込金額を返す(端数は切り捨て)
Function TaxIncluded(ByVal amount As Currency, Optional ByVal rate As Double = 0.1) As Currency
TaxIncluded = Int(amount * (1 + rate))
End Function
' 明細のE列(税抜金額)から F列(税込金額)を書き込む
Sub WriteTaxIncluded()
Dim ws As Worksheet, lastRow As Long, i As Long
Set ws = ThisWorkbook.Worksheets("明細")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastRow
If IsNumeric(ws.Cells(i, "E").Value) And Not IsEmpty(ws.Cells(i, "E").Value) Then
ws.Cells(i, "F").Value = TaxIncluded(ws.Cells(i, "E").Value)
End If
Next i
End Sub引数の ByVal と ByRef
引数には ByVal(値渡し)と ByRef(参照渡し)があり、何も書かないと ByRef になります。ByRef の引数を呼び出し先で書き換えると、呼び出し元の変数まで変わります。意図せず元の値が変わると原因を追いにくいため、受け取った値を読むだけの引数には ByVal を明記するのがおすすめです。
逆に、複数の結果を返したいときは ByRef を意図的に使う方法もあります。次の例では、エラーメッセージを ByRef の引数で呼び出し元に返しています。
' 入力値をチェックし、問題があれば msg に理由を入れて False を返す
Function CheckCode(ByVal code As String, ByRef msg As String) As Boolean
msg = ""
If Len(code) <> 6 Then
msg = "コードは6桁で入力してください"
ElseIf Not code Like "[A-Z][A-Z]####" Then
msg = "英大文字2桁+数字4桁の形式ではありません"
End If
CheckCode = (msg = "")
End Function
Sub TestCheck()
Dim reason As String
If Not CheckCode("AB12", reason) Then MsgBox reason
End Sub自作関数をワークシートで使う
標準モジュールに Public(省略時も Public)で書いた Function は、ワークシートの数式から =TaxIncluded(E2) のように呼び出せます。社内独自の計算ルールを関数にしておくと、数式が短くなり、ルールの説明もしやすくなります。
ただし、ワークシートから呼び出す関数には制限があります。ほかのセルの値や書式を変更する処理は動かず、エラー値になります。また、引数に指定していないセルを関数の中で参照すると、そのセルが変わっても自動では再計算されません。必要な値はすべて引数で受け取るようにします。自作関数を使ったブックはマクロ有効ブック(.xlsm)で保存する必要があり、マクロを無効にして開くと結果が「#NAME?」になる点も、共有する前に周知しておくと混乱を防げます。
- シートから呼ぶ Function は標準モジュールに置く
- ほかのセルを書き換える処理は書けない
- 参照する値はすべて引数で受け取る
部品化のコツ
1つの Sub や Function は「1つのことだけをする」ように分けると、名前から処理内容が分かり、再利用もしやすくなります。名前は TaxIncluded や CheckCode のように、何をするかが分かる英語や、チームで決めたルールに合わせて付けてください。共通の部品は「共通処理」のような専用モジュールにまとめておくと、ほかのブックへのコピーも簡単です。