はじめに
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を使わないのが一番の解決策だと思う( ˘ω˘)