VBAを使っていると、コードが複雑になってよく分からなくなる。。。
そんな悩みありませんか?
VBAでプログラムを書く際、最初は簡単な1つの処理を組み立てることから始めます。
しかし、プログラムが大きくなると、処理を分けたり、複数のプロシージャを使って再利用できるようにしたりすることが重要です。
ここで「Callステートメント」を使うことで、複数のプロシージャ(処理のかたまり)をうまく呼び出し、VBAコードをすっきり整理できます。
この記事では、初心者の方でも理解しやすいように「Callステートメント」を使ったプロシージャの呼び出し方法、そして「ByVal」と「ByRef」の使い方を詳しく解説していきます。
最後まで読んでいただければ、VBAのコードがよりシンプルに、柔軟に書けるようになります。
【 この記事の概要 】
よく使う度 | |
難しさ | |
覚えておくと安心度 |
この記事を読むとできること
複数の処理をまとめる「Callステートメント」とは?
「Callステートメント」は、簡単に言うと、他のプロシージャ(処理のまとまり)を呼び出して実行するためのコマンドです。
例えば、同じ処理を何度も書く代わりに、1つのプロシージャにまとめておいて、必要な時にそのプロシージャを「Call」することで処理を行うことができます。
また、プロシージャを分けて書くことで、コードの変更やデバッグを簡単に行うこともできます。
callを使わないと管理が大変。。。
callを使うと管理が容易
Callステートメントの基本的な使い方
例として、2つのプロシージャを使って、1つ目のプロシージャで「こんにちは」と表示し、2つ目のプロシージャで「さようなら」と表示する例を考えてみましょう。
Sub MainProcedure()
Call Greet '// Greetプロシージャを呼び出し
Call Farewell '// Farewellプロシージャを呼び出し
End Sub
Sub Greet()
MsgBox "こんにちは"
End Sub
Sub Farewell()
MsgBox "さようなら"
End Sub
処理結果
どのように動作したのか?
このように、「Callステートメント」を使うことで、他のプロシージャを実行することができます。
これにより、処理を分けて整理し、コードを簡潔に保つことができます。
ByVal (値渡し)とByRef (参照渡し)の違いって?
VBAでは、プロシージャにデータを渡す際に「ByVal (値渡し)」または「ByRef (参照渡し)」という方法を使います。
この2つの方法は、渡したデータの扱い方に違いがあります。
ByVal (値渡し)とは?
ByValは「値渡し」です。
データをコピーしてプロシージャに渡します。
元のデータはそのままで、呼び出し元には影響を与えません。
コード例:
Sub MainProcedure()
Dim num As Long
num = 5
Call ChangeValueByVal(num)
MsgBox "呼び出し元の値: " & num
End Sub
Sub ChangeValueByVal(ByVal num As Long)
num = num + 10
MsgBox "内部の値(ByVal): " & num
End Sub
処理結果
解説
このように、「ByVal (値渡し)」を使用すると、呼び出し先(ChangeValueByVal)で、変数「num」を変更しても、呼び出し元(MainProcedure)に戻ってきた際に、変数「num」の値は変化しません。
ByRef (参照渡し)とは?
ByRefは「参照渡し」です。
データそのものを渡します。
プロシージャ内でデータが変更されると、呼び出し元のデータも変更されます。
コード例:
Sub MainProcedure()
Dim num As Long
num = 5
Call ChangeValueByRef(num)
MsgBox "呼び出し元の値: " & num
End Sub
Sub ChangeValueByRef(ByRef num As Long)
num = num + 10
MsgBox "内部の値(ByRef): " & num
End Sub
処理結果
解説
このように、「ByRef (参照渡し)」を使用すると、呼び出し先(ChangeValueByRef)で、変数「num」を変更すると、呼び出し元(MainProcedure)に戻ってきた際に、変数「num」の値が変化します。
ByVal (値渡し)とByRef (参照渡し)を指定しなかった場合
「ByVal (値渡し)」や「ByRef (参照渡し)」について解説しました。
この「ByVal (値渡し)」や「ByRef (参照渡し)」は、記述しなくても問題なく動作します。
その場合は「何も指定しない場合、デフォルトで「ByRef (参照渡し)」になる」ということを覚えておきましょう。
しかし、常に「ByRef (参照渡し)」にするべきではありません。
理由として、予期しない変更が発生する可能性があるためです。
たとえば、別のプロシージャで変数の値を意図せず変更してしまうと、後続の処理に悪影響を与えることがあるためです。
意図的に値を変更したくない場合は、「ByVal (値渡し)」を明示的に指定するようにしましょう。
ByVal (値渡し)・ByRef (参照渡し)を使用する際の注意ポイント
Callステートメントを使う際のByVal (値渡し) とByRef (参照渡し) の注意ポイントを解説します。
「ByVal (値渡し)」と「ByRef (参照渡し)」の使い分け
呼び出し元の値を保持したい場合は、「ByVal (値渡し)」を使いましょう。
逆に、値を変更し呼び出し元にも反映させたい場合は「ByRef (参照渡し)」を使います。
しかし、すべてを「ByRef (参照渡し)」にすると予期しない変更が発生することがあるので注意が必要です。
「ByRef (参照渡し)」は慎重に使用しましょう。
デフォルトは「ByRef (参照渡し)」
明示的に指定しない場合は、デフォルトで「ByRef (参照渡し)」になります。
意図しない変更を避けるため、変更を加えたくない場合は「ByVal (値渡し)」を指定する習慣をつけましょう。
ByVal (値渡し)・ByRef (参照渡し)の使用例をご紹介
ここからは、「動的配列」の使用例を紹介します。
どれも実際に動作するコードなので、ぜひ実行してみてください。
使用例1: 表の削除と後続処理 … ByVal (値渡し)
指定された範囲の表を削除し、その後の処理を実行する方法を紹介します。
この例では、CurrentRegionを使って範囲を指定し、ByVal (値渡し)を用いて削除処理を行います。
主な使用用途
表全体を一時的に操作し、その後の処理に影響を与えないようにする場合に便利です。
Sub MainProcedure()
Dim targetRange As Range
Set targetRange = Sheet1.Range("A1").CurrentRegion
'// ByValを使って表を削除
Call DeleteTable(ByVal targetRange)
'// 他の処理を続ける
MsgBox "表が削除されました。"
End Sub
Sub DeleteTable(ByVal rng As Range)
rng.ClearContents
MsgBox "指定された範囲の内容が削除されました。"
End Sub
- STEP1範囲の指定
CurrentRegionを使用して、Sheet1の「A1」セルを含む表全体を対象範囲として設定します。
- STEP2表の削除
ByValを使って、指定した範囲targetRangeをDeleteTableサブルーチンに渡します。
ここで指定された範囲内の内容が削除されます。 - STEP3後続処理
表の内容が削除された後に、メッセージを表示し、他の処理を続行します。
処理結果
このコードで使用している機能
1行ずつ解説
- Sub MainProcedure()
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
このサブルーチンは、指定した範囲の表を削除し、その後の処理を行うための一連の操作を実行します。
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
- Dim targetRange As Range
- ここでは、「targetRange」という名前の変数をRange型で宣言しています。
この変数は、シート上の特定の範囲を指定するために使用されます。
- ここでは、「targetRange」という名前の変数をRange型で宣言しています。
- Set targetRange = Sheet1.Range(“A1”).CurrentRegion
- この行では、targetRangeに「Sheet1」の「A1」セルを基準とした表全体の範囲を設定しています。
CurrentRegionは、「A1」セルから周囲の空白セルまでの範囲を自動的に選択します。
- この行では、targetRangeに「Sheet1」の「A1」セルを基準とした表全体の範囲を設定しています。
- Call DeleteTable(ByVal targetRange)
- この行では、「DeleteTable」という別のサブルーチンを呼び出しています。
ByVal (値渡し)を使って「targetRange」を引数として渡し、その範囲内の内容を削除します。
ByVal (値渡し)を使うことで、削除対象の範囲をコピーして操作するため、元の範囲には影響がありません。
- この行では、「DeleteTable」という別のサブルーチンを呼び出しています。
- MsgBox “表が削除されました。”
- この行では、メッセージボックスを表示し、「表が削除されました」というメッセージをユーザーに知らせます。
MsgBox関数を使うことで、処理が完了したことを簡単に通知できます。
- この行では、メッセージボックスを表示し、「表が削除されました」というメッセージをユーザーに知らせます。
- End Sub
- この行は、「MainProcedure」サブルーチンの終了を示しています。
ここでこのサブルーチンのすべての処理が完了し、プログラムは次の処理に進みます。
- この行は、「MainProcedure」サブルーチンの終了を示しています。
- Sub DeleteTable(ByVal rng As Range)
- この行では、新しいサブルーチン「DeleteTable」を定義しています。
このサブルーチンは、指定された範囲(rng)の内容を削除するために使われます。
ここで指定されている「rng」は、「targetRange」の範囲のことです。
- この行では、新しいサブルーチン「DeleteTable」を定義しています。
- rng.ClearContents
- この行では、「rng」で指定された範囲内のすべてのセルの内容を削除します。
セルの中身だけがクリアされ、セルのフォーマット(色や罫線など)はそのまま残ります。
- この行では、「rng」で指定された範囲内のすべてのセルの内容を削除します。
- MsgBox “指定された範囲の内容が削除されました。”
- この行では、メッセージボックスを表示し、「指定された範囲の内容が削除されました」というメッセージをユーザーに知らせます。
- End Sub
- この行は、「DeleteTable」サブルーチンの終了を示しています。
すべての処理が完了し、サブルーチンは終了します。
- この行は、「DeleteTable」サブルーチンの終了を示しています。
ByValを使うことで、削除操作が呼び出し元に影響を与えないように処理されます。
削除後にメッセージボックスを表示して、処理が正常に完了したことをユーザーに知らせることで、簡単で効果的なフィードバックが得られます。
また、DeleteTableサブルーチンでは、指定された範囲内のセルの内容をクリアし、もう一度メッセージボックスで操作の結果を通知しています。
このサブルーチンは、簡潔で効果的なコードの構造を示しており、特定の処理を別のサブルーチンに分けることで、コードの可読性と再利用性が向上します。
使用例2: 配列を条件分岐する … ByRef (参照渡し)
配列を別のサブルーチンに渡し、その内容を条件分岐で処理して結果を返す方法を紹介します。
この例では、ByRef (参照渡し) を使用して配列を直接操作し、その結果を呼び出し元に反映させます。
主な使用用途
ByRef (参照渡し) を使用して、配列の内容をサブルーチン内で変更し、その結果を呼び出し元に反映させたい場合に便利です。
たとえば、配列内のデータを条件に基づいて加工し、その変更を後続の処理に利用したい場合に、この方法が有効です。
Sub MainProcedure_使用例2()
Dim numArray() As Variant
numArray = Array(1, 2, 3, 4, 5)
'// ByRefを使って配列を処理
Call ProcessArray(numArray)
'// 処理後の結果を表示
MsgBox "配列の処理が完了しました: " & Join(numArray, ", ")
End Sub
Sub ProcessArray(ByRef arr() As Variant)
Dim i As Long
For i = LBound(arr) To UBound(arr)
If arr(i) Mod 2 = 0 Then
arr(i) = arr(i) * 2 '// 偶数なら2倍にする
Else
arr(i) = arr(i) + 5 '// 奇数なら5を加える
End If
Next i
End Sub
- STEP1配列の初期化
Array関数を使用して、numArrayという動的配列を初期化します。
この段階で配列に「1, 2, 3, 4, 5」という値が格納されます。 - STEP2配列の処理
ByRefを使ってnumArray配列をProcessArrayサブルーチンに渡します。
サブルーチン内でForループを使用し、配列の各要素に対して処理を行います。
具体的には、偶数であればその要素を2倍にし、奇数であれば5を加えます。 - STEP3処理結果の表示
処理が完了した配列の内容を、Join関数を使ってカンマ区切りの文字列に変換し、メッセージボックスで表示します。
これにより、配列がどのように変更されたかを確認できます。
処理結果
このコードで使用している機能
1行ずつ解説
- Sub MainProcedure()
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
このサブルーチンは、配列を初期化し、その配列を別のサブルーチンで処理するための手順を実行します。
- この行では、新しいサブルーチン「MainProcedure」を定義しています。
- Dim numArray() As Variant
- ここでは、「numArray」という名前の配列をVariant型として宣言しています。
この配列は、複数の数値を格納するために使用されます。
Array関数を使用する場合は、型を「Variant」にする必要があります。
- ここでは、「numArray」という名前の配列をVariant型として宣言しています。
- numArray = Array(1, 2, 3, 4, 5)
- この行では、配列「numArray」に「1, 2, 3, 4, 5」という5つの数値を格納しています。
Array関数を使って、一度に複数の数値を配列に設定します。
- この行では、配列「numArray」に「1, 2, 3, 4, 5」という5つの数値を格納しています。
- Call ProcessArray(ByRef numArray)
- この行では、「ProcessArray」という別のサブルーチンを呼び出しています。
ByRef (参照渡し)を使って、配列「numArray」そのものを引数として渡し、その内容を処理します。
ByRef (参照渡し)を使うことで、配列の変更が呼び出し元にも反映されます。
- この行では、「ProcessArray」という別のサブルーチンを呼び出しています。
- MsgBox “配列の処理が完了しました: ” & Join(numArray, “, “)
- この行では、MsgBox関数を使って、処理後の配列の内容をメッセージボックスで表示します。
Join関数を使って、配列の要素をカンマで区切った文字列として表示します。
- この行では、MsgBox関数を使って、処理後の配列の内容をメッセージボックスで表示します。
- End Sub
- ここの行は、「MainProcedure」サブルーチンの終了を示しています。
ここでこのサブルーチンのすべての処理が完了し、プログラムは次の処理に進みます。
- ここの行は、「MainProcedure」サブルーチンの終了を示しています。
- Sub ProcessArray(ByRef arr() As Variant)
- この行では、新しいサブルーチン「ProcessArray」を定義しています。
このサブルーチンは、渡された配列を処理するために使用されます。
ByRef (参照渡し)を使って配列「arr」そのものを受け取り、その内容を直接変更します。
- この行では、新しいサブルーチン「ProcessArray」を定義しています。
- Dim i As Long
- ここでは、「i」という名前の変数をLong型で宣言しています。
この変数は、配列内の各要素にアクセスするためのインデックスとして使用されます。
- ここでは、「i」という名前の変数をLong型で宣言しています。
- For i = LBound(arr) To UBound(arr)
- この行では、Forループを開始しています。
LBound関数で配列の最初のインデックス(通常は0)を取得し、UBound関数で配列の最後のインデックスを取得します。
この範囲内でループを繰り返し、配列の各要素を順番に処理します。
- この行では、Forループを開始しています。
- If arr(i) Mod 2 = 0 Then
- この行では、配列の現在の要素が偶数かどうかを確認しています。
Mod 2を使って「2」で割った余りが「0」なら、その数は偶数です。
- この行では、配列の現在の要素が偶数かどうかを確認しています。
- arr(i) = arr(i) * 2
- この行では、配列の現在の要素が偶数であれば、その値を2倍にします。
- Else
arr(i) = arr(i) + 5- この行では、配列の現在の要素が奇数であれば、その値に「5」を加えます。
Elseは、Ifの条件が成立しなかった場合に実行される部分です。
- この行では、配列の現在の要素が奇数であれば、その値に「5」を加えます。
- End If
- この行は、If条件文の終了を示しています。
ここで条件分岐の処理が完了します。
- この行は、If条件文の終了を示しています。
- Next i
- この行では、次のインデックスに移動し、Forループを繰り返します。
すべての配列の要素が処理されるまで、ループが続行されます。
- この行では、次のインデックスに移動し、Forループを繰り返します。
- End Sub
- この行は、ProcessArrayサブルーチンの終了を示しています。
すべての処理が完了し、サブルーチンは終了します。
- この行は、ProcessArrayサブルーチンの終了を示しています。
ByRefを使用することで、配列そのものがサブルーチンに渡され、処理結果が呼び出し元にも反映される仕組みを理解できました。
これにより、配列の内容をサブルーチン内で自由に操作し、その変更を次の処理に活かすことが可能になります。
SubプロシージャとFunctionプロシージャの使い分け
Callステートメントを使う際、もう1つ重要なのが「Sub」と「Function」の使い分けです。
プロシージャには、「Subプロシージャ」と「Functionプロシージャ」の2種類があり、それぞれ用途が異なります。
これらは使い方が異なるため、使い分けが重要です。
「Sub」は処理を行うだけで結果を返しませんが、「Function」は結果を返します。
そのため、計算結果や処理結果を返す必要がある場合は、「Function」を使う方が適しています。
Subプロシージャを使うべき場合
VBAのFunctionプロシージャの使い方|結果を返す処理の基本と応用引用元:
Functionプロシージャを使うべき場合
VBAのFunctionプロシージャの使い方|結果を返す処理の基本と応用引用元:
「Functionプロシージャ」を使用すると、結果を返すことができます。
結果が必要な場合は「Function プロシージャ」、処理を行うだけであれば「Sub プロシージャ」を使うようにしましょう。
「Functionプロシージャ」については、別記事で解説を行っています。
合わせてご覧ください。
まとめ
Callステートメントを使うことで、VBAのコードを整理してシンプルに書くことができました。
また、ByVal (値渡し)とByRef (参照渡し)を使い分けることで、データの渡し方に柔軟性を持たせることができます。
ポイントのおさらい
このように、CallステートメントやByVal (値渡し)とByRef (参照渡し)の使い分けを理解することで、複数の処理を効率よく整理し、プログラム全体を柔軟かつ効率的に作成できます。