VBAで処理を分割していくと、「商品名と数量を渡したい」「開始行と終了行を渡したい」など、1つのSubやFunctionへ複数の値を渡したい場面が出てきます。
このような場合、VBAでは引数をカンマで区切って複数指定することで、1回の呼び出しで複数の値を渡せます。
ただし、単純に引数を増やせばよいわけではありません。文字列・数値などのデータ型を適切に指定し、呼び出す側と受け取る側の役割を分かりやすくしておくことが重要です。
特に実務用のマクロでは、1つのプロシージャに処理を詰め込むより、「値を渡す側」と「受け取って処理する側」に分割した方が、仕様変更や再利用に対応しやすくなります。
この記事では、VBAで複数の引数をSub・Functionへ渡す基本から、実務で処理を分割するときの考え方まで順番に解説します。
✅ VBAで複数の引数を渡す基本
複数の値を渡す処理は構文自体は難しくありませんが、実務では「どの値を、どの順番で、何のために渡しているのか」が分からなくなることがあります。特に文字列や数値を直接並べたコードは、作成した本人には理解できても、後から修正する人には意図が伝わりにくくなります。
また、引数の順番を間違えると、データ型によってはエラーになったり、意図しない処理につながったりします。複数引数を使う目的はコードを短くすることではなく、処理の役割を分けて再利用しやすくすることです。そのため、値を渡す構文だけでなく、引数名やデータ型の設計も重要になります。まずは基本的な仕組みから確認していきましょう。
・複数の引数を受け取るSubの構文
Subで複数の値を受け取る場合は、プロシージャ名の後ろに引数をカンマ区切りで指定します。
基本的な形は次のとおりです。
Sub プロシージャ名(引数1 As データ型, 引数2 As データ型)
' 引数を使用した処理
End Sub
たとえば、商品名と数量を受け取る処理なら次のようにできます。
Sub ShowOrderInfo(ByVal productName As String, ByVal quantity As Long)
MsgBox "商品名:" & productName & vbCrLf & _
"数量:" & quantity
End Sub
ここでは、
productName:商品名を受け取るquantity:数量を受け取る
という役割を明確にしています。
str1やvalue1などの汎用的すぎる名前でもコードは動作しますが、実務では後から見たときに何を渡しているのか判断しにくくなります。
そのため、引数名だけを見ても用途が分かる命名にしておくことが大切です。
・Subへ複数の値を渡して呼び出す方法
先ほど作成したShowOrderInfoを別のSubから呼び出してみましょう。
Sub ExecuteOrderProcess()
Dim productName As String
Dim orderQuantity As Long
productName = "商品A"
orderQuantity = 10
' 商品名と数量を渡して処理を呼び出す
ShowOrderInfo productName, orderQuantity
End Sub
処理の流れは次のようになります。
productNameへ商品名を格納するorderQuantityへ数量を格納するShowOrderInfoへ2つの値を渡す- 呼び出されたSubが値を受け取る
- 受け取った値を使って処理する
ここで重要なのは、呼び出し側と受け取り側で引数の順番が対応していることです。
ShowOrderInfo productName, orderQuantity
と呼び出した場合、
Sub ShowOrderInfo(ByVal productName As String, ByVal quantity As Long)
の1番目と2番目へ、それぞれ対応する値が渡されます。
つまり、単に「2つの値を渡している」のではなく、
1番目:商品名
2番目:数量
という対応関係があります。
引数が増えるほど、この順番は重要になります。
・直接値を指定するより変数を使うと意図が伝わりやすい
複数の引数は、次のように直接指定することもできます。
ShowOrderInfo "商品A", 10
短いテストコードであれば分かりやすい書き方です。
しかし、業務処理では次のように変数を経由させる方法も検討します。
Dim productName As String
Dim orderQuantity As Long
productName = "商品A"
orderQuantity = 10
ShowOrderInfo productName, orderQuantity
コード量は増えますが、
- 何の商品名なのか
- 10という数値が何を表しているのか
- 呼び出す前に値をどこで取得しているのか
を追いやすくなります。
特に引数が3つ、4つと増えていく場合、
RunProcess "商品A", 10, 3, True
のようなコードでは、それぞれの値が何を意味するのか分かりにくくなります。
複数の値を渡せることと、複数の値を分かりやすく渡せることは別です。
実務では後者を意識してコードを組み立てることが、保守しやすいマクロにつながります。
複数の値を受け渡す処理では、「引数」と「パラメータ」という言葉が使われることがあります。コードの書き方だけでなく、それぞれが何を指しているのか整理しておくと、SubやFunctionの宣言と呼び出しの関係も理解しやすくなります。
👉 【VBA】パラメータと引数の違い理解する|初心者が必ずつまずく概念を徹底解説では、混同しやすい2つの用語と役割を詳しく解説しています。
✅ VBAでデータ型が異なる複数の値を引数として渡す
複数の引数は、すべて同じデータ型にする必要はありません。文字列・数値・日付・Booleanなど、処理に必要な情報をそれぞれ適切な型で受け取れます。ただし、ここで「とりあえずVariantにしておけば何でも渡せる」と考えると、後からどのような値を想定しているプロシージャなのか分かりにくくなります。特に業務マクロでは、商品コードはString、数量はLong、処理日ならDateというように、データの役割が比較的明確です。適切な型を指定しておけば、誤った値を渡したときにも問題を発見しやすくなります。
また、引数を見るだけでプロシージャが必要としている情報を把握できるメリットもあります。複数引数では、数だけでなく型にも意味を持たせることが重要です。
・文字列・数値・日付をまとめて渡す例
たとえば、商品名・数量・処理日を別のSubへ渡す場合は、次のように記述できます。
Sub ProcessOrder( _
ByVal productName As String, _
ByVal quantity As Long, _
ByVal processDate As Date)
Debug.Print "商品名:" & productName
Debug.Print "数量:" & quantity
Debug.Print "処理日:" & Format(processDate, "yyyy/mm/dd")
End Sub
呼び出す側は次のようにします。
Sub ExecuteOrder()
Dim productName As String
Dim orderQuantity As Long
Dim processDate As Date
productName = "商品A"
orderQuantity = 10
processDate = Date
' 注文処理に必要な情報をまとめて渡す
ProcessOrder productName, orderQuantity, processDate
End Sub
このコードでは、
商品名 → String
数量 → Long
処理日 → Date
と、値の意味に合わせて型を分けています。
これにより、ProcessOrderの宣言部分を見るだけでも、「この処理を実行するには商品名・数量・処理日が必要」ということが分かります。
引数に適切なデータ型を指定するには、受け渡している値がどのような型なのか把握しておくことも重要です。特にVariant型の変数やセルから取得した値を引数へ渡す場合は、実際のデータ型を確認したいケースがあります。
👉 【VBA】VarType関数の使い方|変数・セルのデータ型を判定する方法では、変数やセルの値がどのデータ型として扱われているかを判定する方法を解説しています。
・複数引数ではByValとByRefの役割も意識する
ここまでのサンプルでは、引数にByValを指定しています。
ByVal productName As String
ByValを使用すると、呼び出し元の変数そのものを直接変更するのではなく、渡された値を受け取って処理できます。
今回のように、
「呼び出し元から情報を受け取り、その情報を使って処理する」
ことが目的なら、意図しない呼び出し元の変更を防ぐ意味でも分かりやすい設計です。
ただし、ByValとByRefの詳しい違いや使い分けそのものは、複数引数とは別のテーマです。
複数の値を渡すときには、
- 値を処理へ渡すだけなのか
- 呼び出し先で変更した内容を呼び出し元にも反映したいのか
という役割を確認して、ByValまたはByRefを選択します。
今回の記事では、複数の引数をどのように設計して受け渡すかを中心に考えていきます。
複数の引数を設計するときは、それぞれの値を単に受け取るだけなのか、呼び出し元へ変更を反映する必要があるのかによってByValとByRefを使い分けます。引数が増えるほど、この役割を明確にしておくことが保守性にも影響します。
👉 【VBA】Sub・FunctionでByValとByRefを使い分ける方法|引数設計で失敗しない実務テクニックでは、Sub・Functionを作るときのByValとByRefの選択を実務目線で詳しく解説しています。
✅ VBAのFunctionへ複数の引数を渡して結果を受け取る
複数の引数を渡せるのはSubだけではありません。Functionでも複数の値を受け取り、それらを使って計算・判定した結果を呼び出し元へ返せます。ここで混乱しやすいのが、「引数として渡す値」と「Functionから返ってくる戻り値」は別物だという点です。
たとえば単価と数量をFunctionへ渡し、計算された金額を1つの戻り値として受け取ることができます。この仕組みを理解すると、計算処理だけをFunctionへ切り出し、複数箇所から再利用しやすくなります。
反対に、何でもFunctionへまとめてしまうと処理の責任範囲が分かりにくくなるため、何を受け取り何を返すのかを明確にすることが重要です。Subとの違いも意識しながら、複数引数を使ったFunctionの基本を確認しましょう。
・単価と数量をFunctionへ渡して金額を取得する
たとえば、単価と数量を受け取り、金額を計算して返すFunctionは次のように作成できます。
Function CalculateAmount( _
ByVal unitPrice As Currency, _
ByVal quantity As Long) As Currency
' 単価と数量から金額を計算して返す
CalculateAmount = unitPrice * quantity
End Function
呼び出す側では、戻り値を変数へ格納します。
Sub ExecuteCalculation()
Dim unitPrice As Currency
Dim quantity As Long
Dim totalAmount As Currency
unitPrice = 1500
quantity = 3
' 2つの値を渡し、計算結果を受け取る
totalAmount = CalculateAmount(unitPrice, quantity)
MsgBox "金額:" & Format(totalAmount, "#,##0") & "円"
End Sub
この処理では、
unitPriceとquantityを用意する- 2つの値を
CalculateAmountへ渡す - Function内部で金額を計算する
- 計算結果を戻り値として返す
- 呼び出し元の
totalAmountで受け取る
という流れになっています。
このようにFunctionを利用すると、
入力:単価・数量
処理:金額計算
出力:計算結果
という役割を明確にできます。
金額計算を複数の処理から利用する場合でも、同じ計算式を各Subへ繰り返し記述する必要がありません。
複数の引数を使えるようになると、処理をSubやFunctionへ分割し、必要な場所から呼び出して再利用する機会も増えてきます。値の受け渡しだけでなく、プロシージャをどこに配置し、どのように呼び出すかまで整理しておくと、大きなマクロでも処理を追いやすくなります。
👉 【VBA】標準モジュールの呼び出し方法とは|Sub・Functionの使い分けと実務設計では、Sub・Functionの呼び出し方と処理を分割するときの考え方を解説しています。
✅ VBAでOptionalを使って一部の引数を省略する
複数の引数を使うようになると、「この値は毎回必要ではない」というケースが出てきます。そのたびに不要な値まで指定する設計にすると、呼び出し側のコードが分かりにくくなります。一方で、何でもOptionalにしてしまうと、プロシージャを実行するために本当に必要な情報が分かりづらくなるため注意が必要です。
実務では、必ず必要な値と、必要な場合だけ変更したい設定値を分けて考えることが重要です。Optionalは単純に引数を省略するための機能ではなく、プロシージャを再利用しやすくする選択肢として利用できます。特に同じ処理を条件だけ少し変えて何度も呼び出す場合に便利です。
・Optionalで省略可能な引数を設定する方法
たとえば、商品名と数量は必須ですが、処理結果をメッセージで表示するかどうかは必要に応じて変更したいとします。
その場合は、次のようにOptionalを指定できます。
Sub ProcessProduct( _
ByVal productName As String, _
ByVal quantity As Long, _
Optional ByVal showMessage As Boolean = True)
' 商品情報を使用した処理
Debug.Print productName & ":" & quantity
' 必要な場合だけメッセージを表示する
If showMessage Then
MsgBox "商品処理が完了しました。"
End If
End Sub
通常は、次のように必須となる2つの引数だけを渡せます。
ProcessProduct "商品A", 10
この場合、showMessageには既定値として指定したTrueが使用されます。
メッセージを表示したくない場合は、
ProcessProduct "商品A", 10, False
と指定できます。
この設計で重要なのは、商品名と数量という処理に欠かせない情報は必須引数として残していることです。
Optionalは「指定しなくても処理として成立する値」に限定した方が、プロシージャの用途を理解しやすくなります。
✅ VBAで名前付き引数を使って複数の値を分かりやすく渡す
複数の引数を渡すときに実務で起きやすい失敗が、引数の順番を見ただけでは値の意味を判断できなくなることです。特に数値やBooleanが続くプロシージャでは、「10は数量なのか行番号なのか」「Trueは何を有効にしているのか」が呼び出し部分だけでは分からないことがあります。引数が少なければ問題になりにくいものの、後から仕様が追加されると可読性が急激に低下することがあります。このような場合に利用できる方法の1つが名前付き引数です。
ただし、名前付き引数を使えば何個でも引数を増やしてよいわけではありません。まずは、複数引数の呼び出しを読みやすくする方法として仕組みを理解しておきましょう。
・名前付き引数で値の役割を明確にする方法
通常、複数の引数は定義されている順番に渡します。
ProcessProduct "商品A", 10, False
このコードだけを見ると、Falseが何を表しているのかすぐには分からない場合があります。
名前付き引数を使うと、次のように記述できます。
Call ProcessProduct( _
productName:="商品A", _
quantity:=10, _
showMessage:=False)
これなら呼び出し部分を見るだけでも、
"商品A"は商品名10は数量Falseはメッセージを表示するかどうか
という意味を把握できます。
また、名前付き引数では引数名を明示するため、Optional引数が複数ある場合にも役立ちます。
たとえば、処理条件を変更する値だけ指定したい場合にも、どの設定を変更しているのか分かりやすくなります。
一方で、毎回名前付き引数を使用する必要はありません。
CalculateAmount(unitPrice, quantity)
のように、2つ程度の引数で意味が明確なら通常の呼び出しでも十分です。
読み手が呼び出し部分だけで処理の意図を理解できるかを基準に選択するとよいでしょう。
✅ VBAで複数の値を渡す処理と複数の値を返す処理を区別する
「複数の値を渡す」という言葉から、複数の計算結果を呼び出し元へ返す方法まで同じものとして考えてしまうことがあります。しかし、「引数として複数の値をプロシージャへ渡すこと」と「プロシージャから複数の結果を返すこと」は設計上の目的が異なります。この違いを理解しないままByRefを増やしていくと、どの引数が入力で、どの引数が処理によって変更されるのか分かりにくいコードになりがちです。
特に複数人で改修するマクロでは、呼び出しただけで複数の変数が書き換わる処理は注意して扱う必要があります。Functionの戻り値、ByRef、配列などにはそれぞれ適した用途があります。今回の記事では詳細なByRefの仕組みには踏み込まず、「渡す」と「返す」を区別して設計することを押さえておきましょう。
・入力する値と処理結果を明確に分ける
たとえば、単価と数量を渡して金額を求めるのであれば、前半で紹介したFunctionの形が分かりやすくなります。
Function CalculateAmount( _
ByVal unitPrice As Currency, _
ByVal quantity As Long) As Currency
CalculateAmount = unitPrice * quantity
End Function
ここでは、
入力:unitPrice、quantity
出力:CalculateAmountの戻り値
と役割が明確です。
一方、ByRefを使えば呼び出し元の変数を書き換える設計もできます。
Sub CalculateOrder( _
ByVal unitPrice As Currency, _
ByVal quantity As Long, _
ByRef totalAmount As Currency)
totalAmount = unitPrice * quantity
End Sub
この方法自体が間違いというわけではありません。
ただし、引数が増えて、
入力用の引数
結果を返すための引数
設定を指定する引数
が混在すると、プロシージャの役割を把握しにくくなります。
1つの結果を返すだけならFunctionを利用できないか検討するなど、入力と出力がコードから判断しやすい構成を意識することが大切です。
ByRefを利用すると、呼び出し先で変更した内容を呼び出し元の変数へ反映できます。ただし、複数の引数にByRefを使用すると入力と出力の役割が分かりにくくなる場合もあるため、仕組みを理解したうえで使用することが重要です。
👉 【VBA】ByRefの基本的な使用方法|引数の参照渡しを理解するでは、ByRefによって呼び出し元の変数がどのように扱われるのかを基本から解説しています。
✅ VBAで複数の引数を増やしすぎると保守しにくくなる
VBAでは必要に応じて複数の引数を定義できますが、「渡せるから増やす」という設計はおすすめできません。実務では最初に3個だった引数が仕様変更のたびに追加され、気付けば8個、10個と並んでいるプロシージャも発生します。この状態になると呼び出し時の順番を確認するだけでも時間がかかり、値の指定漏れや取り違えも起きやすくなります。
また、引数が多いことは、そのプロシージャが多くの役割を担当しすぎているサインである場合もあります。特に別の担当者が改修する場合、引数の意味を調査するところから始めなければならず、変更の影響範囲も把握しづらくなります。複数引数を使うときは、追加方法だけでなく「増えすぎたら設計を見直す」という判断も重要です。
・引数が多くなったら処理を分割できないか確認する
たとえば、次のようなプロシージャがあるとします。
Sub ExecuteSalesProcess( _
ByVal productName As String, _
ByVal quantity As Long, _
ByVal unitPrice As Currency, _
ByVal processDate As Date, _
ByVal customerName As String, _
ByVal outputRow As Long, _
ByVal showMessage As Boolean)
' 複数の業務処理
End Sub
コードとしては成立します。
しかし、このプロシージャが、
- 金額を計算する
- 顧客情報を処理する
- セルへ結果を書き込む
- 完了メッセージを表示する
といった複数の役割をまとめて担当しているなら、引数の数だけを見るのではなく、処理そのものを分割できないか検討した方がよいでしょう。
たとえば、
CalculateAmount
↓
WriteSalesData
↓
ShowCompletionMessage
のように役割を分ける方法があります。
処理を適切に分割すれば、それぞれのSub・Functionが必要とする引数も少なくできる可能性があります。
ただし、「引数は3個まで」のような固定ルールを作る必要はありません。
業務上まとまった情報を扱うために、4個以上必要になるケースもあります。
重要なのは数そのものではなく、
「なぜこのプロシージャがすべての引数を必要としているのか」
を説明できる状態になっているかどうかです。
・引数を減らすためだけにグローバル変数へ逃がさない
引数が多いからといって、値をすべてモジュールレベルの変数やPublic変数へ移せばよいわけではありません。
たとえば、
Public productName As String
Public quantity As Long
Public unitPrice As Currency
としておけば、確かに各Subへ値を渡す必要は減ります。
しかし、どの処理がいつ変数を書き換えたのか追いにくくなり、別のマクロからも値を変更できる状態になります。
引数には、
「この処理が何の情報を必要としているのかを明示できる」
という大きなメリットがあります。
そのため、コードを短くするためだけに引数をなくすのではなく、必要な値は明示的に受け渡す方が保守しやすいケースが多くあります。
✅ VBAで複数の値を渡すときの実務的な設計ポイント
複数引数の構文を覚えるだけなら、カンマで値を並べることで実現できます。しかし、実務用マクロでは「動くこと」よりも、半年後に見ても何を渡しているのか理解できることが重要になります。特に仕様変更によって引数が追加されると、呼び出し箇所すべてへの影響を確認しなければならない場合があります。
また、ByVal・ByRef・Optionalを無計画に混在させると、どの値が変更されるのか判断しづらくなります。複数の値を渡す目的は処理を複雑にすることではなく、処理同士の役割を明確にして再利用しやすくすることです。最後に、実務で複数引数を設計するときに確認しておきたいポイントを整理します。
・呼び出す側と受け取る側の役割を明確にする
複数の引数を利用するときは、次のように考えると整理しやすくなります。
| 確認するポイント | 考え方 |
|---|---|
| 必ず必要な値 | 通常の引数として定義する |
| 省略可能な設定 | Optionalを検討する |
| 呼び出し元を変更しない値 | ByValを検討する |
| 呼び出し元へ変更を反映する値 | ByRefが必要か検討する |
| 1つの結果を返したい | Functionの戻り値を検討する |
| 引数の意味が分かりにくい | 変数や名前付き引数を活用する |
| 引数が増え続けている | プロシージャの役割を見直す |
特に重要なのは、引数の宣言部分を見ただけで、そのプロシージャが何を必要としているのかある程度判断できる状態にしておくことです。
また、複数の値を渡す処理ではByValとByRefの選択も重要ですが、単純に「ByValが安全」「ByRefが便利」と決めるものではありません。
呼び出し元の値を変更する必要があるかどうかを基準に選択し、変更する必要がなければ意図しない影響を避けられる設計を検討します。
複数のSubやFunctionを組み合わせるようになると、このような引数設計がコード全体の読みやすさに大きく影響します。
✅ まとめ:VBAで複数の引数を分かりやすく渡して処理を分割しよう
VBAでは、SubやFunctionへ複数の引数を渡すことで、必要な情報を受け渡しながら処理を分割できます。ただし、引数を増やすだけでは保守しやすいコードにはなりません。何を入力として受け取り、何を処理し、何を結果として返すのかを明確にすることが重要です。
また、Optionalや名前付き引数を適切に利用すれば、呼び出し側のコードを分かりやすくできる場合があります。引数が増え続けている場合は、そのプロシージャが多くの役割を持ちすぎていないか確認することも大切です。
- 複数の引数はカンマで区切ってSub・Functionへ渡せる
- 引数名は値の役割が分かる名前にする
- String・Long・Dateなど、用途に応じたデータ型を指定する
- 省略しても処理できる値にはOptionalを利用できる
- 名前付き引数を使うと、複数の値の意味を明確にできる
- 複数の値を「渡す」ことと、複数の結果を「返す」ことは分けて考える
- 1つの結果を返す処理ではFunctionの戻り値も検討する
- 引数が増えすぎた場合は、処理を分割できないか確認する
- 引数を減らすことだけを目的にグローバル変数へ置き換えない
- ByVal・ByRefは、呼び出し元の値を変更する必要があるかを考えて選択する
最初は2つ程度の引数を渡すシンプルなSubやFunctionから始め、処理が大きくなったら「この値は本当にこのプロシージャに必要なのか」を確認してみましょう。値の受け渡しと処理の役割を整理できるようになると、機能追加や仕様変更が発生しても追いやすく、別の処理にも再利用しやすいVBAコードを作成できます。