【VBA】functionの戻り値を複数にするにはどの方法がベストか

VBAのfanctionは関数名と同じ変数に戻り値を指定するわけですが、他の多くの言語と同じで1つしか変数を戻すことが出来ません。

fanctionから複数の変数を呼び元に戻したい時があるので、そのような場合には以下を検討することになるかなと思います。

  • ByRefの引数を複数指定する
  • 戻り値用のクラスを作成する
  • 戻り値を配列にして複数返却する

こんな感じで色々とあるかと思いますが、どの方法が良いのでしょうか。

結論から言うと

おすすめ出来ないのは「戻り値を配列にして複数返却する」です。

おすすめ出来るのは「ByRefの引数を複数指定する」です。

詳細を記載してみたいと思います。

「戻り値を配列にして複数返却する」がいまいちな理由

まず戻り値を配列にして複数返却するというのは、以下のような方法のことを想定しています。

Sub main()
    Dim v As Variant
    v = arraytest()
End Sub


Function arraytest()
    Dim ret(2) As String

    ret(0) = "a"
    ret(1) = "b"
    ret(2) = "c"
    arraytest = ret
End Function

↑こんな感じで戻り値に配列を使用してしまう感じです。

上記を動かすとこのような感じで、配列の戻り値を取得することが出来ます。これで複数の戻り値をfunctionから得ることが出来ました。

intやstringを混ぜて複数の戻り値を返却したい場合は、Variant型にするかArray関数を使えばよいかと思います。

Sub main()
    Dim v As Variant
    v = arraytest()
End Sub

Function arraytest()
    arraytest = Array("a", "b", 1)
End Function

ちなみにArray関数に文字列と数値を混ぜた場合は、当然Variant型になりますが、文字列だけ戻した場合もVariant型になります。(String型の配列にはならないので注意)

この方法はお手軽でよいのですが、「どんな値が戻り値になるのかコードを読まなければわかりにくい」という状況になりがちです。

なので関数が肥大化していくと、解読するのが難しくなってしまうため、大きなプロジェクトでは避けたほうが良いと思います。

(とはいえ、自分もファイル読み込みで行ごとの文字列を戻すときとかにたまに使いますが…。要は読みにくくなりがちなので、それが発生しないのであればOKだと思います。)

「ByRefの引数を複数指定する」は比較的わかりやすい

引数にByRefを使用することで、その引数の値を呼び元で受け取ることが出来るので、それを利用して擬似的にfunctionの戻り値を複数にします。

Sub main()
    Dim b As String
    Dim c As Integer
    Call byreftest("あ", b, c)
End Sub


Sub byreftest(ByVal a As String, ByRef b As String, ByRef c As Integer)
    b = a + "い"
    c = 1
End Sub

ByValは変更不可の引数、ByRefは戻り値として利用できる引数です。

たまにByVal、ByRefをつけていない文を紹介しているサイトもありますが、基本つけたほうがわかりやすいと思います。

この方法を使用すると、関数の戻り値が容易に推測できるのがメリットですね。

Sub byreftest(ByVal a As String, ByRef b As String, ByRef c As Integer)

↑上記を見ただけで、bという変数に文字列が戻されて、cという変数に数値が戻されるというのがわかると思います。

一方で配列を使用してfunctionの戻り値を複数にする方法では、コードの内容を読まないと戻り値の型などが推測できません。

戻り値がVariant型なので呼び元のソースもわかりにくくなりがちです。


戻り値用のクラスを作成して複数の値を持たせる

面倒くさいけど、わかりやすくて大規模プロジェクト向きなのが複数戻り値用のクラスを作成する方法です。

まずは以下のようなクラスを作成します。(自分はtestClassという名前で保存)

Public a As String
Public b As String
Public c As Integer

※Privateにしてgetter、setterを使ったほうがより硬いです。

このクラスを利用して以下のように、クラスを戻り値とするfunctionを作成することが出来ます。

Sub main()
    Dim ret As testClass
    Set ret = typetest()
End Sub


Function typetest()
    Dim ret As testClass
    Set ret = New testClass
    ret.a = "a"
    ret.b = "b"
    ret.c = 1
    
    Set typetest = ret
End Function

このfunctionからの戻り値のクラスは複数のフィールドを持っているため、こちらも擬似的に複数の戻り値を持つfunctionと言えるかと思います。

この方法の良いところは、戻したい値が大量にある場合でもシンプルに書けるということですね。

例えばDBからの取得結果やjsonの解析結果などを、ばらして戻したい時とかは良いかと思います。

デメリットは、やはり書くのが面倒だということですね。

クラスを作らないといけないですし、インスタンスを作ったりするコードも増えるのでどうしてもコード量が多くなります。

サクッと実装したい時はByRefの方で十分かなあと考えています。

まとめ

  • functionの戻り値を複数指定する方法は色々ある
  • 「戻り値を配列にして複数返却する」は、解読性が低くなりがち
  • 「ByRefの引数を複数指定する」は、お手軽でわかりやすい
  • 「戻り値用のクラスを作成する」は、わかりやすいがコード記載量が多い

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)