0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【VBA】ByValで配列を渡すとどうなる?VB.NETとの違いを検証

0
Posted at

目次

  1. はじめに
  2. VB.NETの元コードを確認
  3. VBAで同じ動作を再現できるか検証
  4. なぜVBAでは再現できないのか
  5. VB.NETとVBAの配列の違い
  6. 実務での注意点
  7. まとめ

はじめに

先日書いた記事のコメント欄で、VB.NETのコード例をいただきました。このコードはVB.NETでの配列のByVal渡しの挙動を示すものですが、VBAで同じ動作を再現できるか気になったので検証してみました。結論から言うと、VBAでは完全に再現することはできませんでした。この記事では、なぜ再現できないのかを実際に試しながら確認していきます。

コメントを頂いた記事はこちらです。

VB.NETの元コードを確認

まず、コメントでいただいたVB.NETのコードを見てみます。このコードはOneCompiler(オンラインのプログラム実行環境)で実行できます。

Imports System

Public Module Program
    Public Sub Main()
        Dim arr As Long() = New Long() {10, 20, 30}
        Console.WriteLine(arr(0))  ' 10
        Call ChangeArray(arr)      ' 参照型配列の値渡し
        Console.WriteLine(arr(0))  ' 999
    End Sub
    
    Sub ChangeArray(ByVal arg As Long())
        arg(0) = 999               ' 配列要素の変更は呼出し元に影響する
        arg = new Long() {40, 50}  ' 変数への再代入は呼出し元に影響しない
    End Sub
End Module

このコードの実行結果は次のようになります。

10
999

重要なポイントは、ByValで配列を渡しているのにarr(0)の値が999に変わっている点です。これは、VB.NETでは配列が参照型(データそのものではなく、データの場所を示す情報を持つ型)として扱われるためです。

VBAで同じ動作を再現できるか検証

それでは、VBAで同じ動作を再現できるか試してみます。まず、VB.NETと同じようにByVal arg() As Longという形式で書いてみます。

Sub ChangeArray(ByVal arg() As Long)
    arg(0) = 999
End Sub

しかし、この書き方はVBAでは構文エラーになります。VBAでは配列の引数にByValを指定することができません

image.png

VBAで配列を引数として受け取る場合、ByValは使用できず、ByRefのみが許可されるようです。

【Variantで受け取る場合】

それでは、配列をVariant型で受け取る形式に変更してみます。

Sub Main()
    Dim arr() As Long
    ReDim arr(0 To 2)
    
    arr(0) = 10
    arr(1) = 20
    arr(2) = 30
    
    Debug.Print "呼び出し前: " & arr(0)
    Call ChangeArray(arr)
    Debug.Print "呼び出し後: " & arr(0)
End Sub

Sub ChangeArray(ByVal arg As Variant)
    arg(0) = 999
    arg = Array(40, 50)
End Sub

このコードを実行すると、イミディエイトウィンドウ(デバッグ結果を表示する画面)に次のように表示されます。

呼び出し前: 10
呼び出し後: 10

VB.NETでは999になったのに、VBAでは10のままです。つまり、VBAでは配列をByValVariant型として受け取ると、配列全体がコピーされるため、要素の変更すら呼び出し元に影響しないのです。

【ByValを省略した場合】

では、引数の宣言でByValを省略した場合はどうなるか確認してみます。VBAでは引数の渡し方を省略すると、デフォルトでByRef(参照渡し)になります。

Sub Main()
    Dim arr() As Long
    ReDim arr(0 To 2)
    
    arr(0) = 10
    arr(1) = 20
    arr(2) = 30
    
    Debug.Print "呼び出し前: " & arr(0)
    Call ChangeArray(arr)
    Debug.Print "呼び出し後: " & arr(0)
End Sub

Sub ChangeArray(arg As Variant)
    arg(0) = 999
    arg = Array(40, 50)
End Sub

実行結果は次のようになります。

呼び出し前: 10
呼び出し後: 40

今度はarr(0)40に変わりました。これは、ByRefが省略されているため参照渡しとなり、argへの再代入(arg = Array(40, 50))も呼び出し元に影響するためです。

なぜVBAでは再現できないのか

この違いは、VB.NETとVBAで配列の扱い方が根本的に異なるためです。

【VB.NETの場合】

VB.NETでは、配列は参照型として扱われます。Microsoftの公式ドキュメントにも次のように書かれています。

要素が値型の場合でも、すべての配列は参照型

つまり、Long()という配列は参照型なので、ByValで渡すと「参照のコピー」が渡されます。変数自体はコピーですが、両方の変数が同じ配列データを指しているため、要素を変更すると呼び出し元にも影響します。

変数 → 参照をコピー → 引数
  ↓                    ↓
  └─→ 同じ配列データ ←┘

ただし、変数への再代入(arg = new Long() {40, 50})は、引数の変数が別の配列を指すようになるだけなので、呼び出し元には影響しません。

【VBAの場合】

一方、VBAでは次のような制約があります。

  1. 配列の引数にByValを指定できない
  2. 配列をVariant型でByVal受け取ると、配列全体がコピーされる
  3. 引数の渡し方を省略するとByRef(参照渡し)になる

配列をVariant型でByVal受け取った場合、配列全体がコピーされます。

変数 → 配列全体をコピー → 引数

配列全体がコピーされるため、引数の配列を変更しても呼び出し元の配列には影響しません。これは値型(データそのものを持つ型)のような動作です。

VB.NETとVBAの配列の違い

ここまでの内容を整理すると、次のような違いがあります。

VB.NETでは配列が参照型として扱われるため、ByValで渡しても参照のコピーが渡されます。そのため、配列要素の変更は呼び出し元に影響しますが、変数への再代入は影響しません。

VBAでは配列の引数にByValを指定できません。配列をVariant型でByVal受け取ると配列全体がコピーされるため、配列要素の変更も呼び出し元に影響しません。引数の渡し方を省略するとByRefになり、変数への再代入も呼び出し元に影響します。

この根本的な違いにより、VBAではVB.NETのコードと同じ動作を再現することができません。

実務での注意点

この違いは、実務でコードを書く際にも影響します。

【VBAで配列の要素を変更したい場合】

VBAで配列の要素を変更して呼び出し元に反映させたい場合は、ByRef(参照渡し)を使う必要があります。ByRefは省略可能ですが、明示的に書いておくとコードの意図が明確になります。

Sub ChangeArray(ByRef arg As Variant)
    arg(0) = 999
End Sub

ByRefを使うと、変数自体が共有されるため、要素の変更が呼び出し元に反映されます。ただし、変数への再代入も反映されるため、意図しない動作にならないよう注意が必要です。

【パフォーマンスへの影響】

VBAで配列をVariant型でByVal受け取ると配列全体がコピーされるため、要素数が多い配列を頻繁に渡す場合はパフォーマンスに影響する可能性があります。大きな配列を扱う場合は、ByRefを使用することでコピーのオーバーヘッド(余計な処理時間)を避けられます。

【他の言語から移植する際の注意】

VB.NETなどの他の言語からVBAにコードを移植する際は、配列の扱いが異なることを意識する必要があります。特に、ByValで配列を渡すコードは、VBAでは期待した動作にならない可能性があります。

まとめ

VB.NETでは配列が参照型として扱われるため、ByValで渡しても要素の変更が呼び出し元に影響します。しかし、VBAでは配列の引数にByValを指定できず、Variant型でByVal受け取ると配列全体がコピーされます。この違いを理解しておくと、他の言語からVBAに移植する際の混乱を避けられます。

情報求む

私が試した範囲では、Excel VBAで配列をByValで渡して値が変わるケースは見つかりませんでした。もし「こういう場合は配列でも値が変わる」という例をご存知の方がいらっしゃいましたら、コメント欄で教えていただけると嬉しいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?