VBAでByVal(値渡し)とByRef(参照渡し)の違いを理解しよう|Callステートメントの使い方を徹底解説

Callステートメントについてのアイキャッチ VBA

VBAを使っていると、コードが複雑になってよく分からなくなる。。。
そんな悩みありませんか?

お悩みポイント
悩む人
  • 複数の処理をまとめて呼び出す方法がわからない…
  • 関数に渡したデータをそのまま使いたい、でもどうすればいい?
  • 関数にデータを渡した後、そのデータを呼び出し元にも反映させたい!

VBAでプログラムを書く際、最初は簡単な1つの処理を組み立てることから始めます。
しかし、プログラムが大きくなると、処理を分けたり、複数のプロシージャを使って再利用できるようにしたりすることが重要です。

ここで「Callステートメント」を使うことで、複数のプロシージャ(処理のかたまり)をうまく呼び出し、VBAコードをすっきり整理できます。

この記事では、初心者の方でも理解しやすいように「Callステートメント」を使ったプロシージャの呼び出し方法、そして「ByValと「ByRefの使い方を詳しく解説していきます。

最後まで読んでいただければ、VBAのコードがよりシンプルに、柔軟に書けるようになります。


【 この記事の概要 】

  よく使う度    4.0  
  難しさ     4.0  
  覚えておくと安心度     4.5  

この記事を読むとできること

この記事を見てできるようになること

複数の処理をまとめる「Callステートメント」とは?

「Callステートメント」は、簡単に言うと、他のプロシージャ(処理のまとまり)を呼び出して実行するためのコマンドです。

例えば、同じ処理を何度も書く代わりに、1つのプロシージャにまとめておいて、必要な時にそのプロシージャを「Call」することで処理を行うことができます。
また、プロシージャを分けて書くことで、コードの変更やデバッグを簡単に行うこともできます。

callを使わないと管理が大変。。。

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ステートメントを使うとどのような結果となるか

どのように動作したのか?

callステートメントの動作概要

このように、「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の処理結果

解説


このように、「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の処理結果

解説

ByRefの動作概要

このように、「ByRef (参照渡し)」を使用すると、呼び出し先(ChangeValueByRef)で、変数「num」を変更すると、呼び出し元(MainProcedure)に戻ってきた際に、変数「num」の値が変化します

ByVal (値渡し)とByRef (参照渡し)を指定しなかった場合

「ByVal (値渡し)」や「ByRef (参照渡し)」について解説しました。

この「ByVal (値渡し)」や「ByRef (参照渡し)」は、記述しなくても問題なく動作します。
その場合は「何も指定しない場合、デフォルトで「ByRef (参照渡し)」になる」ということを覚えておきましょう。

ByValや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行ずつ解説

コードの詳細
  1. Sub MainProcedure()
    • この行では、新しいサブルーチン「MainProcedure」を定義しています。
      このサブルーチンは、指定した範囲の表を削除し、その後の処理を行うための一連の操作を実行します。
  2. Dim targetRange As Range
    • ここでは、「targetRange」という名前の変数をRange型で宣言しています。
      この変数は、シート上の特定の範囲を指定するために使用されます。
  3. Set targetRange = Sheet1.Range(“A1”).CurrentRegion
    • この行では、targetRangeに「Sheet1」の「A1」セルを基準とした表全体の範囲を設定しています。
      CurrentRegionは、「A1」セルから周囲の空白セルまでの範囲を自動的に選択します。
  4. Call DeleteTable(ByVal targetRange)
    • この行では、「DeleteTable」という別のサブルーチンを呼び出しています。
      ByVal (値渡し)を使って「targetRange」を引数として渡し、その範囲内の内容を削除します。
      ByVal (値渡し)を使うことで、削除対象の範囲をコピーして操作するため、元の範囲には影響がありません。
  5. MsgBox “表が削除されました。”
    • この行では、メッセージボックスを表示し、「表が削除されました」というメッセージをユーザーに知らせます。
      MsgBox関数を使うことで、処理が完了したことを簡単に通知できます。
  6. End Sub
    • この行は、「MainProcedure」サブルーチンの終了を示しています。
      ここでこのサブルーチンのすべての処理が完了し、プログラムは次の処理に進みます。

  1. Sub DeleteTable(ByVal rng As Range)
    • この行では、新しいサブルーチン「DeleteTable」を定義しています。
      このサブルーチンは、指定された範囲(rng)の内容を削除するために使われます。
      ここで指定されている「rng」は、「targetRange」の範囲のことです。
  2. rng.ClearContents
    • この行では、「rng」で指定された範囲内のすべてのセルの内容を削除します。
      セルの中身だけがクリアされ、セルのフォーマット(色や罫線など)はそのまま残ります。
  3. MsgBox “指定された範囲の内容が削除されました。”
    • この行では、メッセージボックスを表示し、「指定された範囲の内容が削除されました」というメッセージをユーザーに知らせます。
  4. End Sub
    • この行は、「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行ずつ解説

コードの詳細
  1. Sub MainProcedure()
    • この行では、新しいサブルーチン「MainProcedure」を定義しています。
      このサブルーチンは、配列を初期化し、その配列を別のサブルーチンで処理するための手順を実行します。
  2. Dim numArray() As Variant
    • ここでは、「numArray」という名前の配列をVariant型として宣言しています。
      この配列は、複数の数値を格納するために使用されます。
      Array関数を使用する場合は、型を「Variant」にする必要があります。
  3. numArray = Array(1, 2, 3, 4, 5)
    • この行では、配列「numArray」に「1, 2, 3, 4, 5」という5つの数値を格納しています。
      Array関数を使って、一度に複数の数値を配列に設定します。
  4. Call ProcessArray(ByRef numArray)
    • この行では、「ProcessArray」という別のサブルーチンを呼び出しています。
      ByRef (参照渡し)を使って、配列「numArray」そのものを引数として渡し、その内容を処理します。
      ByRef (参照渡し)を使うことで、配列の変更が呼び出し元にも反映されます。
  5. MsgBox “配列の処理が完了しました: ” & Join(numArray, “, “)
    • この行では、MsgBox関数を使って、処理後の配列の内容をメッセージボックスで表示します。
      Join関数を使って、配列の要素をカンマで区切った文字列として表示します。
  6. End Sub
    • ここの行は、「MainProcedure」サブルーチンの終了を示しています。
      ここでこのサブルーチンのすべての処理が完了し、プログラムは次の処理に進みます。

  1. Sub ProcessArray(ByRef arr() As Variant)
    • この行では、新しいサブルーチン「ProcessArray」を定義しています。
      このサブルーチンは、渡された配列を処理するために使用されます。
      ByRef (参照渡し)を使って配列「arr」そのものを受け取り、その内容を直接変更します。
  2. Dim i As Long
    • ここでは、「i」という名前の変数をLong型で宣言しています。
      この変数は、配列内の各要素にアクセスするためのインデックスとして使用されます。
  3. For i = LBound(arr) To UBound(arr)
    • この行では、Forループを開始しています。
      LBound関数で配列の最初のインデックス(通常は0)を取得し、UBound関数で配列の最後のインデックスを取得します。
      この範囲内でループを繰り返し、配列の各要素を順番に処理します。
  4. If arr(i) Mod 2 = 0 Then
    • この行では、配列の現在の要素が偶数かどうかを確認しています。
      Mod 2を使って「2」で割った余りが「0」なら、その数は偶数です。
  5. arr(i) = arr(i) * 2
    • この行では、配列の現在の要素が偶数であれば、その値を2倍にします。
  6. Else
    arr(i) = arr(i) + 5
    • この行では、配列の現在の要素が奇数であれば、その値に「5」を加えます。
      Elseは、Ifの条件が成立しなかった場合に実行される部分です。
  7. End If
    • この行は、If条件文の終了を示しています。
      ここで条件分岐の処理が完了します。
  8. Next i
    • この行では、次のインデックスに移動し、Forループを繰り返します。
      すべての配列の要素が処理されるまで、ループが続行されます。
  9. End Sub
    • この行は、ProcessArrayサブルーチンの終了を示しています。
      すべての処理が完了し、サブルーチンは終了します。

ByRefを使用することで、配列そのものがサブルーチンに渡され、処理結果が呼び出し元にも反映される仕組みを理解できました。
これにより、配列の内容をサブルーチン内で自由に操作し、その変更を次の処理に活かすことが可能になります。

SubプロシージャとFunctionプロシージャの使い分け

Callステートメントを使う際、もう1つ重要なのが「Subと「Functionの使い分けです。

プロシージャには、SubプロシージャFunctionプロシージャの2種類があり、それぞれ用途が異なります。
これらは使い方が異なるため、使い分けが重要です。

Sub」は処理を行うだけで結果を返しませんが、「Function」は結果を返します
そのため、計算結果や処理結果を返す必要がある場合は、「Function」を使う方が適しています。

Subプロシージャを使うべき場合

Sub を使うべき場合
  • 処理を行うだけで、結果を返す必要がない場合
  • 例えば「セルの値を変更」「メッセージを表示」したりする場合に使います。
引用元:VBAのFunctionプロシージャの使い方|結果を返す処理の基本と応用

Functionプロシージャを使うべき場合

Function を使うべき場合
  • 計算や条件分岐によって結果を返す必要がある場合
  • 複数のプロシージャで同じ計算ロジックを使いたい場合。

引用元:VBAのFunctionプロシージャの使い方|結果を返す処理の基本と応用

「Functionプロシージャ」を使用すると、結果を返すことができます。
結果が必要な場合は「Function プロシージャ」、処理を行うだけであれば「Sub プロシージャ」を使うようにしましょう。

「Functionプロシージャ」については、別記事で解説を行っています。
合わせてご覧ください。

まとめ

Callステートメントを使うことで、VBAのコードを整理してシンプルに書くことができました。

また、ByVal (値渡し)とByRef (参照渡し)を使い分けることで、データの渡し方に柔軟性を持たせることができます。

ポイントのおさらい

  • Callステートメントでコードを整理
  • ByVal (値渡し)とByRef (参照渡し)の使い分け
    • ByVal (値渡し)
      • ByVal (値渡し)は、変数の「コピー」をプロシージャに渡します
        プロシージャ内で変数を変更しても、呼び出し元の変数には影響がありません
        例えば、プロシージャ内で変数を一時的に操作したいが、呼び出し元の値はそのままにしたい場合に使います。
    • ByRef (参照渡し)
      • ByRef (参照渡し)は、変数そのものをプロシージャに渡します
        プロシージャ内で変数が変更されると、呼び出し元の変数もその変更が反映されます
        例えば、プロシージャで変数を更新し、その結果を呼び出し元に反映させたい場合に使います。

        「ByValとByRefの違いって?」もう一度見る。
  • ByVal (値渡し)とByRef (参照渡し)の使用例
    • ByValを使った表の削除と後続処理
    • ByRefを使った配列の処理と返却
  • SubプロシージャとFunctionプロシージャの使い分け
    • Subプロシージャ
      • Subプロシージャは、特定の処理を行うためのコードブロックで、結果を返さないのが特徴です。
        たとえば、セルの値を変更したり、メッセージを表示したりする場合に使用します。
        結果を返す必要がない処理、つまり何らかのアクションだけを実行する場合には、Subプロシージャを使うべきです。
    • Functionプロシージャ
      • Functionプロシージャは、特定の処理を行い、結果を返すコードブロックです
        計算結果や条件分岐の結果などを他のプロシージャで利用するために使用されます。
        計算を行って結果を他のプロシージャに渡したい場合、または何らかの返り値が必要な場合には、Functionプロシージャを使います。

        「SubプロシージャとFunctionプロシージャの使い分け」もう一度見る。

このように、CallステートメントやByVal (値渡し)とByRef (参照渡し)の使い分けを理解することで、複数の処理を効率よく整理し、プログラム全体を柔軟かつ効率的に作成できます。

タイトルとURLをコピーしました