ユーザー定義型の制限とクラスとの使い分け
VBAにはユーザー定義型(Type)があり、複数の要素(複数のデータ型)を含むデータ型を定義できます。
複数の値をひとまとめで扱う方法として配列がありますが、配列は同じ型の値しか扱うことができませんが、
ユーザー定義型の変数には、文字列型、数値型等々の複数のデータ型をひとまとめにして入れることができます。
また、
ユーザー定義型の機能は、クラスにインスタンス変数(Public変数やPublicプロパティ)を作ることで同じことができます。
ユーザー定義型
以降の説明で使うユーザー定義型は以下になります。
Public Type TypePerson
番号 As Long
名前 As String
住所 As String
End Type
標準モジュールのモジュールレベルに記述します。
クラスについて
以降の説明で使うクラスは以下になります。
Option Explicit
Public 番号 As Long
Public 名前 As String
Public 住所 As String
クラスのオブジェクト名:clsPerson
ユーザー定義型とクラスの速度比較
100万件の配列での比較をしています。
計測時間については、複数回実行の平均値ですが、
そもそも、PC環境によりますので、あくまで対比としての参考です。
Sub type_sample1()
Dim sTime As Double
sTime = Timer
Dim tPerson() As TypePerson
ReDim tPerson(1 To 1000000)
Dim i As Long
For i = 1 To 1000000
tPerson(i).番号 = i
tPerson(i).名前 = "名前" & i
tPerson(i).住所 = "住所" & i
Next
Debug.Print Timer - sTime
End Sub
Sub class_sample1()
Dim sTime As Double
sTime = Timer
Dim cPerson() As New clsPerson
ReDim cPerson(1 To 1000000)
Dim i As Long
For i = 1 To 1000000
cPerson(i).番号 = i
cPerson(i).名前 = "名前" & i
cPerson(i).住所 = "住所" & i
Next
Debug.Print Timer - sTime
End Sub
ユーザー定義型:type_sample1
0.3875秒
2.0453秒
クラスは、インスタンス作成の時間がかかるので仕方ありません。
とはいえ、100万回で2秒ですから、通常は処理時間として考慮するレベルのものではないと思います。
上記VBAは、Forループ内で
Set cPerson() = New clsPerson
このようにしても、処理時間は変わりません。
この記事を書くためにテストVBAを実行した範囲の事ですが、
クラスのVBAは実行後に何度かエクセルが反応なしになっています。
PC環境の問題もあると思いますが、VBAのメモリ解放に問題があるかもしれません。
Sub type_sample2()
Dim sTime As Double
sTime = Timer
Dim tPerson() As TypePerson
ReDim tPerson(1 To 1000000)
Dim i As Long
For i = 1 To 1000000
tPerson(i) = type_sub2(i)
Next
Debug.Print Timer - sTime
End Sub
Function type_sub2(i As Long) As TypePerson
type_sub2.番号 = i
type_sub2.名前 = "名前" & i
type_sub2.住所 = "住所" & i
End Function
Sub class_sample2()
Dim sTime As Double
sTime = Timer
Dim cPerson() As clsPerson
ReDim cPerson(1 To 1000000)
Dim i As Long
For i = 1 To 1000000
Set cPerson(i) = class_sub2(i)
Next
Debug.Print Timer - sTime
End Sub
Function class_sub2(i As Long) As clsPerson
Set class_sub2 = New clsPerson
class_sub2.番号 = i
class_sub2.名前 = "名前" & i
class_sub2.住所 = "住所" & i
End Function
ユーザー定義型:type_sample2
0.6734秒
2.1203秒
対して、クラスではほとんど時間の変化がありません。
ユーザー定義型は、データを値で渡していて(つまりデータをコピーしている)、
クラスはオブジェクトへの参照を渡している事に起因します。
クラスはオブジェクトとして、そのオブジェクトのアドレスを渡しているのでループ内で直接入れている場合とほとんど差が無いという事です。
Sub type_sample3()
Dim sTime As Double
sTime = Timer
Dim tPerson() As TypePerson
ReDim tPerson(1 To 1000000)
Dim i As Long
For i = 1 To 1000000
Call type_sub3(tPerson(i), i)
Next
Debug.Print Timer - sTime
End Sub
Sub type_sub3(ByRef arg As TypePerson, i As Long)
arg.番号 = i
arg.名前 = "名前" & i
arg.住所 = "住所" & i
End Sub
Sub class_sample3()
Dim sTime As Double
sTime = Timer
Dim cPerson() As clsPerson
ReDim cPerson(1 To 1000000)
Dim i As Long
For i = 1 To 1000000
Call class_sub3(cPerson(i), i)
Next
Debug.Print Timer - sTime
End Sub
Sub class_sub3(arg As clsPerson, i As Long)
Set arg = New clsPerson
arg.番号 = i
arg.名前 = "名前" & i
arg.住所 = "住所" & i
End Sub
ユーザー定義型:type_sample3
0.4250秒
2.0703秒
どちらも引数で参照渡ししているので、アドレスを渡す事以外は差が無いという事です。
type_sample2のような戻り値を使うのではなく、
type_sample3のようにByRefでの使い方をしたほうが良いでしょう。
この制限は、配列と同様になります。
ByRef指定にするか、指定を省略してください。
ユーザー定義型の制限
ユーザー定義型はVariant変数に入れることはできません
Sub type_sample4()
Dim v
Dim tPerson As TypePerson
v = tPerson
End Sub
これはコンパイルエラーになります。
同じユーザー定義型変数同士であれば入れることはできます。
クラスはVariant変数に入れられます
Sub type_sample6()
Dim dic
Set dic = CreateObject("Scripting.Dictionary")
Dim tPerson As TypePerson
dic.Add "key", tPerson
End Sub
クラスなら問題ありません。
このような場合はクラスを使用してください。
ユーザー定義型はCollectionに入れることはできません
Sub type_sample5()
Dim col As New Collection
Dim tPerson As TypePerson
col.Add tPerson
End Sub
これもコンパイルエラーになります。
クラスはCollectionに入れられます
Sub class_sample5()
Dim col As New Collection
Dim cPerson As clsPerson
col.Add cPerson
End Sub
クラスなら問題ありません。
このような場合はクラスを使用してください。
ユーザー定義型はDictionaryに入れることはできません
Sub type_sample6()
Dim dic
Set dic = CreateObject("Scripting.Dictionary")
Dim tPerson As TypePerson
dic.Add "key", tPerson
End Sub
これもコンパイルエラーになります。
クラスはDictionaryに入れられます
Sub class_sample6()
Dim dic
Set dic = CreateObject("Scripting.Dictionary")
Dim cPerson As New clsPerson
dic.Add "key", cPerson
End Sub
クラスなら問題ありません。
このような場合はクラスを使用してください。
ユーザー定義型の制限とクラスとの使い分け
ただし、あくまでオブジェクトを作成することと、データ型を定義することの違いは理解しておきましょう。
インスタンス作成したオブジェクト変数を他のオブジェクト変数に入れた場合、
実体のオブジェクトは同じもので、オブジェクトがコピーされるわけではありません。
つまり、オブジェクトへの入り口をコピーしたにすぎません。
対して、ユーザー定義型はあくまでデータです。
ユーザー定義型の変数を他の同じユーザー定義型の変数に入れた場合はデータがコピーされます。
ここを理解した上で、ユーザー定義型とクラスを使い分けることが最も重要です。
逆の見方をすれば、この違いを意識する必要がないのであれば、
ユーザー定義型の制限だけ意識していれば、都度どちらを使っても大差ないとも言えます。
同じテーマ「マクロVBA技術解説」の記事
VBAにおける配列やコレクションの起点について
VBAのマルチステートメント(複数のステートメントを同じ行に)
クリップボードに2次元配列を作成してシートに貼り付ける
ユーザー定義型の制限とクラスとの使い分け
シングルクォートの削除とコピー(PrefixCharacter)
空文字列の扱い方と処理速度について(""とvbNullString)
VBAにおける変数のメモリアドレスについて
Evaluateメソッド(文字列の数式を実行します)
Rangeオブジェクトの論理演算(差集合と排他的論理和)
VBAで写真の撮影日時や音楽動画の長さを取得する
VBAでWindowsMediaPlayerを使い動画再生する
新着記事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.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.繰り返し処理(For Next)|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入門
- ホーム
- マクロVBA応用編
- マクロVBA技術解説
- ユーザー定義型の制限とクラスとの使い分け
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。