[VBA] インタフェース継承を使用する際にクラス変数として配列を使用する方法
概要
この記事について
VBAではインタフェースを用いることによって、 オブジェクト指向のプログラミングをすることができる。
ただ、後述するように
クラス変数に特徴的な制約があるため、
継承したクラスにクラス変数として配列をもたせる場合に
通常の変数と同じようにするとエラーとなる。
この記事で、その現象と、
回避方法について記したい。
説明のために作成したExcelファイルとソースコード、テスト用データはこちらでダウンロードできます。
作成環境
Windows 10 Home(64bit)
MSOffice 2016
概要
About
チームクラスインタフェースclsAbsTeam
を実装したチームクラスclsAnalyzeTeam
、clsNewTeam
が存在する。
それぞれのチームクラスが、
チームメンバー名前の格納用配列と、
指定されたインデックスのチームメンバー名前を取得するメソッドを持つ。
クラス図(変数・メソッド記載無し)
エラーの発生
About
次のようにインタフェースと各継承先クラスを実装する。
1'*** clsAbsTeam ***
2
3Option Explicit
4
5'**************************
6'*チームクラスインタフェース
7'**************************
8
9'定数欄
10
11'変数欄
12
13
14'******************************************************************************************
15'*getter/setter欄
16'******************************************************************************************
17
18
19Public Function getMemberName(ByVal idx As Long) As String
20
21End Function
22
1'*** clsAnalyzeTeam ***
2
3Option Explicit
4
5Implements clsAbsTeam
6
7'**************************
8'*チームクラス 解析チーム
9'**************************
10
11'定数欄
12
13'変数欄
14
15
16'******************************************************************************************
17'*getter/setter欄
18'******************************************************************************************
19
20
21'******************************************************************************************
22'*関数名 :clsAbsTeam_getMemberName
23'*機能 :チームメンバーの名前取得
24'*引数 :対象者のインデックス番号
25'*戻り値 :チームメンバーの名前
26'******************************************************************************************
27Private Function clsAbsTeam_getMemberName(ByVal idx As Long) As String
28
29 '定数
30
31 '変数
32
33 '***ここに名前取得の処理を入れる***
34
35
36ExitHandler:
37
38 Exit Function
39
40End Function
41
42
1'*** clsNewTeam ***
2
3Option Explicit
4
5Implements clsAbsTeam
6
7'**************************
8'*チームクラス 新設のチーム
9'**************************
10
11'定数欄
12
13'変数欄
14
15
16'******************************************************************************************
17'*getter/setter欄
18'******************************************************************************************
19
20
21'******************************************************************************************
22'*関数名 :clsAbsTeam_getMemberName
23'*機能 :チームメンバーの名前取得
24'*引数 :対象者のインデックス番号
25'*戻り値 :チームメンバーの名前
26'******************************************************************************************
27Private Function clsAbsTeam_getMemberName(ByVal idx As Long) As String
28
29 '定数
30
31 '変数
32
33 '***ここに名前取得の処理を入れる***
34
35
36ExitHandler:
37
38 Exit Function
39
40End Function
41
42
ここで、チームメンバーの名前を収納するための配列をもたせるために、
インタフェースにPublicなスコープの配列変数を定義しようとする。
1'*** clsAbsTeam ***
2
3Option Explicit
4
5'**************************
6'*チームクラスインタフェース
7'**************************
8
9'定数欄
10
11'変数欄
12Public arrayMenberName(1 To 6) As String
13
14
15'******************************************************************************************
16'*getter/setter欄
17'******************************************************************************************
18
19
20Public Function getMemberName(ByVal idx As Long) As String
21
22End Function
23
すると、
定数、固定長文字列、配列、ユーザー定義型および Declare ステートメントは、
オブジェクト モジュールのパブリック メンバーとしては使用できません。
とのエラーが発生する。
原因
VBAの仕様上、
配列やユーザ定義型などの一部の型の変数は、
Publicのスコープで宣言することができるのは標準モジュールに限られる。
それは、インタフェースで使用するクラスであっても同様のようである。
そのため、クラスのPrivateな配列に外部からアクセスできる仕組みを作らなければならない。
回避方法
インタフェースにはgetter/setterのみ定義し、
継承先のクラスにPrivateなスコープの配列変数を所持させて、
継承したgetter/setterを通してアクセスできるようにする。
1'*** clsAbsTeam ***
2
3Option Explicit
4
5'**************************
6'*チームクラスインタフェース
7'**************************
8
9'定数欄
10
11'変数欄
12
13
14'******************************************************************************************
15'*getter/setter欄
16'******************************************************************************************
17Public Property Let arrayMenberName(ByVal idx As Long, ByVal name As String)
18
19End Property
20
21Public Property Get arrayMenberName(ByVal idx As Long) As String
22
23End Property
24
25
26
27
28Public Function getMemberName(ByVal idx As Long) As String
29
30End Function
31
1'*** clsAnalyzeTeam ***
2
3Option Explicit
4
5Implements clsAbsTeam
6
7'**************************
8'*チームクラス 解析チーム
9'**************************
10
11'定数欄
12
13'変数欄
14Private myArrayMenberName(1 To 6) As String
15
16
17
18'******************************************************************************************
19'*getter/setter欄
20'******************************************************************************************
21Private Property Let clsAbsTeam_arrayMenberName(ByVal idx As Long, ByVal name As String)
22 myArrayMenberName(idx) = name
23End Property
24
25Private Property Get clsAbsTeam_arrayMenberName(ByVal idx As Long) As String
26 clsAbsTeam_arrayMenberName = myArrayMenberName(idx)
27End Property
28
29
30
31'******************************************************************************************
32'*関数名 :clsAbsTeam_getMemberName
33'*機能 :チームメンバーの名前取得
34'*引数 :対象者のインデックス番号
35'*戻り値 :チームメンバーの名前
36'******************************************************************************************
37Private Function clsAbsTeam_getMemberName(ByVal idx As Long) As String
38
39 '定数
40
41 '変数
42
43 clsAbsTeam_getMemberName = idx & "番目のチームメンバーは" & myArrayMenberName(idx) & "です。"
44
45
46ExitHandler:
47
48 Exit Function
49
50End Function
51
1'*** clsNewTeam ***
2
3Option Explicit
4
5Implements clsAbsTeam
6
7'**************************
8'*チームクラス 新設のチーム
9'**************************
10
11'定数欄
12
13'変数欄
14
15
16'******************************************************************************************
17'*getter/setter欄
18'******************************************************************************************
19Private Property Let clsAbsTeam_arrayMenberName(ByVal idx As Long, ByVal name As String)
20 '何もしない
21End Property
22
23Private Property Get clsAbsTeam_arrayMenberName(ByVal idx As Long) As String
24 clsAbsTeam_arrayMenberName = "名前無し"
25End Property
26
27'******************************************************************************************
28'*関数名 :clsAbsTeam_getMemberName
29'*機能 :チームメンバーの名前取得
30'*引数 :対象者のインデックス番号
31'*戻り値 :チームメンバーの名前
32'******************************************************************************************
33Private Function clsAbsTeam_getMemberName(ByVal idx As Long) As String
34
35 '定数
36
37 '変数
38
39 clsAbsTeam_getMemberName = "※新設のチームにはメンバーが存在しません。"
40
41
42ExitHandler:
43
44 Exit Function
45
46End Function
47
上記のようにしてやれば、
エラーを回避し、なおかつ予期した動作と同じ動作をする配列を継承先クラスに持たせることができる。
なお、新設チームクラスの場合はメンバーが居ないため、
配列変数自体をクラスに持たせる必要がなく、持たせていない。
サンプル
クラス図
コード(M_Calling)
1'*** M_Calling ***
2
3Option Explicit
4
5'**************************
6'*呼び出し元モジュール
7'**************************
8
9
10'******************************************************************************************
11'*関数名 :output3rdMemberName
12'*機能 :動作テスト
13'*引数 :
14'*戻り値 :True > 正常終了、False > 異常終了
15'******************************************************************************************
16Public Sub testFunc()
17
18 '定数
19 Const FUNC_NAME As String = "testFunc"
20
21 '変数
22 Dim team As clsAbsTeam
23 Dim coll As New Collection
24
25 On Error GoTo ErrorHandler
26
27 '解析チームの名前を設定する
28 Set team = New clsAnalyzeTeam
29 team.arrayMenberName(1) = "佐藤"
30 team.arrayMenberName(3) = "Mike"
31 team.arrayMenberName(5) = "梦蝶"
32
33 '処理するチームを追加
34 coll.Add team
35 coll.Add New clsNewTeam
36
37 '名前を出力
38 If Not outputSelectedMemberName(coll, 3) Then GoTo ExitHandler
39
40ExitHandler:
41
42 Exit Sub
43
44ErrorHandler:
45
46 MsgBox "エラーが発生したため、マクロを終了します。" & _
47 vbLf & _
48 "関数名:" & FUNC_NAME & _
49 vbLf & _
50 "エラー番号:" & Err.Number & vbNewLine & _
51 Err.Description, vbCritical, "Interface-Array-Member"
52
53 GoTo ExitHandler
54
55End Sub
56
57
58
59'******************************************************************************************
60'*関数名 :outputSelectedMemberName
61'*機能 :引数のコレクションの各チームの「idx」番目のメンバー名前を出力する
62'*引数 :
63'*戻り値 :True > 正常終了、False > 異常終了
64'******************************************************************************************
65Private Function outputSelectedMemberName(ByVal collTeam As Collection, ByVal idx As Long) As Boolean
66
67 '定数
68 Const FUNC_NAME As String = "outputSelectedMemberName"
69
70 '変数
71 Dim cntTeam As clsAbsTeam
72
73 On Error GoTo ErrorHandler
74
75 outputSelectedMemberName = False
76
77 For Each cntTeam In collTeam
78 Debug.Print cntTeam.getMemberName(idx)
79 Next cntTeam
80
81TruePoint:
82
83 outputSelectedMemberName = True
84
85ExitHandler:
86
87 Exit Function
88
89ErrorHandler:
90
91 MsgBox "エラーが発生したため、マクロを終了します。" & _
92 vbLf & _
93 "関数名:" & FUNC_NAME & _
94 vbLf & _
95 "エラー番号:" & Err.Number & vbNewLine & _
96 Err.Description, vbCritical, "Interface-Array-Member"
97
98 GoTo ExitHandler
99
100End Function
101
102
実行
テスト関数testFunc
を実行すると、
イミディエイトウィンドウに次のように出力される。
13番目のチームメンバーはMikeです。
2※新設のチームにはメンバーが存在しません。
これにより、
インタフェースを実装したクラスでも
クラス変数として配列をもたせることが可能となることがわかった。
関連記事
- [VBA] クラスを利用するメリットと方法について & 簡単なサンプル(2)
- [VBA] クラスを利用するメリットと方法について & 簡単なサンプル(1)
- [Access VBA] フォームのコントロールに対してWithEventsでイベントリスナークラスを作成する際に気をつけなければならないこと
- [Excel VBA]ポリモーフィズムを用いて、IF文を使わずラジオボタンごとの処理分岐を行う
- [VBA, PowerShell] Accessのモジュール・クラスやクエリのSQLから特定文字列を抽出するためのテクニック