ExcelVBAにおける参照渡しと値渡しの違い

何事もそうですが、何かに取り組んでいたり勉強したりしていると「壁」というか、どうしてもわからない部分って出てくると思います。

 

例えばテキストの前半部分を読んでいてどうしてもわからない部分があったとします。そういった所は読み飛ばしてどんどん進んでいくと後半部分に書かれていることで前半部分でわからなかった所が理解できることもあります。

 

ExcelVBAでも、以前の自分は次の機能の概念や違いがわかっていませんでした。

  • 参照渡し
  • 値渡し

 

テキストを読んだ時は何となくわかったような気がするんですけど、ExcelVBAでいざコードを書いてみると何かこちらの意図通りに動いてくれないとか、例題のコードをVBE上で動かしても「なぜそうなるのか」がわかっていない時がありました。

 

ExcelVBAを勉強してきた中でなかなか理解できなかった部分は多々ありました。その中でも今回は「参照渡しと値渡しの違い」について、とある本をきっかけに理解できるようになったので備忘録という意味でも整理していってみます。

ExcelVBAにおける参照渡しと値渡しとは

ExcelVBAにおける参照渡しと値渡しとは何か。簡単に説明すると教科書的には以下のような形になります。

  • 参照渡しは「変数そのもの」を渡すデータの渡し方
  • 値渡しは「変数の値だけ」を渡すデータの渡し方

 

これだけ見てもチンプンカンプンかと思います。以前勉強していた自分も教科書上で何度この部分を見返してもなかなかこの違いが理解できませんでした。「変数そのもの」と「変数の値だけ」という言葉を見ても「えっ?」って感じになるかと思います。

 

コードの中で、この参照渡しと値渡しが使われる前後の部分をもう少し説明すると参照渡しの結果を得られるコードにおいて、『VBAエキスパート公式テキスト ExcelVBAスタンダード』には次のように書かれています。

p.33
Sub Sample6()
    Dim buf As String
    buf = "Excel VBA"
    Call Sample7(buf)
    MsgBox buf
End Sub

Sub Sample7(msg As String)
    msg = "VBAエキスパート"
End Sub

上記のコードを簡単に説明するとSample6のプロシージャでまず、文字列型のbufを宣言、そのbufに「Excel VBA」という文字列を格納します。そしてCallステートメントでSample7を呼び出します。

 

Sample7ではSample6で設定された「buf」を引数として受け取り、msgへ格納。そのmsgをSample7でmsgへ格納されたbuf(この時点ではまだbufに「Excel VBA」が格納されている)の値を「VBAエキスパート」という文字列へ変更しています。

 

処理はSample6へ戻り、Call Sample7(buf)の次の行であるMsgBox bufが実行されて、ここでSample7で設定された「VBAエキスパート」の文字列がメッセージボックスとして表示されます。

 

普通に考えるとSample7は「Subプロシージャ」なので、Sample6へ戻り値は送らず、メッセージボックスには「Excel VBA」の文字列が表示されるはずですが、実際には表示はされていません。

 

以上がテキスト上では「参照渡し」の処理となっています。もう少しわかりやすいように比較として「値渡し」の処理を引用してみます。

p.34
Sub Sample6()
    Dim buf As String
    buf = "Excel VBA"
    Call Sample7(buf)
    MsgBox buf
End Sub

Sub Sample7(ByVal msg As String)
    msg = "VBAエキスパート"
End Sub

さっきのコードと違う点はSample7においてSample6で設定されたbufを受け取る時の引数の形が「Byval」になっているという点です。この部分の引数の定義に「ByVal」というキーワードをつけると「変数の値だけを受け取る」という意味になります。

 

ちなみに「参照渡し」で受け取るときは、引数の定義に「ByRef」というキーワードをつけるというのがルールになっています。ただし、この参照渡しと値渡しを使う上での注意点として次のようなものがあります。

  • 「ByRef」と「ByVal」のどちらも指定しなかった場合には、「ByRef」が指定されたものとみなされる。
  • そのため引数の定義でデータの渡し方を省略すると、その引数は「参照渡し」になる

 

以上が文章で説明したExcelVBAにおける「参照渡し」と「値渡し」の違いとなります。今はちゃんと理解できた形で書けていますが、以前の自分はこれだけでは理解するのが困難でした。ですから、この部分に関しては悩むというかしばらくの間悶々とする日々を送ることになります。

 

そういった中で気分転換というか、以前から面白そうだなと思っていた本がありまして、そっちを読んでいたら偶然答えが見つかることになります。それが

  • 『Excel VBAでIEを思いのままに操作できるプログラミング術』

という本でした。

『Excel VBAでIEを思いのままに操作できるプログラミング術』に書かれている参照渡しと値渡しの説明

ExcelVBAを勉強していると「参照渡し」とか「値渡し」といった言葉が出てきます。自分も何度もこの部分は読んではみたのですが、正直いまいち理解できなかった、ということはここまで書いてきました。

 

このブログでは以下のような過去記事を書いています。

引用した過去記事ではExcelVBAが操作できる範囲はExcelVBAだけではなく、ワードとかパワーポイント、アクセス、さらにはそれ以外のアプリケーションも操作できるということがわかってきて「ExcelVBAってこんなこともできるんだ!」といった内容の記事です。

 

そういった理解やきっかけもあって『Excel VBAでIEを思いのままに操作できるプログラミング術』という本に非常に興味を持つようになり、本書を購入して読むようになりました。その後偶然にも本書を読んでからは、今回の記事で取り上げた「参照渡し」と「値渡し」について理解できるようになったので、その部分について引用してみます。

p.37

 1 Sub ObjectTest()
 2   Dim Sht1 As Object ←オブジェクト変数Sht1を宣言
 3   Dim Sht2 As Object ←オブジェクト変数Sht2を宣言
 4   
 5   Set Sht1 = ThisWorkbook.Sheets(1)←シート名はSheet1
① 6   Set Sht2 = Sht1
 7
② 8   Sht1.Name = "新しい名前"
 9
 10  MsgBox Sht1.Name & " " &Sht2.Name
 11End Sub

このサンプルコードを実行すると、表示されるメッセージは「新しい名前 Sheet1」ではありません。「新しい名前 新しい名前」と表示されます。

これは、②で変数Sht1のNameプロパティを変更したことが、変数Sht2にも影響しているためです。なぜこのようなことが起こるのでしょうか?

ここでオブジェクト変数に値を格納する際に利用した「Setステートメント」の登場です。文字列や数値などでは変数にはデータそのものを格納するのに対し、オブジェクトの場合はオブジェクトそのものを格納するわけではありません。「オブジェクトのありか」を格納しています。

したがって①で変数Sht2にはシート「Sheet1」のありかが格納されたことになります。②で変数Sht1のシート名をSheet1から「新しい名前」に変更したことで、当然、同じシートを指している変数Sht2のNameプロパティも「新しい名前」となります。

(中略)

このように、String型やInteger型など値を直接格納する変数を「値型」と呼ぶのに対し、オブジェクト変数のような「ありかを指し示す」ものを「参照型」と呼びます。

参照渡し、値渡しの理解に苦しんでいた自分にとって、次の言葉は非常にわかりやすいものでした。

  • オブジェクトのありか

引用した文章では「オブジェクト」という形で話が進んでいますが、この考え方は『VBAエキスパート公式テキスト ExcelVBA スタンダード』でも引用した参照渡しや値渡しとその例題のコードにも当てはめることが出来ます。

 

もう少し整理すると、イメージとしては

  • 参照渡しが元の値を扱う
  • 値渡しが元の値の「コピー」を扱う

といった解釈になります。

経理業務的に整理すると

  • 参照渡しが請求書の原本を使う
  • 値渡しが請求書のコピーを使う

といった形になります。

 

先ほど書いた参照渡しのSample6のコードでは「参照渡し」で値がSample7のコードに渡されました。この場合だと変数の「ありか」、つまり「元の値」が変更されてしまったのでbufに格納されていた「Excel VBA」が「VBAエキスパート」に変更されてしまったわけです。

 

一方もうひとつの値渡しのコードとしてSample6からSample7へ「値渡し」として値が渡され、Sample7でbufの値が変更されました。この場合だと変数bufに格納されたのは「コピー」になります。これだと大元の値である「ありか」の値が変更されたわけではありません。変更されたのは「コピー」の値だけです。

 

コピーの値をいくら変更しても「元の値」が変更されたわけではないので、Sample6で格納されたbufの値「Excel VBA」には何も影響がないわけです。

 

以上のような知識から、自分はやっとExcelVBAにおける「参照渡し」と「値渡し」の違いを理解できるようになりました。

まとめ

今回はExcelVBAにおいて「参照渡し」と「値渡し」という所がわからなかったけれども、とあるきっかけから教科書以外の本を読んでいたら理解できるようになった、といったことを書いてきました。

 

こういうことって勉強とか仕事に限らず結構あると思うんですよね。今までの延長線上でのやり方ではどうしてもわからなかったんだけど、一旦正規のルートを外れて周りを見渡してみると意外な所に解決策が存在していた的な。

 

何事も根を詰めすぎると限界にぶつかってしまうというか、今までと同じやり方ではどうしても解決できない問題って出てきてしまう時ってあると思います。そんな時に「思いがけない何か」を見つけられるようにするには、いろんな選択肢というかある程度の「遊び」の部分も仕事や日常生活には必要かと思います。

あわせて読みたい

こんな記事も読まれています

コメント