エクセルVBAクラス備忘録 メイン文ひな形

20240422 エクセルVBAクラス備忘録 メイン文ひな形

 

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

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


前回、メイン処理について、思ったことのメモの続きです。
前回、【自分なりのルール】を考えるということで、

●クラスを使う場合、なるべくメイン文で、クラスをインスタンス化する
(一度インスタンス化したデータをメイン文内で追えるようにしておく)
●そのインスタンス化したデータを利用して、他の処理につなげる場合は、functionでメイン文にデータを持ってくる。
(よく利用するデータは深いところで処理しない(処理してもfunctionでメインに戻す))
●そのインスタンス化したデータを利用しない場合(一度切りの処理・中間処理など)、メインに戻さないでよい。
(クラスメソッドでインスタンス化して、そこで終了)(インスタンスが消える処理すればなお良い)
とか、書きました。

 

以下に、メイン文の書き方を意識したひな形のようなコードを書きます。
(どんどんややこしくなります。)
(変数名もややこしくてすみません。)

 

<ひな形1>(基本形)
Sub メイン文ひな形1()

    Dim vクラス_sub As クラス_sub
    Set vクラス_sub = New クラス_sub

    vクラス_sub.値_sub = "hello_sub"
    
    Debug.Print vクラス_sub.値_sub
    
    Dim vクラス_func As クラス_func
    Set vクラス_func = New クラス_func

   
     vクラス_func.判定_number = 6
    
     Dim クラス_func_戻り値_main As Variant
    
    クラス_func_戻り値_main = vクラス_func.割り算(vクラス_func.判定_number)
    Debug.Print クラス_func_戻り値_main

End Sub
//////////////////////////////
'クラス_sub
Option Explicit

Private m値_sub As Variant

'let
Public Property Let 値_sub(p値_sub As Variant)
     m値_sub = p値_sub
End Property

'get
Public Property Get 値_sub() As Variant
     値_sub = m値_sub
End Property
//////////////////////////////
'クラス_func
Option Explicit

Private m判定_number As Variant

'let
Public Property Let 判定_number(p判定_number As Variant)
     m判定_number = p判定_number
End Property

'get
Public Property Get 判定_number() As Variant
     判定_number = m判定_number
End Property

Function 割り算(pクラス_func_引数) As Variant

        割り算 = pクラス_func_引数 / 2

End Function
//////////////////////////////
メイン文で、クラス_subと'クラス_funcをインスタンス化して実行しています。
クラス_subは、戻り値なしで、メイン文には、引続き使用するデータはありません。
(うまく言えませんがDebug.Printするだけ。
まあ、vクラス_sub.値_subは持っているのですが。)

'クラス_funcは、functionで戻り値を使って、メイン文に値を戻しています。
(メイン文で宣言した、クラス_func_戻り値_mainを持ちます。
これで自由にメイン文で使用できます。)

=========================================================
<ひな形2>失敗?(あ、セッター通らない)
Sub メイン文ひな形2()

    Dim vクラス_値 As クラス_値
    Set vクラス_値 = New クラス_値
   
    vクラス_値.判定_number = 6
    
    Dim クラス_値_戻り値_main As Variant
    
    クラス_値_戻り値_main = vクラス_値.判定_number    'funcではないが
    
    Debug.Print クラス_値_戻り値_main
    
   
    Dim vクラス_func As クラス_func
    Set vクラス_func = New クラス_func
 
    'メインに戻す
    
     Dim クラス_func_戻り値_main As Variant
     
    
    クラス_func_戻り値_main = vクラス_func.割り算(クラス_値_戻り値_main)  'propertyletは外部から入る時。これだと直接メソッドに入る・・・

    
    Debug.Print クラス_func_戻り値_main

End Sub
/////////////////////////////////////
'クラス「クラス_値」
Option Explicit

Private m判定_number As Variant

'let
Public Property Let 判定_number(p判定_number As Variant)
     m判定_number = p判定_number
End Property

'get
Public Property Get 判定_number() As Variant
     判定_number = m判定_number
End Property
/////////////////////////////////////
'クラス「クラス_func」
Option Explicit

Private mクラス_値_戻り値 As Variant

'let
Public Property Let クラス_値_戻り値(pクラス_値_戻り値 As Variant)   '通らない
     mクラス_値_戻り値 = pクラス_値_戻り値
End Property

'get
Public Property Get クラス_値_戻り値() As Variant    '通らない
     クラス_値_戻り値 = mクラス_値_戻り値
End Property


Function 割り算(pクラス_値_戻り値) As Variant
        mクラス_値_戻り値 = pクラス_値_戻り値

        割り算 = mクラス_値_戻り値 / 2

End Function
//////////////////////////////////
ひな形2では、'クラス「クラス_値」というクラスを1個作りました。
「クラス_func」と「クラス_値」の二つのクラスを親子にして、メインから呼ぶための前段階として作りました。
(何も実装していませんが、「クラス_値」で、数字や文字判定などしてもいいかなとか思ったりしました。)
この段階では、「クラス_func」も「クラス_値」メインに直接入れて、インスタンス化しています。

でも、このコードだと、「クラス_func」のProperty Let クラス_値_戻り値、Property Get クラス_値_戻り値を通りません。

クラス_func_戻り値_main = vクラス_func.割り算(クラス_値_戻り値_main) 

の「クラス_値_戻り値_main」は、メイン文のただの変数なので、セッターもゲッターも通らず、スルーです。
(あまり実例を知りませんが、ただの値が入る変数は、セッターやゲッターを通した方がいいのかなと思います。)
(中身が、コレクションや配列やシートなどのオブジェクトは、セッターやゲッターを通さなくてもいいのか??不明)

で、どうするか?

=========================================================
<ひな形3>(セッターを通すコード)
Sub メイン文ひな形3()
 
    Dim vクラス_値 As クラス_値
    Set vクラス_値 = New クラス_値
   
    vクラス_値.判定_number = 6
      
    Dim クラス_値_戻り値_main As Variant
    
    クラス_値_戻り値_main = vクラス_値.判定_number    'funcではないが
    
    Debug.Print クラス_値_戻り値_main
    
   
    Dim vクラス_func As クラス_func
    Set vクラス_func = New クラス_func
 
 
     Dim クラス_func_戻り値_main As Variant
     
    vクラス_func.クラス_値_戻り値 = クラス_値_戻り値_main
    クラス_func_戻り値_main = vクラス_func.割り算(vクラス_func.クラス_値_戻り値)
    
    Debug.Print クラス_func_戻り値_main

End Sub
////////////////////////////////////
'クラス「クラス_値」
Option Explicit
Private m判定_number As Variant

'let
Public Property Let 判定_number(p判定_number As Variant)
     m判定_number = p判定_number
End Property

'get
Public Property Get 判定_number() As Variant
     判定_number = m判定_number
End Property
//////////////////////////////////////
'クラス「クラス_func」
Option Explicit
Private mクラス_値_戻り値 As Variant

'let
Public Property Let クラス_値_戻り値(pクラス_値_戻り値 As Variant)
     mクラス_値_戻り値 = pクラス_値_戻り値
End Property

'get
Public Property Get クラス_値_戻り値() As Variant
     クラス_値_戻り値 = mクラス_値_戻り値
End Property


Function 割り算(pクラス_値_戻り値) As Variant
        mクラス_値_戻り値 = pクラス_値_戻り値

        割り算 = mクラス_値_戻り値 / 2

End Function
////////////////////////////////////
<ひな形3>では、
    vクラス_func.クラス_値_戻り値 = クラス_値_戻り値_main
    クラス_func_戻り値_main = vクラス_func.割り算(vクラス_func.クラス_値_戻り値)
として、割り算メソッドの引数をクラスの変数にしました。
上段の
vクラス_func.クラス_値_戻り値 = クラス_値_戻り値_main
で、Property Let クラス_値_戻り値を通り、
下段の
クラス_func_戻り値_main = vクラス_func.割り算(vクラス_func.クラス_値_戻り値)
で、Property Get クラス_値_戻り値を通った後、
Function 割り算を実行します。

まあ、セッターとゲッターを通したかっただけです。汗。

===================================================
<ひな形4>(クラスを子・孫にする)(メイン文(親)-クラス(子)-クラス(孫)にする)

階層としては、
・メイン文(親)
 ・子クラス(メイン文に子クラス「クラス_値」の結果を戻す)
  ・孫クラス(孫「クラス_func」の結果は、子クラス「クラス_値」に返す)

Option Explicit
Sub メイン文ひな形4()
 
    Dim vクラス_値 As クラス_値
    Set vクラス_値 = New クラス_値
   
   
    vクラス_値.判定_number = 6     '・・・①
    
    
    Dim クラス_値_戻り値_main As Variant
    クラス_値_戻り値_main = vクラス_値.処理_func(vクラス_値.判定_number) '・・・③
    
    
    Debug.Print クラス_値_戻り値_main '・・・⑨

End Sub
///////////////////////////////////
'クラス「クラス_func」
Option Explicit
Private mクラス_値_戻り値 As Variant

'let
Public Property Let クラス_値_戻り値(pクラス_値_戻り値 As Variant) '・・・⑥
     mクラス_値_戻り値 = pクラス_値_戻り値
End Property

'get
Public Property Get クラス_値_戻り値() As Variant  '・・・⑦ 
     クラス_値_戻り値 = mクラス_値_戻り値
End Property


Function 割り算(pクラス_値_戻り値) As Variant '・・・⑧
        mクラス_値_戻り値 = pクラス_値_戻り値

        割り算 = mクラス_値_戻り値 / 2

End Function
/////////////////////////////////////
'クラス「クラス_値」
Option Explicit
Private m判定_number As Variant

'let
Public Property Let 判定_number(p判定_number As Variant) '・・・②
     m判定_number = p判定_number
End Property

'get
Public Property Get 判定_number() As Variant '・・・④
     判定_number = m判定_number
End Property


Function 処理_func(p判定_number As Variant) As Variant '・・・⑤
    
    Dim vクラス_func As クラス_func
    Set vクラス_func = New クラス_func
    vクラス_func.クラス_値_戻り値 = p判定_number

    処理_func = vクラス_func.割り算(vクラス_func.クラス_値_戻り値)

End Function
////////////////////////////////////////
番号を振った順①~⑨に実行します。
「クラス_値」(子)で、「クラス_func」(孫)を呼んでいますが、結果は、functionでメイン文(親)に戻しています。


素人なので、間違ってたらすみません。汗

追記:
変数の名づけ方は、本当に分かっていない。
ドメインオブジェクトは、プロパティやコレクションなど「変数の型」を定義した入れ物(タイ焼きの型)だと思った方がいいか。
その他のつなぎのオブジェクト(クラス)は、メソッドとかが入っているツール。

 

エクセルVBAクラス備忘録 メイン文の使い方

20240421 エクセルVBAクラス備忘録 メイン文の使い方

 

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

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


今回、メイン処理について、思ったことのメモです。
大事な気がするので、きちんと書きたかったのですが、まとめる時間がなく(能力もなく)、なんとなくのイメージです。

何回か前の備忘録で、「メイン文に「全部のドメインオブジェクト(重要なクラスのインスタンス)」が見えないのは、分かりにくいのでは?」
といった主旨のことを書いて、メイン文に、ドメインオブジェクトを記載した回があります。

 

その後、考えたのですが、
メインから、クラスを呼んで、さらにそこから、他のクラスを呼ぶことはある。
・・・その場合、そうするのか?
とか悩みます。(まあ、自分が分かりやすかったらいいだけですが。)

 

で、【自分なりのルール】を考える。
今現在のスキルで考えていることやコードを書く上で、当たり前のことですが、メモ。


●クラスを使う場合、なるべくメイン文で、クラスをインスタンス化する
(一度インスタンス化したデータをメイン文内で追えるようにしておく)
●そのインスタンス化したデータを利用して、他の処理につなげる場合は、functionでメイン文にデータを持ってくる。
(よく利用するデータは深いところで処理しない(処理してもfunctionでメインに戻す))
●そのインスタンス化したデータを利用しない場合(一度切りの処理・中間処理など)、メインに戻さないでよい。
(クラスメソッドでインスタンス化して、そこで終了)(インスタンスが消える処理すればなお良い)

 

<以下実例>(例と言いながら、かなりややこしいです。)


エクセルシートは、前回同様、下記の通りです。



コードは、youtube「ひとりごとワンダーランド」様を参考に書いています。(←すごく感謝)
(不都合があれば削除いたします。)

 

////////////////////////////////////                                
メイン文                                
Sub 処理開始()                                
    ''シート読み込み                                
    Dim v売上シート As 売上シートk                                
    Set v売上シート = New 売上シートk                                
                                
    Dim v元データ As Variant                            ・・・①    
    v元データ = v売上シート.読み込み("売上")    '売上はシート名 functionで戻し    ・・・②
                                    
    ''売上データリスト作成                                
    Dim v売上オブジェクト生成 As 売上オブジェクト生成k                                
    Set v売上オブジェクト生成 = New 売上オブジェクト生成k                                
                                    
    Dim v売上データリスト As 売上データリストk                                
    Set v売上データリスト = v売上オブジェクト生成.リスト生成(v元データ)   ・・・③④
End Sub                                
                                
/////////////////////////////////////////////////////                                
'クラス「売上データ」                                
Private m名前 As String                                
Private m日付 As Date                                
Private m売上金額 As Long                                
                                
Public Property Let 名前(p名前 As String)                                
    m名前 = p名前                                
End Property                                
Property Get 名前() As String                                
    名前 = m名前                                
End Property                                
                                
Public Property Let 日付(p日付 As Date)                                
    m日付 = p日付                                
End Property                                
Property Get 日付() As Date                                
    日付 = m日付                                
End Property                                
                                
Public Property Let 売上金額(p売上金額 As Long)                                
    m売上金額 = p売上金額                                
End Property                                
Property Get 売上金額() As Long                                
    売上金額 = m売上金額                                
End Property                                
                                
/////////////////////////////////////////////////////                                
クラス「売上シート」                                
Public Function 読み込み(pシート名) As Variant                                
                                
    With ThisWorkbook.Worksheets(pシート名)                                
            Dim hani As Range                                
                                            
            Set hani = .Range("A2").CurrentRegion                                
            Set hani = hani.Offset(1).Resize(hani.Rows.Count - 1)                                
            読み込み = hani '標題除く 2次元配列                                
    End With                                
                                
End Function                                
                                
/////////////////////////////////////////////////////                                
'クラス「売上オブジェクト生成」                                
Public Function リスト生成(v元データ As Variant) As 売上データリストk    '←オブジェクトを返す                                
                                
    Dim v売上データリスト As 売上データリストk      '←返したいデータオブジェクト                                
    Set v売上データリスト = New 売上データリストk                                
                                
    Dim vRowCount As Long                                
    For vRowCount = LBound(v元データ) To UBound(v元データ)                                
        Dim v売上データ As 売上データk                                
        Set v売上データ = New 売上データk                                
                                        
        v売上データ.名前 = v元データ(vRowCount, 2)  '順番変えた                                
        v売上データ.日付 = v元データ(vRowCount, 1)                                
        v売上データ.売上金額 = v元データ(vRowCount, 3)                                
                                
        Call v売上データリスト.追加(v売上データ)                                
    Next vRowCount                                
                                    
    Set リスト生成 = v売上データリスト                                
                                    
End Function                                
                                
/////////////////////////////////////////////////////                                
'クラス「売上データリスト」                                
Private m売上データリスト As Collection    'private化。publicでなくProperty Get 取得で取り出し。                                
           'letはないけどgetは作る。変数名Itemsとかでなくprivate変数(m売上データリスト)でcollection                                
                                
Public Property Get 取得() As Collection                                
    Set 取得 = m売上データリスト                                
End Property                                
                                
Public Sub 追加(p売上データ As 売上データk)                                
    If m売上データリスト Is Nothing Then                                
        Set m売上データリスト = New Collection                                
    End If                                
    m売上データリスト.Add p売上データ                                  
                                    
End Sub                                
                                
                                
解説メモ                                
①v元データという命名は気に入っています。名前からして重要そうでない。また、クラス名にも類似のものがない。                                
(variantで受けるので、多分配列ということが分かる。)                                
                                
②クラス「売上シート」の読み込むメソッド(function)で、値(配列)を読み込みに入れて、さらに、メイン文のv元データに戻している。                                
(この使い方で、メイン文に、v元データオブジェクトをメインに戻している。データを続けて使うため。)                                
                                
③④        Set v売上データリスト = v売上オブジェクト生成.リスト生成(v元データ)                               
ここでは最終的に、v元データ(配列)を売上データリスト(collectionオブジェクト)に、入れることをしている。                                
                                
まず、    v売上オブジェクト生成.リスト生成(v元データ) の箇所について、
クラス「売上オブジェクト生成」では、リスト生成メソッド    (function)がある。    
そこで、配列v元データのデータ(1行分)をクラスv売上データ(1item)に入れている。                                
(クラスv売上データのプロパティ(日付・名前・売上金額))                                
上記処理のために、このクラス内で、クラス「v売上データ」をNewしているが、この「売上データ」オブジェクトは メイン文には表れない。メインに戻す必要はない。                    
                                
続けて、このクラスでは、最終的に欲しい「売上データリスト」オブジェクトをNewするが、                                
    Set v売上データリスト = New 売上データリストk                                
として「売上データ」クラスを使って、v売上データリストをNewしている。
                                
で、この「売上データリスト」クラスは、collectionとして、設定されている。                                
そしてこの「売上データリスト」クラスには、追加メソッドを持っているので、                                
クラス「売上データリスト」の追加メソッドを使って、v売上データ(1item)を、                                
売上データリストにcollectionしていく。                                
                                
で、上記で、できたものをfunctionで、メイン文のv売上データリストに戻して完了。                                
    Set v売上データリスト = v売上オブジェクト生成.リスト生成(v元データ)                                   


その他
クラスの命名については、もっと分かりやすくならないだろうか・・・(課題)

今回使用したクラスは、
・売上シート
・売上データ
売上データリスト
・売上王ジェクト生成
と4つあるが、
売上データが、入れ物(プロパティの塊)で、売上データリストが、出すもの(collection)。
残りの売上シートは、配列用の範囲で、売上王ジェクト生成はfunction。
v元データは、ワークシート上にしかない。

各クラスがそんな機能・働きなのか分かりやすければ、もっといいかも。

 

エクセルVBAクラス備忘録 変数の使い方(宣言)(privateかpublicか)しつこい

20240420 エクセルVBAクラス備忘録 変数の使い方(宣言)(privateかpublicか)


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

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

今回:
今回、メイン処理について、思ったことをメモしようと思いましたが、そこにたどり着く前に、備忘録。
また、変数の使い方(宣言)(privateかpublicか)です。

 

クラスを作成する場合、privateかpublicかどっちがいいのかという話がありました。
privateだと、セッターとか、ゲッターとか面倒だし、その使い方も、中をただ通すだけならあんまり意味ないのでは?と思っていました。

でも、bing様(copilot様)に、簡単なクラスを書いてもらったら、「ああこんな風に使えるのか」というのがあったので、備忘録。
(以下コードは適宜直しています。)


シートはこんな感じ。


簡単に日付と店名と金額があって、それらを行ごとに取得するコードを書いてもらう。

---------------------------------------
Sub Main2()
    Dim salesData As New LibWorkSheet
    salesData.Init "売上" ' シート名を指定

    ' レコードが最終行に到達するまで日付を表示
    Do While salesData.売上_Date <> ""
        Debug.Print "日付: " & salesData.売上_Date
        salesData.ReadNext
    Loop
End Sub


-----------------------------------
' LibWorkSheetクラス
'Public Class LibWorkSheet
    Private Ws As Worksheet
    Private readrow As Long

    ' 初期化
    Public Sub Init(sheet_name As String)
           
        Set Ws = ThisWorkbook.Sheets(sheet_name)   '
        readrow = 2 ' 読み込み行数(初期値は2行目)
    End Sub

    ' 日付を取得
    Public Property Get 売上_Date() As Variant
        売上_Date = Ws.Cells(readrow, 1).Value  ' A列から値を取得
    End Property

    ' 店名を取得
    Public Property Get ShopName() As Variant
'        ShopName = Val("B" & ReadRow)
        ShopName = Ws.Cells(readrow, 2).Value

    End Property

    ' 金額を取得
    Public Property Get Amount() As Variant
'        Amount = Val("C" & ReadRow)
        Amount = Ws.Cells(readrow, 3).Value
    End Property

    ' 次の行を読み込む
    Public Sub ReadNext()
        readrow = readrow + 1
    End Sub

 
'End Class
--------------------------------------------

こんな感じ。親モジュールのDo Whileで、日付をdebug.printする。

注目は、下記のプロパティ。

    ' 日付を取得
    Public Property Get 売上_Date() As Variant
        売上_Date = Ws.Cells(readrow, 1).Value  ' A列から値を取得
    End Property

通常、自分だと、単純に、
    Public Property Get 売上_Date() As Variant
        売上_Date = m売上_Date
    End Property

として、クラス内のprivate変数(m売上_Date)を売上_Dateに戻して、メインに返すようにするが、ここでは、
Ws.Cells(readrow, 1).Value
として、readrowを変えることで、次のデータに移行している。
(また、この売上_Dateプロパティは、シートから、触接値を取得するため、セッターを使っていないのも注目)

この使い方なら、セッター・ゲッターを設定する価値はあると思った。

ちなみに、「public変数を使って」自分で書くなら以下のコード。
*****************************
Option Explicit
Public readrow As Long  'publicはここしか置けないし、dimだとだめなので、publicしかない。

Sub main_kari()
    Dim cnt As Long
    

    Dim v売上 As 売上
    Set v売上 = New 売上 
    readrow = 2 'publicでクラスに渡す
    v売上.初期化

    ' レコードが最終行に到達するまで日付を表示
    Do While v売上.店名 <> ""
        Debug.Print "日付: " & v売上.日付
        v売上.ReadNext
        v売上.初期化
    Loop
End Sub

-----------------------------------
'クラス「売上」
Option Explicit

Public 日付 As Date
Public 店名 As String
Public 金額 As Double

Private Ws As Worksheet


Sub 初期化()      

    Set Ws = ThisWorkbook.Sheets("売上")

    日付 = CDate(Ws.Cells(readrow, 1).Value)
    店名 = Ws.Cells(readrow, 2).Value
    金額 = Ws.Cells(readrow, 3).Value
End Sub

Public Sub ReadNext()
    readrow = readrow + 1
End Sub

-------------------------------
これだと、セッター・ゲッターを使わないが、特にメインモジュールに書いた、readrowが危険すぎる気がする。
長いコードになると分からなくなる。
もっと上手な書き方はあると思うが、今回はここまでです。


その他
do whileは、v売上.店名 <> ""にしています。
'    Do While v売上.日付 <> ""    '型が一致しません 「""」は文字型のため
エラーになるようです。

 

 

エクセルVBAクラス備忘録 シートからコレクション(名前付きセルの名前と値の取得)

20240412 エクセルVBAクラス備忘録 シートからコレクション
エクセルVBA オブジェクト指向備忘録クラス シートからコレクション(名前付きセルの名前と値の取得)

 

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


前回の備忘録に、「エクセルは、オブジェクト指向と相性がいいのかも」と書いたことなどを書きます。
自分用のメモです。間違っていたらすみません。汗


今回、シートからのデータの取得について、書きます。
「手続き型」でもいろんな方法が考えられます。
(配列を使ったり、別シートに転記したり。いろいろ。)

 

前提:
シート名「入力シート」
B4セルに「入力欄1」、
E3セルに「入力欄2」を用意します。

中のセルには、(何でもいいのですが、)
B4セルに「犬」、
E3セルに「猫」と入力しておきます。

これを取り出すためのコードを書く前に、今回、これらのセルに名前を付けて、その名前をfor each文で回して取得することを考えました。

まず、注意ですが、セルに名前を付ける場合、「数式タブ」「名前の管理」から「新規作成」で行って下さい。
この場合、範囲を「入力シート」に限定して下さい。範囲「ブック」全体にすると、うまくfor each文で取得できませんでした。
(ちなみに、名前ボックスから、セルに名前を付けると、範囲が「ブック」全体になってしまいます。)

 

で、最初に、作ったコードが下記です。(今回、「Bing様」に聞いたりしているので、変数名がおかしくすみません。)

------------------------------
Sub GetAllNamedCells1()    'nameオブジェクトを取っている
    Dim ws As Worksheet
    Dim namedCell As Name      '名前付きセル操作

    ' アクティブなシートを取得
    Set ws = ActiveSheet

    ' シート内の名前付きセルをループ
    For Each namedCell In ws.Names
        ' 名前を表示
        MsgBox namedCell.Name    '入力シート!入力欄1
        MsgBox namedCell.value   'これだと、=入力シート!$E$3
        Stop
    Next namedCell
    Stop
    
End Sub
---------------------------

コメントに書きましたが、
MsgBox namedCell.Name    '入力シート!入力欄1
MsgBox namedCell.value   'これだと、=入力シート!$E$3
という感じで、セルの値である「犬」が取れません。

***************************

 

で、次に作ったコードがこれ。
--------------------------------
Sub GetNamedCellValues1_2()   
    Dim ws As Worksheet
    Dim namedCell As Name
    Dim cellValue As Variant

    ' アクティブなシートを取得
    Set ws = ActiveSheet

    ' シート内の名前付きセルをループ
    For Each namedCell In ws.Names
        ' 名前付きセルの値を取得
        cellValue = namedCell.RefersToRange.value

        ' 名前と値を表示
        MsgBox "名前: " & namedCell.Name & vbNewLine & "値: " & cellValue
    Next namedCell
End Sub

--------------------------------

これだと、
名前:入力シート!入力欄1
値:「犬」
が取れます。

*************************

 

で、上記をコレクションに入れたいと思って作ったコードがこれ。
--------------------------------
Sub GetAllNamedCells2()     'nameオブジェクトを取っている
    Dim ws As Worksheet
    Dim namedCell As Name
    Dim namedCellValues As New Collection ' 新しいコレクションを作成

    ' アクティブなシートを取得
    Set ws = ActiveSheet

    ' シート内の名前付きセルをループ
    For Each namedCell In ws.Names
        ' 名前付きセルの値をコレクションに追加
        namedCellValues.Add namedCell.RefersToRange.value, Key:=namedCell.RefersToRange.Name
        
    Next namedCell
    
    Stop
End Sub

 

--------------------------------
上記のstopの時点で、ローカルウィンドウを使い、namedCellValuesを見てみると、
: namedCellValues :  : Collection/Collection
    : Item 1 : "犬" : Variant/String
    : Item 2 : "猫" : Variant/String
となっています。
コレクションではありますが、
name名前:入力シート!入力欄1
value値:「犬」
ではありません。

こうなっているのは、Dim namedCell As Nameで、nameオブジェクトを取っているからなのでしょう。
(stringなど)
で、For Each文にebug.Printを入れてみます。
        Debug.Print namedCell.Name     '入力シート!入力欄1①
        Debug.Print namedCell.RefersToRange.value    '犬②
        Debug.Print namedCell.RefersToRange.Name   '=入力シート!$B$4③
と出ます。
上記のコレクションでは、①と②が欲しいのに②しか取れていないようです。
(厳密には③も持っています。)
Debug.Print namedCellValues.Item("=入力シート!$B$4")    '犬②

 

        'addでnameオブジェクトを取得してる。(当該セルの情報)
        'nameオブジェクトには、名前付きセルのプロパティはない。(①は持てない)

という感じかなと思いました。

++++++++++++++++++++++


で、いろいろした挙句、下記のコードにしました。(日本語の変数名も混ざってすみません。)
名前付きセルの名前と値をコレクションに取れるようにクラス「セルクラス」を作成しました。

-----------------------

クラス「セルクラス」
Option Explicit

Public 値 As String
Public セル名 As String

----------------------

Sub GetAllNamedCells3()
    Dim ws As Worksheet
    Dim namedCell As Name
    Dim namedCellValues As Collection
    Set namedCellValues = New Collection  ' 新しいコレクションを作成

    ' アクティブなシートを取得
    Set ws = ActiveSheet

    ' シート内の名前付きセルをループ
    For Each namedCell In ws.Names

        'ここで、クラスを作る
        Dim vセルクラス As セルクラス
        Set vセルクラス = New セルクラス 

        vセルクラス.セル名 = namedCell.Name
        vセルクラス.値 = namedCell.RefersToRange.value

        ' 名前付きセルの値をコレクションに追加
        namedCellValues.Add vセルクラス, vセルクラス.セル名

    Next namedCell
Stop

End Sub

-----------------------------
コレクションに、namedCellを直接addせずに、クラスを使って、必要な情報(string)を入れました。

: namedCellValues :  : Collection/Collection
  : Item 1 :  : Variant/Object/セルクラス
      : セル名 : "入力シート!入力欄1" : String
      : 値 : "犬" : String
  : Item 2 :  : Variant/Object/セルクラス
      : セル名 : "入力シート!入力欄2" : String
      : 値 : "猫" : String

こんな感じに入っています。

コード自体は、過去に書いたように、もっとクラス側に寄せた方がいいと思います。

**************************

 

で、思ったことは、「エクセルはオブジェクトの集合」で、それを使わない手はないということ。
ただ、オブジェクトでも何でもかんでもプロパティを持っているわけではない。
(オブジェクトブラウザとかを活用する。)
必要なものはクラスを作って、なんとかする。汗

この記事に間違いがたくさんあると思いますが、今はこんな感じ(認識)です。


コードには、

Dim namedCell As 「Name」

For Each namedCell In ws.「Names」

しれっと、「Name」「Names」とか微妙なものも出ています。(理解が足りない。)
name は、ワークブック内の名前付き範囲や名前付きセルを指します。
names は、ワークブック内のすべての名前付き範囲や名前付きセルのコレクションを表します。
ということだったり、
cell は、単一のセルを指します。たとえば、Range("A1") はシート上のセル A1 を表します。
cells は、複数のセルを指定するためのメソッドです。たとえば、Range("A1:B5") はセル範囲 A1 から B5 を表します。
というようなことをbing様は教えてくれました。(合っているのか?汗)

 

今後は、もっと、既存の「オブジェクト」を理解することが必要なのかなと思っています。

 

エクセルVBA オブジェクト指向備忘録クラス クラス隠蔽(カプセル化)~PrivateとPublic~

20240410 エクセルVBAクラス備忘録 クラス隠蔽(カプセル化) 

エクセルVBA オブジェクト指向備忘録クラス クラス隠蔽(カプセル化)~PrivateとPublic~

 

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


今回も、コードもない、ぼんやりとした疑問です。
(解決してません。実践で試すしかないかな・・・。)

VBAで(も)、下記のような、クラス内で使用するセッターやゲッターがある。
(例)
Private mItem番号 As long

'let
Public Property Let Item番号(pItem番号 As long)
     mItem番号 = pItem番号
End Property

'get
Public Property Get Item番号() As long
     Item番号 = mItem番号
End Property

例では、
「Private mItem番号 As long」
というように、Private変数にしている。
(注:mはメンバー変数(private)の略。勝手につけてます。)


他方、Publicで済ます手もある。
「Public  Item番号 As long」

こう(Public)すれば、クラスの呼び出し元から直接アクセスできると言うが・・・。
(それが危険と言うが・・・)
Private変数を使っても、セッターを使えば同じでないの?と思ってしまう。
(それどころか、プライベート変数が増えてしまうし・・・メモリ食う???)

 


***********************************
少し、道をそれるけど、オブジェクト指向(クラス使用)でなく、「手続き型」で作成する場合、
Private変数ばかりで、Public変数は、どうしても仕方ない場合しか使わなかった。
(複数のユーザーフォームで、同じ配列データをリストとして使う場合など、他に思いつかず仕方なく当時使った。)

Private変数しか使わない理由は、他のプログラムに影響が出るのが怖いから。
他の人が使う場合、複数のブックを同時に開いていたり、想定しないことを起こす場合がある。
(外部のブックにまで、影響したり、影響されたりするかは分からないが・・・。)

変数の宣言位置や、スコープを考えればいいのだろうけど、面倒なので、Private変数しか使わないと決めていた。
********************************


で、オブジェクト指向の場合、どうすればいいか?
上でも書いたように、セッターを使えば、public変数として使えるんでは?
と思ってしまう。
対策とすれば、
Public Property Let Item番号(pItem番号 As long)
     mItem番号 = pItem番号
End Property
の内部に、条件処理を入れて、使用場面を限定するとか
(例:元から数字があれば書き換え不可のif文など。)。

また、クラスはインスタンス化しないと使えないから、インスタンス化して、変数を使うので、そうそう誤作動は起こさないのでは?という気もしている。

============================


素人なのですみません。
自分が恐れているのは、
外部のブックから影響を受けることはないのか?
(自分のデータベースが、外部ブックのシートデータを間違って転記、書き換えるとか)
また
外部のブックに影響を与えることないのか?
(外部ブックのシートに、書き込んでしまうとか)
ということです。

まあ、具体的に、ブックを指定したり、コードを注意すればいい、別の問題という気もしますが。

 

+++++++++++++++++++++++++
クラスの隠ぺい(カプセル化)には、クラスのフィールドを直接触らせたくないということもあるそうです。
もっと、クラスを書いてみないと、よく意味は分かりませんが。


+++++++++++++++++++++++++
あと、セッターから考えると、何で、こんなプロパティいるのと考えてしまうが、
もしかしたら、ゲッターしかないクラスがあった場合、このクラスは、取り出ししかできないから、
完全に守られているのかも。

そう考えると、セッターとゲッターは対(ワンセット)でなくてもいいのかも・・・。

 

追記:また、後日、もやもやとしたことを書くことになるが、オブジェクト指向とエクセルはもしかしたらすごく相性がいいのかも・・・と先日思った。

 

エクセルVBA オブジェクト指向備忘録クラス(何となく分かったこと。クラスに寄せる。)

20240405エクセルVBA オブジェクト指向備忘録クラス(何となく分かったこと。クラスに寄せる。)

 

ここしばらく、エクセルVBAオブジェクト指向について、書いてます。

自分用なりのメモ。素人なのですみません。汗。(今回、コードもないです。振り返りメモ。)

 

過去の記事

(1)記事「20240301エクセルVBA オブジェクト指向備忘録クラス ドメインオブジェクト全部を標準モジュールに」
では、「重要なオブジェクト(ドメインオブジェクト=各クラスの結果)を、標準モジュールで確認できた方がいいのでは?」
との考えを元に、クラスに書く内容を、標準モジュールに「書き戻し」たりした。

 

過去の記事

(2)記事「20240307 エクセルVBAクラス備忘録 (メモ)お手本を見ながら、クラスを作って改良する」では、標準モジュールに書かれたコードを、「クラス側に寄せる」ことをやりました。
(その後のいくつかの記事も同じパターンで、別の例でやっています。)

 


特に記事(2)ですが、オブジェクトに親子関係を「作っている」。
「氏名」クラスが「子」クラスだとしたら、「氏名リスト」がそれらを束ねる「親」クラス。
これは、worksheetsオブジェクト(親)がworksheetオブジェクト(子)を持つイメージに近いですが、
自分で作るオブジェクトは、実際に、コレクションなど「コーディングを行うことで、親子関係にする必要」がある。
(自動で勝手にするわけではないし、親だ、子だと宣言するのでもない。コレクションなどで関連づけ。VBAだけ?)

 

で、その「親子」関係を持つ複数のクラスを作る場合、親子の関係をつなぐコードは、「クラス内部」に書くのが自然だろう。


(コレクションなら子をアイテムとして、親にコーディングが自然。)

 

・・・とすると、記事(1)で、書いたように、すべての重要なオブジェクト(ドメインオブジェクト=各クラスの結果)」を、標準モジュールに書くことは、あまりよくない。(かえって、クラス同志の関係が分かりにくくなる。)

 

同じことだが、クラスとクラスの関連が分かれば、コードを「クラスに寄せる」方がいいのだろう。多分。

 

あと、親子のクラスの作り方ですが、今まで、「最小単位の子クラス」を作って、さて、次はどうしようと思っていたが、クラスは「親子関係」(が多い)と考えるなら、コーディングでは、大元・基本となる「親」クラスから、書き始めるのもいいのかもと思っている。


「親の親の親」とか、かもしれないが、演繹的に、書くことができれば、「流れ」が見える気がする。

 

クラスを使わない「手続き」型の流れは、標準モジュールのコードの「上から下へ流れる」が、オブジェクト指向は、「クラスの親から子に流れる」のか・・・も。

 

また、以前は、メモリを食わないように、シート中心に考えていたが、今は、データを一気にコレクションとかディクショナリとか、シートからはがして読み込んでもいいのかも。

 

いろんなHPやYouTube等に感謝感謝感謝です。

エクセルVBA オブジェクト指向備忘録クラス ディクショナリとクラス(とコレクション)

20240331 エクセルVBAクラス備忘録 

エクセルVBA オブジェクト指向備忘録クラス ディクショナリとクラス(とコレクション)

 

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

 

今回の疑問
前回、クラス・コレクションを使ったコードを4つ書きました。
今回、クラスとディクショナリで考えてみます。(注)

YouTubeに「こいこいの人工知能」様の「ディクショナリ」を使った動画があり、参考にさせてもらいました。
その動画は、ユーザー定義型(type)とディクショナリを扱うものですが、typeをクラスに置き換えました。

で、できたものを、前回同様に、「いつも隣にITのお仕事」様
(エクセルVBAでクラスモジュールを使っての独自のコレクションを作る方法他の記事)に合わせて、
コードを4個作成しています。

(ご両者には大変感謝しております。)
(もし、掲載で不都合ありましたら、すぐに削除いたします。)

 

注:実際の内容は、ディクショナリを使ってできた「売上」を「売上コレクション」として一つにまとめるものです。
もしかしたら、ディクショナリにしながら、コレクションに突っ込むことができるのかもしれませんが、初心者なのですみません。

オブジェクト指向のコーディングに近づけて考えてみました。

下記に4個のコードを書いています。同じ挙動ですが、オブジェクト指向の度合いが高くなっていきます。
参考HP:「いつも隣にITのお仕事」様(エクセルVBAでクラスモジュールを使っての独自のコレクションを作る方法他の記事)
(感謝いたします。)
(もし、掲載で不都合ありましたら、すぐに削除いたします。)

時間なかったので、部分部分、Bing様に聞いています。
(感謝いたします。)
(もし、掲載で不都合ありましたら、すぐに削除いたします。)

 

コード1
//////////////////////////////////////
' 売上クラスモジュール
    Public mise As String
    Public d As String  '割引あった日の文字列
    Public beer As Long    '合計金額用
    Public eda As Long
/////////////////////////////////

' 親モジュール
Sub Rensou2class_collection()  'まだ
    Dim dic As Object, i As Long, data, keys
    Dim n As Long, m As Long
    Dim v売上() As 売上      '配列
    
    Dim v売上コレクション As Collection
    Set v売上コレクション = New Collection
    
    Set dic = CreateObject("Scripting.Dictionary")

    'ビールデータ格納
    data = Sheets("sheet1").Range("A1").CurrentRegion
    
    For i = 2 To UBound(data)
        If Not dic.Exists(data(i, 2)) Then  '存在するか確認(店)
            dic.Add data(i, 2), n      'n最初は0(例:B店,0)
            
            ReDim Preserve v売上(n)
            Set v売上(n) = New 売上   'new
            v売上(n).mise = data(i, 2)
            v売上(n).beer = data(i, 3)
            v売上(n).eda = data(i, 4)
            
            '割引  ’〇の時に日付
            If data(i, 5) <> "" Then
                v売上(n).d = data(i, 1)
            End If
            
            n = n + 1
        Else      '存在する場合
            '保存場所の値を格納
            m = dic(data(i, 2))    '店の位置がmに取れる
            
            v売上(m).beer = v売上(m).beer + data(i, 3)
            v売上(m).eda = v売上(m).eda + data(i, 4)
            
            '割引
            If data(i, 5) <> "" Then
                If v売上(m).d = "" Then
                    v売上(m).d = data(i, 1)
                Else
                    v売上(m).d = v売上(m).d & vbCrLf & data(i, 1)
                End If
            End If
        End If
    Next i

    For i = LBound(v売上) To UBound(v売上)
        v売上コレクション.Add v売上(i), v売上(i).mise
   
    Next i

  Stop
End Sub

--------------------------------------------
コード2
//////////////////////////////////////
' 売上クラスはコード1と変わらず。

/////////////////////////////////////
'売上のコレクションを独自コレクションに設定(売上コレクション)
Public Items As Collection

////////////////////////////////////
' 親モジュール
Sub Rensou2class_collection()  'OK

 

    Dim dic As Object, i As Long, data, keys
    Dim n As Long, m As Long
    Dim v売上() As 売上      '配列
    
    
    Dim v売上コレクション As 売上コレクション
    Set v売上コレクション = New 売上コレクション
    Set v売上コレクション.Items = New Collection
    
 
    
    Set dic = CreateObject("Scripting.Dictionary")

    'ビールデータ格納
    data = Sheets("sheet1").Range("A1").CurrentRegion
    
    For i = 2 To UBound(data)
        If Not dic.Exists(data(i, 2)) Then  '存在するか確認(店)
            dic.Add data(i, 2), n      'n最初は0(例:B店,0)
            
            ReDim Preserve v売上(n)
            Set v売上(n) = New 売上   'new
            v売上(n).mise = data(i, 2)
            v売上(n).beer = data(i, 3)
            v売上(n).eda = data(i, 4)
            
            '割引  ’〇の時に日付
            If data(i, 5) <> "" Then
                v売上(n).d = data(i, 1)
            End If
            
            n = n + 1
        Else      '存在する場合
            '保存場所の値を格納
            m = dic(data(i, 2))    '店の位置がmに取れる
            
            v売上(m).beer = v売上(m).beer + data(i, 3)
            v売上(m).eda = v売上(m).eda + data(i, 4)
            
            '割引
            If data(i, 5) <> "" Then
                If v売上(m).d = "" Then
                    v売上(m).d = data(i, 1)
                Else
                    v売上(m).d = v売上(m).d & vbCrLf & data(i, 1)
                End If
            End If
        End If
        
        
    Next i

    For i = LBound(v売上) To UBound(v売上)
        v売上コレクション.Items.Add v売上(i), v売上(i).mise
    Next i


    Stop
End Sub

--------------------------------------------
コード3(コード2とコード3は微妙な差しかないです。)
//////////////////////////////////////
' 売上クラスはコード1と変わらず。

/////////////////////////////////////
'売上のコレクションを独自コレクションに設定(売上コレクション)
Public Items As Collection

Private Sub Class_Initialize()
    Set Items = New Collection
End Sub


/////////////////////////////////////
' 親モジュール
Sub Rensou2class_collection()  'OK


    

    Dim dic As Object, i As Long, data, keys
    Dim n As Long, m As Long
    Dim v売上() As 売上      '配列
    
    
    Dim v売上コレクション As 売上コレクション
    Set v売上コレクション = New 売上コレクション
'    Set v売上コレクション.Items = New Collection    'ここコメントアウト!!!
    
 
    Set dic = CreateObject("Scripting.Dictionary")

    'ビールデータ格納
    data = Sheets("sheet1").Range("A1").CurrentRegion
    
    For i = 2 To UBound(data)
        If Not dic.Exists(data(i, 2)) Then  '存在するか確認(店)
            dic.Add data(i, 2), n      'n最初は0(例:B店,0)
            
            ReDim Preserve v売上(n)
            Set v売上(n) = New 売上   'new
            v売上(n).mise = data(i, 2)
            v売上(n).beer = data(i, 3)
            v売上(n).eda = data(i, 4)
            
            '割引  ’〇の時に日付
            If data(i, 5) <> "" Then
                v売上(n).d = data(i, 1)
            End If
            
            n = n + 1
        Else      '存在する場合
            '保存場所の値を格納
            m = dic(data(i, 2))    '店の位置がmに取れる
            
            v売上(m).beer = v売上(m).beer + data(i, 3)
            v売上(m).eda = v売上(m).eda + data(i, 4)
            
            '割引
            If data(i, 5) <> "" Then
                If v売上(m).d = "" Then
                    v売上(m).d = data(i, 1)
                Else
                    v売上(m).d = v売上(m).d & vbCrLf & data(i, 1)
                End If
            End If
        End If
        
    Next i

    For i = LBound(v売上) To UBound(v売上)
        v売上コレクション.Items.Add v売上(i), v売上(i).mise
    
    Next i

    Stop
End Sub

----------------------------------------------------
コード4
//////////////////////////////////////
' 売上クラスはコード1と変わらず。

/////////////////////////////////////
'売上のコレクションを独自コレクションに設定(売上コレクション)
Public Items As Collection


Private Sub Class_Initialize()

    Dim dic As Object, i As Long, data, keys
    Dim n As Long, m As Long
    Dim v売上() As 売上      '配列
       

    Set Items = New Collection
    
    
    Set dic = CreateObject("Scripting.Dictionary")

    'ビールデータ格納
    data = Sheets("sheet1").Range("A1").CurrentRegion
    
    For i = 2 To UBound(data)
        If Not dic.Exists(data(i, 2)) Then  '存在するか確認(店)
            dic.Add data(i, 2), n      'n最初は0(例:B点,0)
            
            ReDim Preserve v売上(n)
            Set v売上(n) = New 売上   'new
            v売上(n).mise = data(i, 2)
            v売上(n).beer = data(i, 3)
            v売上(n).eda = data(i, 4)
            
            '割引  ’〇の時に日付
            If data(i, 5) <> "" Then
                v売上(n).d = data(i, 1)
            End If
            
            n = n + 1
        Else      '存在する場合
            '保存場所の値を格納
            m = dic(data(i, 2))    '店の位置がmに取れる
            
            v売上(m).beer = v売上(m).beer + data(i, 3)
            v売上(m).eda = v売上(m).eda + data(i, 4)
            
            '割引
            If data(i, 5) <> "" Then
                If v売上(m).d = "" Then
                    v売上(m).d = data(i, 1)
                Else
                    v売上(m).d = v売上(m).d & vbCrLf & data(i, 1)
                End If
            End If
        End If
        
    Next i

    For i = LBound(v売上) To UBound(v売上)
        Items.Add v売上(i), v売上(i).mise
    
    Next i

 

End Sub

/////////////////////////////////////////
' 親モジュール
Sub Rensou2class_collection()  'OK

    Dim v売上コレクション As 売上コレクション
    Set v売上コレクション = New 売上コレクション
    

    Stop
End Sub


=======================================
基本的に4つのコードとも同じ動き。
でも、親モジュールでのstopの時のインスタンスの数は3番目が一番少ない。