今回のネタは@mmYYmmddさんのつぶやきから生まれた。感謝。
さて、オーバーロードとは。
ふつう、Functionが取れる引数の数や型は固定されていてあまり自由が利かないのだが、引数の数や型によって処理を振り分けたい場合がある。
VariantやOptional、ParamArrayを活用することでこのような処理も可能であるが、一つの関数で処理を振り分けるとごちゃごちゃしてしまいメンテナンス性が落ちる。
これを解決するのがオーバーロードである。オーバーロードとは、引数の数もしくは型が違えば同名のプロシージャをいくつでも作成できる機能で、JavaやC#、C++などの言語には実装されている。
これを使えばあくまで別の関数として作れるので、複雑にならずにすむ。
残念ながら、VBAにその機能はない。そこで今回はオーバーロード(もどき)を実装してみたい。
業務コードでこれをすると複雑になるだけなので、ライブラリコードでしか使い道はないけど。
まず引数の型を判定するための関数をFunctionプロシージャで作成。
Function GetArgTypeString(ParamArray args()) As String Dim ret As String For Each x In args Select Case True Case IsMissing(x): ret = ret & "M" Case IsArray(x): ret = ret & "A" Case IsObject(x): ret = ret & "O" Case TypeName(x) = "String": ret = ret & "S" Case IsDate(x): ret = ret & "D" Case IsNumeric(x): ret = ret & "N" Case Else: ret = ret & "U" End Select Next GetArgTypeString = ret End Function
この関数にいくつか引数を渡すと、文字列で型が返ってくる。
たとえば、1, #2017/3/5#, "Hello" の順で渡すと、NDSとなる。Number、Date、Stringである。
次に、たとえばAddという関数を作りたい場合、Addに先ほどの文字列をつけた名前で関数を作る。
タイプごとに4つ用意した。
Function AddSSM(a, b) AddSSM = a & b End Function Function AddSSS(a, b, c) AddSSS = a & b & c End Function Function AddNNM(a, b) AddNNM = a + b End Function Function AddDNM(a, b) AddDNM = a + b End Function
そして、渡された引数によって実際の関数へ処理を引き渡すための、窓口となる関数を作る。
これが本来呼び出したいAdd関数。
Function Add(a, b, Optional c) Dim typeString As String typeString = GetArgTypeString(a, b, c) Add = Application.Run("Add" & typeString, a, b, c) End Function
あとはメインコード。
Sub Main() Debug.Print Add(1, 2) Debug.Print Add("1", "1a") Debug.Print Add(#3/5/2017#, 1) Debug.Print Add("A", "B", "C") End Sub
実行するとこのとおり。
3 11a 2017/03/06 ABC
まぁ使い物になるかどうかはわからないが、私が類似の問題を抱えたときにアイデアの叩き台になればと思い、ここに残しておく。
念押しするが、アプリケーションコードでコレは余計複雑になるだけなのでやめておいたほうが賢明。ただライブラリを書く目的ならこういうのもアリかなと思う。