エクセルVBAクラス備忘録(プロシージャの呼び方callカッコ、クラス変数の受け渡し)

20240724エクセル備忘録(プロシージャの呼び方callカッコ、クラス変数の受け渡し)

<前書き>
エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。
<以上前書き>

(素人なので間違っているかもしれません。汗。)

===========================================
今回は、基本的なことのメモです。自分用の覚えです。
回答は、chatGPT様にお聞きしたので、間違っているかもしれません。
(不都合があれば削除いたします。)(回答や例も少し直しています。)


【メモ1】プロシージャの呼び方(callカッコ)
クラスのことでもないです。
プロシージャの呼び方です。基本です。


例:
 Initialize(hoge1,hoge2)
としたら、「=がありません」のエラー。
実際は、 Initialize hoge1,hoge2
か、
call Initialize(hoge1,hoge2)
で動く。
callを使う場合は、引数はカッコでくくることを覚えておこう。(丁寧に書く場合)


GPT様回答
VBAでは、メソッドやプロシージャの引数を渡すときに、通常はカッコでくくらないのが正しいです。ただし、関数呼び出しで戻り値を受け取る場合や特定の状況ではカッコを使用します。


【メモ2】クラス変数の受け渡し
chatGPT様が悪いのでなく、自作のクラスとくっつけようとしたら、おかしくなりました。

chatGPT様が作ったメソッドですが、クラスを通る際に、変数の受け渡しができなかったです。
*********************************
' Tripクラスの定義
Class Trip
    Public TripID As Long
    Public TripStatus As String
    Public EmployeeName As String
    Public Destination As String
    Public StartDate As Date
    Public EndDate As Date

    ' 初期化メソッド
    Public Sub Initialize(id As Long, status As String, name As String, destination As String, startDate As Date, endDate As Date)
        TripID = id
        TripStatus = status
        EmployeeName = name
        Destination = destination
        StartDate = startDate
        EndDate = endDate
    End Sub
(部分的に省略)

下の以前に作ったクラスにくっつけました。
*******************************
' クラスモジュール: 
Option Explicit

Private pTripID As Long
Private pEmployeeName As String
Private pDestination As String
Private pStartDate As Date
Private pEndDate As Date
Private pTripStatus As String


' プロパティの定義
'Public Property Get tripID() As Long
Public Property Get tripID() As String
    tripID = pTripID
End Property

Public Property Let tripID(ByVal value As String)
    pTripID = value
End Property

Public Property Get EmployeeName() As String
    EmployeeName = pEmployeeName
End Property

Public Property Let EmployeeName(ByVal value As String)
    pEmployeeName = value
End Property

Public Property Get destination() As String
    destination = pDestination
End Property

Public Property Let destination(ByVal value As String)
    pDestination = value
End Property

Public Property Get startDate() As Date
    startDate = pStartDate
End Property

Public Property Let startDate(ByVal value As Date)
    pStartDate = value
End Property

Public Property Get endDate() As Date
    endDate = pEndDate
End Property

Public Property Let endDate(ByVal value As Date)
    pEndDate = value
End Property

Public Property Get TripStatus() As String
    TripStatus = pTripStatus
End Property

Public Property Let TripStatus(value As String)
    pTripStatus = value
End Property

    ' 初期化メソッド
    Public Sub Initialize(id As Long, status As String, name As String, destination As String, startDate As Date, endDate As Date)
        TripID = id
        TripStatus = status
        EmployeeName = name
        Destination = destination
        StartDate = startDate
        EndDate = endDate
    End Sub

(部分的に省略)
-----------------------------------------
これを動かすと、TripID,TripStatus,EmployeeNameは変数がクラスに受け渡させるのですが、 Destination,StartDate,EndDateは、途中で消えてしまいます。

同じ変数名(VBAは大文字・小文字区別しない)の受け渡し(左辺=右辺)だと、プロパティを通らないのかなと想像したり・・・。

 

解決法としては、当然ながら、このクラスは、クラス内でプライベート変数を使っているので、
        pTripID = id
        pTripStatus = status
        pEmployeeName = name
        pDestination = destination
        pStartDate = startDate
        pEndDate = endDate
にしてあげればいいです。
受け渡しできる変数とできない変数があるので、なんとなくモヤモヤしましたが、基本通りにすればいいと思いました。

 

GPT様回答
VBAでは、クラスモジュール内のプロパティと引数の名前が同じ場合、名前の競合が発生し、予期しない動作をすることがあります。
具体的には、メソッドの引数とクラスのプロパティが同じ名前の場合、VBAはローカル変数(引数)を優先して解釈します。
そのため、クラスのプロパティに正しい値が代入されないことがあります。

プライベート変数(フィールド)に「p」を付けて区別するのは良い習慣であり、名前の競合を避けるための方法です。
この方法を採用することで、変数がどのスコープに属しているのかが明確になり、意図した動作が得られます。

---------------------------------
ということでした。
クラスモジュール内のプロパティと引数の名前が同じ場合、「予期しない動き」ということと、「VBAはローカル変数(引数)を優先して解釈」ということでなんとなく理解できた気もします。


【メモ2】の経験をしたことで、何となくクラスのカプセル化の重要性が分かった気もします。
(今まで、Public Property Letとか、Public Property Getとか、面倒だなと思ってました。今回、おかしな動きをしたことで、プロパティも注意しようと思いました。)

その他:
ここには、あまり書かないが、そのうちきちんと書こうと思ったことのメモ。
手続き型だと、本流のモジュール(メインモジュール)には、数々の変数が存在している。
(↑これをどう呼べばいいか分からないが、勝手に「底流変数」と命名。)
オブジェクト指向では、極力「底流変数」をなくした方がいい。
というか、ずっと底流変数を流し続けないのが、オブジェクト指向のキモかと思っています。