VBA技術解説
クラスとCallByNameとポリモーフィズム(多態性)

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
公開日:2019-04-06 最終更新日:2019-04-06

クラスとCallByNameとポリモーフィズム(多態性)


VBAの使い方が進んでいくとクラスを使うようになっていきます。
クラスを使うようになるとオブジェクト指向という言葉に出会い、
オブジェクト指向を学んでいくとポリモーフィズム(多態性)という言葉に出会います。


オブジェクト指向における多態性の説明としては、
メソッドの呼び出し時に、メソッドが属するオブジェクト(クラス)の種類によって呼び出し先の実装コードが選択され、処理内容が変化する性質。
このように説明されます。
単に多態性と言った場合は、
プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属することを許すという性質。
ということになります。

今回はオブジェクト指向の多態性に進む前段階として、
そもそも多態性とはどのようなものかを、VBAで理解することを目的とした記事になります。
そこで、関数に多態性を持たせた多態性関数について取り上げてみました。

ただしVBAで多態性関数が使えるわけではなく、多態性関数のような動きを作って多態性についての理解を深めていこうという主旨になります。
今回は、CallByName関数を使いどのような動きになるかを説明していきます。
CallByName関数のヘルプでの説明は以下のように書かれています。
「オブジェクトのメソッドを実行するか、オブジェクトのプロパティを設定するか返します。」
何に使うものなのか、意味も良く分からない、、、ですよね。

CallByName関数は、変数内にある文字列としてのプロシージャー(メソッドやプロパティ)を起動することができます。
つまり、動的(実行時)に実行するプロシージャーを切り替えられるということです。
VBAにおけるCallByName関数は、コールバックやリフレクションを実現するための関数とも言えます。

コールバック(callback)
ある関数などを呼び出す際に別の関数などを実行するよう指定する手法

リフレクション(reflection)

実行時にプログラムの構造や構成要素の情報を取得したり、動作を動的に変更したりすること


VBAで動的(実行時)に実行するプロシージャーを切り替える方法としては、
Application.RunメソッドやApplication.OnTimeメソッドもありますが、
今回はCallByName関数の文法から入り、多態性関数へと進めていきます。


>CallByName関数

>
CallByName関数は、実行時に指定したオブジェクトのプロパティの設定と取得、およびメソッドの実行を行います。

CallByName(object,procname,calltype,[args()])

引数 内容
object 必ず指定します。
バリアント型およびオブジェクト型のいずれかを指定できます。
この関数の実行対象となるオブジェクトの名前を指定します。
procname 必ず指定します。
バリアント型および文字列型のいずれかを指定できます。
オブジェクトのプロパティ名およびメソッド名を含む文字列式を指定します。
calltype 必ず指定します。
定数値を指定します。
呼び出されるプロシージャの種類を表すvbCallTypeのメンバを指定します。
定数 内容
VbMethod 1 メソッドを実行する
VbGet 2 プロパティの値を取得する
VbLet 4 プロパティの値を設定する
VbSet 8 プロパティにオブジェクトへの参照を代入する
args() 省略可能です。
バリアント型および配列を指定できます。

戻り値は、実行したメソッドまたはプロパティの取得値になります。

第1引数はobjectなので、標準モジュールのプロシージャーをCallByName関数で実行することはできません。

>クラスとCallByNameとポリモーフィズム(多態性)のVBAコード

>
多態性関数の処理内容
引数のデータ型により、処理を切り替えます。
文字列型の場合、第1引数の文字を第2引数分繰り返します。
数値型(各種あり)の場合、第1引数に第2引数を掛け算します。
いずれも、第1引数のデータ型で戻します。

つまり、データ型により動的に実行するメソッドを変更してみようということです。

クラス:clsFunc

Option Explicit

Public Function Multi(ByVal arg1 As Variant, ByVal arg2 As Variant) As Variant
  On Error Resume Next '未設定のデータ型の対応
  Multi = CallByName(Me, "Multi_" & TypeName(arg1), VbMethod, arg1, arg2)
End Function

Public Function Multi_String(ByVal arg1 As Variant, ByVal arg2 As Integer) As String
  Multi_String = WorksheetFunction.Rept(arg1, arg2)
End Function

Public Function Multi_Integer(ByVal arg1 As Variant, ByVal arg2 As Long) As Integer
  Multi_Integer = arg1 * arg2
End Function

Public Function Multi_Long(ByVal arg1 As Variant, ByVal arg2 As Long) As Long
  Multi_Long = arg1 * arg2
End Function

Public Function Multi_Single(ByVal arg1 As Variant, ByVal arg2 As Long) As Single
  Multi_Single = arg1 * arg2
End Function

Public Function Multi_Double(ByVal arg1 As Variant, ByVal arg2 As Long) As Double
  Multi_Double = arg1 * arg2
End Function

Public Function Multi_Currency(ByVal arg1 As Variant, ByVal arg2 As Long) As Currency
  Multi_Currency = arg1 * arg2
End Function

標準モジュール

Option Explicit

Sub sample1()
  Dim rtn As Variant
  Dim var As Variant
  
  var = "123"     '文字列型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 123      '整数型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 123&     '長整数型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 1234.543!   '単精度浮動小数点型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 1234.54321  '倍精度浮動小数点型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 1234.5432@  '通貨型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
End Sub

Public Function Multi(ByVal arg1 As Variant, ByVal arg2 As Variant) As Variant
  On Error Resume Next '未設定のデータ型の対応
  Multi = CallByName(New clsFunc, "Multi", VbMethod, arg1, arg2)
End Function

Function Multiについては、以下のよう書くこともできます。

Public Function Multi(ByVal arg1 As Variant, ByVal arg2 As Variant) As Variant
  On Error Resume Next '未設定のデータ型の対応
  Multi = CallByName(New clsFunc, "Multi_" & TypeName(arg1), VbMethod, arg1, arg2)
End Function

さらに、先にクラスのインスタンスを作成するように書くこともできます。

Sub sample1()
  Dim rtn As Variant
  Dim var As Variant
  
  Dim fnc As New clsFunc
  
  var = "123"
  rtn = fnc.Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 123
  rtn = fnc.Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  '・・・
  
End Sub

いずれも書き方の違いであり、実行結果とどれも同じになります。

sample1の実行結果

エクセル VBA クラス 多態性

文章で説明するようなところはほとんどないので、とにかく、
ステップイン(F8)で動作を確認してみましょう。
文章で説明するより、実際に動かして、その挙動を確認してみるのが一番理解が進むはずです。

標準モジュールで関数を使うときの記述は1通りで書いても、
実際に動作する関数は別々のものが実行されていることを確認してください。

多態性関数がサポートされているプログラミング言語であれば、
Multi_String~Multi_Currency
これらを全てFunction Multiとだけ書けば良いということになります。

Sub sample1()
  Dim rtn As Variant
  Dim var As Variant
  
  var = "123"     '文字列型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  var = 123      '整数型
  rtn = Multi(var, 3)
  Debug.Print TypeName(rtn), rtn
  
  '・・・
  
End Sub

Public Function Multi(ByVal arg1 As Variant, ByVal arg2 As Integer) As String
  Multi_String = WorksheetFunction.Rept(arg1, arg2)
End Function

Public Function Multi(ByVal arg1 As Variant, ByVal arg2 As Long) As Integer
  Multi_Integer = arg1 * arg2
End Function

'・・・


オブジェクト(クラス)も動的に変更すると

先に、クラス(clsFunc)のVBAコードを全てThisWorkbookに貼り付けておいてください。
(VBAコード全てをコピーして、ThisWorkbookに貼り付けてください。)

標準モジュール

Option Explicit

Sub sample2()
  Dim obj As Object
  Dim rtn As Variant
  Dim var As Variant
  
  Set obj = New clsFunc
  var = "123"     '文字列型
  rtn = Multi(obj, var, 3)
  Debug.Print TypeName(rtn), rtn
  
  Set obj = ThisWorkbook
  var = 123      '整数型
  rtn = Multi(obj, var, 3)
  Debug.Print TypeName(rtn), rtn
  
  '・・・
  
End Sub

Public Function Multi(ByVal obj As Object, ByVal arg1 As Variant, ByVal arg2 As Variant) As Variant
  On Error Resume Next '未設定のデータ型の対応
  Multi = CallByName(obj, "Multi", VbMethod, arg1, arg2)
End Function

Set obj = New clsFunc
Set obj = ThisWorkbook
上では事前にSetしましたが、この部分をFunction Multiの中で切り替えるようにするとより動的な感じになるでしょう。

ここでは、オブジェクトとして全く違うものでも良いことのサンプとしてクラスとThisWorkbookと極端に違うものにしてみましたが、
普通なら、クラスを複数作って切り替えるということになるでしょう。

このような考え方をさらに進めていき、オブジェクト指向としの多態性を実現するには、
Implementsステートメント(インターフェイス)を使うことになりますが、その説明はまたの機会に。

最後に

CallByName関数の処理速度は遅いです、当り前と言えば当り前の事です。
(ただし遅いといっても、直接メソッド等を実行する場合に比べてということです。)
また今回の多態性関数の場合は、余程多くの関数を作るという場合でも無い限り、
Select Caseで分岐すれば良いだけのことなのは言うまでもないでしょう。

実際に、CallByName関数を使う機会はあまりないと思います。
完成したVBAのテストツールのようなものを作るときに使うとか、
プロパティの一覧がシートにあって、その一覧にプロパティ値を出力したいとか、
実務として使う必要が出てくるとしたら、こういう場合くらいではないかと思います。

今回は、VBAプログラミングの学習の過程として、やっておいた方が良いだろうと言うことで取り上げてみました。
あくまで多態性の必要性・便利さを少しでも理解してもらうための入り口としての記事になります。
従いまして、あまり細かいことにこだわる必要はなく、
オブジェクトのメソッド・プロパティを文字列で指定して実行できるCallByName関数があるということと、
多態性とはどういうことなのかをざっくりと理解するだけで良いでしょう。



同じテーマ「VBAクラス入門」の記事

VBAクラスの作り方:列名のプロパティを自動作成する
VBAクラスの作り方:独自Rangeっぽいものを作ってみた
クラスとイベントとマルチプロセス並列処理
クラスとCallByNameとポリモーフィズム(多態性)
オートフィルターを退避回復するVBAクラス
オートフィルター退避回復クラスを複数シート対応させるVBAクラス
コレクション(Collection)の並べ替え(Sort)に対応するクラス
VBAクラスのAttributeについて(既定メンバーとFor Each)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)
VBAで音楽再生するクラスを作成
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)


新着記事NEW ・・・新着記事一覧を見る

TRIMRANGE関数(セル範囲をトリム:端の空白セルを除外)|エクセル入門(2024-08-30)
正規表現関数(REGEXTEST,REGEXREPLACE,REGEXEXTRACT)|エクセル入門(2024-07-02)
エクセルが起動しない、Excelが立ち上がらない|エクセル雑感(2024-04-11)
ブール型(Boolean)のis変数・フラグについて|VBA技術解説(2024-04-05)
テキストの内容によって図形を削除する|VBA技術解説(2024-04-02)
ExcelマクロVBA入門目次|エクセルの神髄(2024-03-20)
VBA10大躓きポイント(初心者が躓きやすいポイント)|VBA技術解説(2024-03-05)
テンキーのスクリーンキーボード作成|ユーザーフォーム入門(2024-02-26)
無効な前方参照か、コンパイルされていない種類への参照です。|エクセル雑感(2024-02-17)
初級脱出10問パック|VBA練習問題(2024-01-24)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.繰り返し処理(For Next)|VBA入門
3.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
4.変数宣言のDimとデータ型|VBA入門
5.RangeとCellsの使い方|VBA入門
6.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
7.セルのクリア(Clear,ClearContents)|VBA入門
8.メッセージボックス(MsgBox関数)|VBA入門
9.条件分岐(Select Case)|VBA入門
10.ブック・シートの選択(Select,Activate)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。


このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ