VBA　関数をオブジェクトとして扱えるか　～～関数を変数に格納できるか～～


<table>
<tr>
<td>追記 　　 2024/06/27</td>
</tr>

<tr><td>

　この記事で、「関数をオブジェクトとして扱えるか」「関数を変数に格納できるか」について、「できる」と書きましたが、この記事で書いた内容では、全くできていませんでした。
　単に「戻り値の型」を、あるオブジェクト型にして、関数の戻り値を変数に格納しただけであり、
その戻り値であるオブジェクトのプロパティに処理結果を格納した、というだけでした。
　しかし、思いついたとき、「関数の型」が、指定したオブジェクト型になったと勘違いし、関数がそのオブジェクト型の変数に格納された、という勘違いをしてしまいました。
　まったく恥ずかしい話で正直削除したい気持ちもありますが、思い付きで書いてしまったことに対する自分への戒めとして掲載したままにさせてください。


</td></tr>
</table>



## 先に結論
　試行錯誤の結果、タイトルに書いたことは、「関数をオブジェクトとして扱える」、「関数を変数に格納できる」の両者ともできるという結果になりました。


## 今回の方法を考えるきっかけ
　VBAでツールを作っていて、「プログラミングでの抽象化」ということを考えていた時、[「猫型の蓄音機は 1 分間に 45 回にゃあと鳴く」](https://nekogata.hatenablog.com/entry/2015/08/05/001742)というページを見ました。このページのnoteという個所に、「高階関数」というものが紹介されていました。
　この「高階関数」は数学で「変数に関数を代入する」というようなもののようでしたが、これを見ていてふと以前から、プログラムでも「変数に関数を代入する（格納する）」ことを考えていたことを思い出しました。
　ただし、以前考えていたのは、正確には「変数を使って、条件分岐などで、使用される関数を変える（関数を選択する）」に近かったです。
　
　以前考えていたのは、多分、VB.NETやJavaであれば、何らかのクラスをスーパークラスとすれば、これを継承した複数のサブクラスは、スーパークラスの変数にどれも格納することができる。ですのので、条件によって変数に格納するオブジェクトを変えれば、それぞれのメソッドが実行されるので、「条件によって呼ばれる関数（オブジェクトのメソッド）を変える」ということができる。ということです。また、インターフェースを使用しても似たようなことができると思います。
　ただし、これがうまくいっても、変数に格納されるのは、メソッド（関数）が属しているオブジェクトです。
　
　先ほどのページを見ていてなぜかふと「メソッドが何らかのオブジェクト型に定義されていれば（戻り値が何らかのオブジェクトであれば）そのオブジェクト型の変数に格納できる」ということを思いつき、これなら「継承」を書くこともできず、インタフェースも定義できないVBAでも、「関数を変数に格納」して、「条件によって呼ばれる関数（オブジェクトのメソッド）を変える」ということができるのではないか、と思いました。
その方法について、今回書きます。


## 方法とソース


### 1 メソッドを、何らかのオブジェクト型に定義して、そのオブジェクト型の変数に格納する。（そのオブジェクトの基本形となる形を使用した例）


#### ①　メソッドを、何らかのオブジェクト型に定義する。（その基本形）

　タイトルの通り、メソッドを何らかのオブジェクトのオブジェクト型に定義します。これは、戻り値の型をそのオブジェクト型に定義するということになります。「何らかオブジェクト」の基本形として。「戻り値」を格納するための変数を宣言して、これをプロパティとしたクラスを定義します。
　「変数に格納する関数」は、ほかの何かのオブジェクトのメソッドとなりますが、戻り値が「オブジェクト」となるため、本来ならメソッドの「戻り値」として返したかった値は戻り値で返せなくなします。その本来戻り値として返したかった値を、このプロパティに設定します。


　　FunctionBase
```vb: FunctionBase

Option Explicit

Public intReturn As Integer


```

　コードとしては上記のみとなります。


#### ②　変数に格納したい関数（実際には何かのオブジェクトのメソッド）を、上記①のオブジェクト型に定義する。

　　今回は、今回変数に格納する関数は、何かのオブジェクトのメソッドとします。この、変数に格納することになるであろうメソッドを、上記①のオブジェクト型にします。

例えば、
本来

```vb:


Public Function Addition(x As Integer, y As Integer) As Integer
    Addition = x + y
End Function


```
となるメソッド（関数）を


　　
```vb: 


Public Function Addition(x As Integer, y As Integer) As FunctionBase
    Set Addition = New FunctionBase
    Addition.intReturn = x + y
End Function


```

のように定義する、ということです。


下記に示すのが、今回の例となるオブジェクト全体です。
計算するためのオブジェクトのようなものをイメージしました。


　　Calculations
```vb: Calculations


Option Explicit

Public Function Addition(x As Integer, y As Integer) As FunctionBase
    Set Addition = New FunctionBase
    Addition.intReturn = x + y
End Function

Public Function Subtraction(x As Integer, y As Integer) As FunctionBase
    Set Subtraction = New FunctionBase
    Subtraction.intReturn = x - y
End Function

Public Function Multiplication(x As Integer, y As Integer) As FunctionBase
    Set Multiplication = New FunctionBase
    Multiplication.intReturn = x * y
End Function

Public Function Division(x As Integer, y As Integer) As FunctionBase
    Set Division = New FunctionBase
    Division.intReturn = x / y
End Function


```


#### ③　このオブジェクトを使用する際に、①のオブジェクト型で宣言した変数に、②のオブジェクトのメソッドを格納できるので、条件分岐などで使用する。


　　Module1
```vb: Module1

Option Explicit

Sub Call_Macro1()
    
    Macro1 "+", 10, 20
    Macro1 "-", 10, 20
    Macro1 "*", 40, 30
    Macro1 "/", 40, 30
    Macro1 "A", 40, 30
    
End Sub

Sub Macro1(calcu_mode As String, x As Integer, y As Integer)
    
    Dim obj_Calcu As Calculations
    Dim obj_FuncCalcu As FunctionBase
    
    Set obj_Calcu = New Calculations
    Set obj_FuncCalcu = New FunctionBase
    
    If calcu_mode = "+" Then
        Set obj_FuncCalcu = obj_Calcu.Addition(x, y)
    ElseIf calcu_mode = "-" Then
        Set obj_FuncCalcu = obj_Calcu.Subtraction(x, y)
    ElseIf calcu_mode = "*" Then
        Set obj_FuncCalcu = obj_Calcu.Multiplication(x, y)
    ElseIf calcu_mode = "/" Then
        Set obj_FuncCalcu = obj_Calcu.Division(x, y)
    Else
        obj_FuncCalcu.intReturn = 0
    End If
    
    MsgBox "答えは " & obj_FuncCalcu.intReturn & " です"
    
    
End Sub


```


上記のように、変数にメソッドを格納して、条件分岐することができます。
このようなことができるようになることが今回の目的です。




### 2 変数に格納する メソッド(関数）の型となるオブジェクトに、メソッドなどを追加する。（関数の型となるオブジェクトに機能追加した場合の使用例）

　さらに、関数の型となるオブジェクトをエンハンスすることで、いろいろできそうです。




　　FunctionResultOut
```vb: FunctionResultOut


Option Explicit

Public intReturn As Integer

Sub DsipMsg()
    MsgBox "答えは " & Me.intReturn & " です"
End Sub
Sub WriteResult()
    Debug.Print "答えは " & Me.intReturn & " です"
End Sub
Sub DsipMsgAndWrite()
    Me.DsipMsg
    Me.WriteResult
End Sub



```


　　Calculations2
```vb: Calculations2


Option Explicit

Public Function Addition(x As Integer, y As Integer) As FunctionResultOut
    Set Addition = New FunctionResultOut
    Addition.intReturn = x + y
End Function

Public Function Subtraction(x As Integer, y As Integer) As FunctionResultOut
    Set Subtraction = New FunctionResultOut
    Subtraction.intReturn = x - y
End Function

Public Function Multiplication(x As Integer, y As Integer) As FunctionResultOut
    Set Multiplication = New FunctionResultOut
    Multiplication.intReturn = x * y
End Function

Public Function Division(x As Integer, y As Integer) As FunctionResultOut
    Set Division = New FunctionResultOut
    Division.intReturn = x / y
End Function


```





　　Module1
```vb: Module1



Sub Call_Macro2()
    
    Macro2 "M", 10, 20
    Macro2 "W", 30, 20
    Macro2 "MR", 40, 30
    
End Sub

Sub Macro2(Out_mode As String, x As Integer, y As Integer)
    
    Dim obj_Calcu As Calculations2
    Dim obj_FuncCalcu As FunctionResultOut
    
    Set obj_Calcu = New Calculations2
    Set obj_FuncCalcu = New FunctionResultOut
    
    Set obj_FuncCalcu = obj_Calcu.Addition(x, y)
    
    If Out_mode = "M" Then
        obj_FuncCalcu.DsipMsg
    ElseIf Out_mode = "W" Then
        obj_FuncCalcu.WriteResult
    Else
        obj_FuncCalcu.DsipMsgAndWrite
    End If
    
End Sub



```


　書いている私も、これでどのようなことができそうか、まだよくわかっていないです。しかし、こんなことできるんだ、というような気持ちだし、こんなことできるならもっと早く気づいていればよかった、という気もします。
　また、多分、変数に格納したい関数は、引数の数などに関係なく格納でき、C言語のポインタ関数ともちょっと違う気がします。（C言語のポインタ関数については、概要は知っていましたが、C言語は経験も浅く、詳しくは知りません。）
　これでは、何でもありなのでは？という気もしますが、その分、むしろ使い勝手が悪いかもしれない、ということも考えています。
　今は、ほかにやりたいこともあるし、応用の仕方までは研究できそうにないですが、ぜひ活用してほしいと思っています。