VBA

VBAでもTupleが使いたい


はじめに

VBAではFunctionで複数の値を返したい時にC#みたいにTupleが使えない

例えばこんな感じのFunctionがあるとする

(こんなのFunctionにする必要ないのでは?というツッコミはなしで)

Function TryToInt(value As Variant) As Integer

If IsNumeric(value) Then
TryToInt = CInt(value)
Else
TryToInt = 0
End If
End Function

これは引数で受け取ったvalueがIntegerに変換できれば変換した値を、できなければ0を返している

しかし、これだと変換に失敗したかを0で判定するというルールを使う側に強いることになる

また、valueに0を渡した時には変換に失敗した扱いになるという致命的な問題がある

Sub Hoge()

Dim fuga As String: fuga = "23"
Dim piyo As Integer

piyo = TryToInt(fuga )

If piyo <> 0 Then
Debug.Print piyo
Else
Debug.Print "あばばばば"
End If
End Sub

それならと、戻り値をVariantにして失敗時にNullを返すとする

Function TryToInt(value As Variant) As Variant

If IsNumeric(value) Then
TryToInt = CInt(value)
Else
TryToInt = Null
End If
End Function

Sub Hoge()

Dim fuga As String: fuga = "23"
Dim piyo As Variant

piyo = TryToInt(fuga )

If Not IsNull(piyo) Then
Debug.Print CInt(piyo)
Else
Debug.Print "あばばばば"
End If
End Sub

こうすると0を渡した時の問題は解決するが、変換に成功したかの判定にはNullチェックを強要される

また、Variantで帰ってくるので厳密に型を要する場合は結局キャストする必要がある

調べてみたら、この他にも戻り値を配列にしたり、参照渡しでなんとかしたり...などなどいろいろなやり方があったけど、しっくりくるものは見つからなかった


Typeを使おう

ということでどうにかしてC#のTupleみたいな感じのことをしようと考えた時、Typeを使う方法を思いついた

Typeとは、ユーザー定義型といい、いわゆる構造体のこと

このTypeに変換した値と成功したかどうかを入れて返せばTupleっぽいことができそう

注)プロシージャより上に定義する必要があり、PublicなTypeは標準モジュールにのみ定義可能

Type IntBoolTuple

Value As Integer
IsSucceeded As Boolean
End Type

こうやってTypeを定義して

Function TryToInt(Value As Variant) As IntBoolTuple

Dim res As IntBoolTuple
If IsNumeric(Value) Then
res.Value = CInt(Value)
res.IsSucceeded = True
Else
res.IsSucceeded = False
End If
TryToInt = res
End Function

Sub Hoge()

Dim fuga As String: fuga = "23"
Dim piyo As IntBoolTuple

piyo = TryToInt(fuga )

If piyo.IsSucceeded Then
Debug.Print CInt(piyo.Value)
Else
Debug.Print "あばばばば"
End If
End Sub

こうやって使う

こうすれば前述した問題を解決でき、可読性が上がり、インテリセンスまで効くようになる


問題点

ただ、これも少し問題点がある

違う複数の値を返したい時に新たにいちいちTypeを定義する必要がある

今回の例のように、IntegerとBooleanではなくStringとBooleanを返したい時などはまたTypeを定義する必要があってめんどくさい

また、型を決めてしまっていると汎用性がなくなる

これは型をVariantにすればある程度は解消できる

Type Tuple

Value As Variant
IsSucceeded As Boolean
End Type

ただし、厳密に型を扱う時にはキャストする必要があるし、返したい値の数が変わった時(Valueに加えてValue2も返したい時とか)は新たにTypeを定義する必要がある


おわりに

複数の値を返すのが少しマシになったかと思う

必ずしもこのやり方がいいとは限らないので、ケースバイケースで使い分けてね

ほかにいいやり方があれば教えて下さい(人∀・)

まぁどんなに工夫してもなにかしらの不満は残るだろうから、そもそもVBAを使わないのが一番の解決策だと思う( ˘ω˘)