20240122エクセルVBA オブジェクト指向備忘録クラス001
エクセルVBA オブジェクト指向備忘録(クラスの書き方の比較)
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
エクセルVBAで、オブジェクト指向を必要とすることもなかったので、諦めていましたが、
youtubeで、「ひとりごとワンダーランド」様のエクセルVBAでのクラスに関する動画を見させていただき、
少しだけやる気になっています。感謝。
(まだ、動画は最後まで見ていません。汗)
で、「ひとりごとワンダーランド」様が強調していることの一つに、スコープ「public」と「private」についての使い方がありました。
よく変数の宣言に、publicを使っているものが散見されるが、privateがいいのではないかとの意見。
そうしないと、変数がどこからでも書き換えられて、クラスにした意味(意義)が半減するような指摘がありました。
(カプセル化したのに、意味がない。)
で、並行して、ネットHPで「いつも隣にITのお仕事」様も見ている。
(これも途中までしか見てません。汗)
(注:まだ途中です。最後まで読んだら、もしかして、ここに書いた内容(捉え方・考え方)が変わるかもしれません。)(自分用メモなのでご容赦下さい。)
愚弟的には、「いつも隣にITのお仕事」様の方は、「エクセルVBAでクラスモジュールを使って独自のコレクションを作る方法」まで見ています。
そこでは、下記のように、プログラムを書いています。
(もし不都合ありましたら、削除致します。)
一部、private変数(Id)もありますが、基本的にpublic変数を使用しています。
(注:このページは、独自のコレクションを使うと書いてあるように、初心者には、理解がややこしいトコです。クラスを新たに作ってその中にコレクションを置く・・・。)
標準モジュール//////////////////////①
Sub Mysub()
Dim myPersons As Persons
Set myPersons = New Persons
Set myPersons.Items = New Collection
With Sheet1
Dim i As Long
i = 2
Do While .Cells(i, 1).Value <> ""
Dim p As person
Set p = New person
p.Initialize .Range(.Cells(i, 1), .Cells(i, 4))
myPersons.Items.Add p, p.Id
i = i + 1
Loop
End With
Stop
End Sub
クラスPerson///////////////////////②(②は今回のテーマとは関係ありません)
Private Id_ As String 'privateにしている。他の変数はpublic
Public FirstName As String
Public Gender As String
Public Birthday As Date
Public Sub Initialize(ByVal rng As Range)
Id_ = rng(1).Value
FirstName = rng(2).Value
Gender = rng(3).Value
Birthday = rng(4).Value
End Sub
Public Property Let Id(ByVal newId As String)
If Id_ <> "" Then 'もしif文なければ、何回もアクセス可能
Debug.Print "Idは上書きすることはできません"
Else
Id_ = newId
End If
End Property
Public Property Get Id() As String
Id = Id_
End Property
クラスPersons/////////////////////////////③
Public Items As Collection
①②③の構成になっている。特に③にpublicを使っている。
なお、「いつも隣にITのお仕事」様は、お作法として、クラス内のprivate変数は、「Id_」のように、後ろにアンダーバーを入れています。
インスタンスに使う変数はpなどでシンプルです。
これだけで、クラスPersonsのItemsがコレクションとして使用可能になっている。※
(※この章では、コレクションを新たなクラス(クラス②)で作って、オブジェクトとして使用するというややこしいことをしています。)
**********************************************************
上記を、「ひとりごとワンダーランド」様のprivate変数を多用したパターンに書き換える。
(自分で考えたので、間違っていたらスミマセン。)
標準モジュール//////////////////////①
Sub Mysub()
Dim vPersons As Persons '独自クラス
Set vPersons = New Persons 'クラス生成
With Sheet1
Dim i As Long
i = 2
Do While .Cells(i, 1).Value <> ""
Dim vPerson As person
Set vPerson = New person '毎回セット
vPerson.Initialize .Range(.Cells(i, 1), .Cells(i, 4))
vPersons.Items.Add vPerson, vPerson.Id
i = i + 1
Loop
End With
Stop
End Sub
クラスPerson///////////////////////②(②は今回のテーマとは関係ありません)
Private mId As String
Private mFirstName As String
Private mGender As String
Private mBirthday As Date
Public Sub Initialize(rng As Range) 'クラスinitializeではなくメソッドで初期化
'(VBAのクラスinitializeは、(コンストラクタ)引数持てないらしい?)
mId = rng(1).Value
mFirstName = rng(2).Value
mGender = rng(3).Value
mBirthday = rng(4).Value
End Sub
Property Get Id() As String
Id = mId
End Property
Property Get FirstName() As String
FirstName = mFirstName
End Property
Property Get Gender() As String
Gender = mGender
End Property
Property Get Birthday() As Date
Birthday = mBirthday
End Property
Property Let Id(pId As String)
If mId <> "" Then
Debug.Print "IDは上書き不可"
Else
mId = pId
End If
End Property
Property Let FirstName(pFirstName As String)
mFirstName = pFirstName
End Property
Property Let Gender(pGender As String)
mGender = pGender
End Property
Property Let Birthday(pBirthday As Date)
mBirthday = pBirthday
End Property
クラスPersons/////////////////////////////③
Private mItems As Collection 'privateにする
Private Sub Class_Initialize()
Set mItems = New Collection
End Sub
Property Set Items(pItems As Collection)
Set mItems = pItems
End Property
Property Get Items() As Collection
Set Items = mItems
End Property
**********************************************************
上記が、「ひとりごとワンダーランド」様風のprivate変数を多用したパターン。
(違っていたらすみません。)
「ひとりごとワンダーランド」様は、お作法として、基本的に、標準モジュールの変数は、
Set vPersons = New Personsのように、vを付ける。
また、クラス内変数等は、
Property Let Id(pId As String)
mId = pId
End Property
のように、渡ってくる変数にはp、
クラス内変数は、mを付けている。
で、通常、変数をprivateでやり取りしようとすると、
letやgetが必要だが、
今回は、クラス内で、コレクションを扱うため、
setやget以外に、
Private Sub Class_Initialize()
Set mItems = New Collection
End Sub
というように、Class_initializeで、コレクションの定義を行った。
で、いいのかな・・・。汗
追加メモ:(間違っているかも)
propertyは、関数のようなものなのだろう。
mainでクラスが作られて、呼ばれれば必ずそこを通る。
関数っぽいので色んなところから呼ばれても、if文などで、書き換えに制限を加えることができる。
今まで自分は、「手続き型」のコードしか書いてこなかったので、クラスには違和感あるし、今まで作ってきたものをリファクタリングするほど、手慣れることもなさそうだが、違った発想に感じられるので、面白い気もします・・・汗。
追加メモ:(間違っているかも)
propertyは、関数のようなものなのだろう。
mainでクラスが作られて、呼ばれれば必ずそこを通る。
関数っぽいので色んなところから呼ばれても、if文などで、書き換えに制限を加えることができる。