ExcelマクロVBA技術解説
VBAクラスの作り方:独自Rangeっぽいものを作ってみた

ExcelマクロVBAの問題点と解決策、エクセルVBAの技術的解説
最終更新日:2019-03-09

VBAクラスの作り方:独自Rangeっぽいものを作ってみた


クラスの作成は、標準モジュールで作成していた時とは様相が違い戸惑う部分も多いと思います、
それは、初めてVBAに取り組んだ時の戸惑いと同じかもしれません。


最初はとにかく慣れることが一番です、
細かい文法や機能は、少し慣れてから改めて学んでも遅くはありません。

「Rangeっぽいもの」って良くわからないタイトルになっていますが、良いタイトルが思いつきませんでした・・・

標準モジュールでは、直接Rangeオブジェクト(Range,Cells等)を記述せずに、
それらを極力クラスに委ねようということです。
マクロVBAが遅いのは、多くの場合Rangeオブジェクトの操作に関係しています。
これを避けるためには表範囲を配列に入れて処理するといった方法がありますが、
これをクラスにカプセル化しようということです。
クラス化することで入力支援が使えることも利点になります。
ただし、
今回のクラスは即実用で使う為ではなく、あくまでクラスの作り方のサンプルとして作成しています。
従って、どういう機能をどうやって実装するかという部分を中心にVBAコードを読んでください。

VBAクラスに慣れるには、とにかく、VBAコードを読んで書いて動かしてみることです。
その為のVBAクラスのサンプルとして作成しています。
また学習素材の意味として、意図的にいろいろなテクニックを使っている部分もあります。
そいったテクニックも込みでサンプルとして使ってください。
列挙体、配列、コレクション、Optional、ParamArray
これらを使っていますので、もしこれらが分からない場合はクラスの前にそちらを先に学んでください。
特に実務でVBAを使い業務の自動化を目指すのなら、クラスよりもこれらの方がはるかに重要です。


以下、いきなりクラスの全VBAコードを提示します。
クラス モジュールを挿入して、コピペで貼り付けてください。
クラス作成後は、後の標準モジュールで動作を確認しつつクラス内のVBAコードを読んでみてください。


クラスの全VBAコード


クラス名は何でも構いませんが、
後の標準モジュールでの使い方では、
clsRange
このクラス名になっています。
独自Rangeオブジェクトっぽいものということで、この名称にしました。



Option Explicit

'==================================================
' 公開変数等
'==================================================
Public Enum ErrValue 'このクラス専用の列挙の意味でここに
  Through 'そのまま返す
  ToBlank '""を返す
  ToEmpty 'Emptyを返す
  ToNull 'Nullを返す
End Enum

'==================================================
' 非公開変数
'==================================================
Private Ws As Worksheet     '対象ワークシート
Private pHeadRow As Long    '見出し行
Private pDataRow As Long    'データ開始行
Private pDataCol As Long    'データ開始列
Private pArray As Variant    'データ格納2次元配列(1~)
Private pErrCells As Integer  'エラー値の場合の値

'==================================================
' 公開プロパティ
'==================================================

'対象シート
Public Property Get Worksheet() As Worksheet
  Set Worksheet = Ws
End Property
Public Property Set Worksheet(ByVal argWs As Worksheet)
  Set Ws = argWs
End Property

'表の見出し行:名前定義がされている行
Public Property Get HeadRow() As Long
  HeadRow = pHeadRow
End Property
Public Property Let HeadRow(ByVal HeadRow As Long)
  pHeadRow = HeadRow
End Property

'表のデータ開始行
Public Property Get DataRow() As Long
  DataRow = pDataRow
End Property
Public Property Let DataRow(ByVal DataRow As Long)
  pDataRow = DataRow
End Property

'表のデータ開始列
Public Property Get DataCol() As Long
  DataCol = pDataCol
End Property
Public Property Let DataCol(ByVal DataCol As Long)
  pDataCol = DataCol
End Property

'データ行数:ReadOnly Property
Public Property Get RowsCount() As Long
  If IsArray(pArray) Then
    RowsCount = UBound(pArray, 1)
  End If
End Property

'データ列数:ReadOnly Property
Public Property Get ColumnsCount() As Long
  If IsArray(pArray) Then
    ColumnsCount = UBound(pArray, 2)
  End If
End Property

'クラス独自のCells
Public Property Get Cells(ByVal Row As Long, ByVal col As Long) As Variant
  Cells = pArray(Row, col)
  If IsError(Cells) Then
    Select Case pErrCells
      Case ErrValue.Through
        'そのまま
      Case ErrValue.ToBlank
        Cells = ""
      Case ErrValue.ToEmpty
        Cells = Empty
      Case ErrValue.ToNull
        Cells = Null
    End Select
  End If
End Property
Public Property Let Cells(ByVal Row As Long, ByVal col As Long, ByVal Value As Variant)
  pArray(Row, col) = Value
End Property

'エラー値の場合にCellsが戻す値を指定:ReadOnly Property
Public Property Let ErrCells(ByVal arg As ErrValue)
  pErrCells = arg
End Property

'クラス独自のColumn:WriteOnly Property
Public Property Let Column(ByVal col As Long, ByVal Value As Variant)
  Dim i As Long
  For i = 1 To RowsCount
    pArray(i, col) = Value
  Next
End Property

'==================================================
' 公開メソッド
'==================================================

'指定されたワークシートの表全体を2次元配列に入れる
Public Sub StoreValues(Optional ByVal TargetSheet As Worksheet)
  Dim myRange1 As Range, myrange2 As Range
  Set myRange1 = Ws.Cells(pDataRow, pDataCol)
  Set myrange2 = myRange1.CurrentRegion
  Set myrange2 = myrange2.Item(myrange2.Count)
  
  '行・列が途切れている場合は警告
  If myrange2.Column _
    < Ws.Cells(pDataRow, Ws.Columns.Count).End(xlToLeft).Column Then
    If MsgBox("表内に空白列があります。" & vbLf & vbLf & _
         "処理を中断してよろしいですか?", vbYesNo, "確認") _
         = vbYes Then
      End
    Else
      If MsgBox("表内に空白列があるので、" & vbLf & vbLf & _
           "エラーとなる可能性があります。" & vbLf & vbLf & _
           "処理を続行してよろしいですか?", vbYesNo, "確認") _
           = vbYes Then
        End
      End If
    End If
  End If
  pArray = Ws.Range(myRange1, myrange2)
End Sub

'データ格納用の2次元配列から指定範囲をシートに戻す
'オプションの指定状況により範囲を変更
Public Sub PutArray(Optional ByVal Row1 As Long = 0, Optional ByVal Col1 As Long = 0, _
          Optional ByVal Row2 As Long = 0, Optional ByVal Col2 As Long = 0)
  '引数全て省略:全データ→配列全体を出力し抜ける
  If Row1 = 0 And Col1 = 0 And _
    Row2 = 0 And Col2 = 0 Then
    Ws.Cells(DataRow, DataCol) _
      .Resize(RowsCount, ColumnsCount) _
      = pArray
    Exit Sub
  End If
  
  Select Case True
    Case Row1 <> 0 And Col1 <> 0 And Row2 <> 0 And Col2 <> 0
      '全て指定:指定行列範囲
      '処理なし
    Case Row1 <> 0 And Col1 <> 0 And Row2 = 0 And Col2 = 0
      'Row1とCol1のみ:指定セル
      Row2 = Row1
      Col2 = Col1
    Case Row1 <> 0 And Col1 = 0 And Row2 = 0 And Col2 = 0
      'Row1のみ:指定行のみ
      Row2 = Row1
      Col1 = 1
      Col2 = ColumnsCount
    Case Row1 <> 0 And Col1 = 0 And Row2 <> 0 And Col2 = 0
      'Row1とRow2のみ:指定行範囲
      Col1 = 1
      Col2 = ColumnsCount
    Case Row1 = 0 And Col1 <> 0 And Row2 = 0 And Col2 = 0
      'Col1のみ:指定列のみ
      Col2 = Col1
      Row1 = 1
      Row2 = RowsCount
    Case Row1 = 0 And Col1 <> 0 And Row2 = 0 And Col2 <> 0
      'Col1とCol2のみ:指定列範囲
      Row1 = 1
      Row2 = RowsCount
    Case Else
      MsgBox "PutRangeの引数不正"
      Exit Sub
  End Select
  
  '始点行・列~終点行・列範囲を配列からシートへ
  '配列の指定範囲を別配列へ
  Dim myArray()
  Dim i1 As Long, i2 As Long
  ReDim myArray(1 To Row2 - Row1 + 1, 1 To Col2 - Col1 + 1)
  For i1 = 1 To Row2 - Row1 + 1
    For i2 = 1 To Col2 - Col1 + 1
      myArray(i1, i2) = pArray(i1 + Row1 - 1, i2 + Col1 - 1)
    Next
  Next
  '出力先のセル範囲を求める
  Dim myRange1 As Range, myrange2 As Range
  Set myRange1 = Ws.Cells(DataRow + Row1 - 1, DataCol + Col1 - 1)
  Set myrange2 = Ws.Cells(DataRow + Row2 - 1, DataCol + Col2 - 1)
  'シートへ出力
  Ws.Range(myRange1, myrange2) = myArray
End Sub

'データ格納用の2次元配列から指定範囲をシートに戻す
'列1,比較演算子1,値1,列2,比較演算子2,値2・・・
'全てAnd条件です。Orが必要な場合は別途作成
Public Function FilterArray(ParamArray ParamFilter()) As Variant
  '引数未指定は配列全体を返し抜ける
  If UBound(ParamFilter) < 0 Then
    FilterArray = pArray
    Exit Function
  End If
  
  '該当行をコレクションに入れる:要素数が不定なのでコレクション
  Dim i1 As Long, i2 As Long, ix As Long
  Dim myCollection As New Collection
  Dim varTemp As Variant
  Dim myArray()
  ReDim myArray(1 To ColumnsCount)
  For i1 = 1 To RowsCount
    '関数IsTargtで欽定
    If IsTargt(i1, ParamFilter()) Then
      For i2 = 1 To ColumnsCount
        myArray(i2) = Cells(i1, i2)
      Next
      myCollection.Add myArray
      ix = ix + 1
    End If
  Next
  If myCollection.Count = 0 Then
    Exit Function
  End If
  
  'コレクションを2次元配列にする
  ReDim myArray(1 To myCollection.Count, 1 To ColumnsCount)
  i1 = 1
  For Each varTemp In myCollection
    For i2 = 1 To ColumnsCount
      myArray(i1, i2) = varTemp(i2)
    Next
    i1 = i1 + 1
  Next
  
  FilterArray = myArray
  Exit Function
End Function

'Vlookup機能
'検索値(String),検索列(Long),取得列(Long)
'検索値はVariant同士の比較を避けるために型を指定している
Public Function Vlookup(ByVal What As String, _
            ByVal ColSearch As Long, _
            ByVal ColReturn As Long) As Variant
  Dim i As Long
  For i = 1 To RowsCount
    If Cells(i, ColSearch) = What Then
      Vlookup = Cells(i, ColReturn)
      Exit Function
    End If
  Next
End Function

'==================================================
' 非公開メソッド
'==================================================

'列1,比較演算子1,値1,列2,比較演算子2,値2・・・
Private Function IsTargt(ByVal i1 As Long, ParamArray AryFilter()) As Boolean
  Dim i2 As Long
  IsTargt = True
  'ParamArrayを配列で受け取ると(0,要素数)の2次元配列
  For i2 = 0 To UBound(AryFilter(0)) Step 3
    '比較演算子
    Select Case UCase(AryFilter(0)(i2 + 1))
      Case "="
        If Not Cells(i1, AryFilter(0)(i2)) = AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case ">"
        If Not Cells(i1, AryFilter(0)(i2)) > AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case "<"
        If Not Cells(i1, AryFilter(0)(i2)) < AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case ">="
        If Not Cells(i1, AryFilter(0)(i2)) >= AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case "<="
        If Not Cells(i1, AryFilter(0)(i2)) <= AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case "<>"
        If Not Cells(i1, AryFilter(0)(i2)) <> AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case "LIKE"
        If Not Cells(i1, AryFilter(0)(i2)) Like AryFilter(0)(i2 + 2) Then
          IsTargt = False
        End If
      Case Else
        IsTargt = False
    End Select
  Next
End Function

'==================================================
' クラスのメソッド
'==================================================

'クラス初期処理
Private Sub Class_Initialize()
  HeadRow = 1
  DataRow = 2
  DataCol = 1
  pErrCells = ErrValue.Through
End Sub

以下、コメントおよびプロシージャーを抜き出したものです。
全体の構成を見る時の参考にしてください。

'==================================================
' 公開プロパティ
'==================================================
'対象シート
Public Property Get Worksheet() As Worksheet
Public Property Set Worksheet(ByVal argWs As Worksheet)

'表の見出し行:名前定義がされている行
Public Property Get HeadRow() As Long
Public Property Let HeadRow(ByVal HeadRow As Long)

'表のデータ開始行
Public Property Get DataRow() As Long
Public Property Let DataRow(ByVal DataRow As Long)

'表のデータ開始列
Public Property Get DataCol() As Long
Public Property Let DataCol(ByVal DataCol As Long)

'データ行数:ReadOnly Property
Public Property Get RowsCount() As Long

'データ列数:ReadOnly Property
Public Property Get ColumnsCount() As Long

'クラス独自のCells
Public Property Get Cells(ByVal Row As Long, ByVal col As Long) As Variant
Public Property Let Cells(ByVal Row As Long, ByVal col As Long, ByVal Value As Variant)

'エラー値の場合にCellsが戻す値を指定:ReadOnly Property
Public Property Let ErrCells(ByVal arg As ErrValue)

'クラス独自のColumn:WriteOnly Property
Public Property Let Column(ByVal col As Long, ByVal Value As Variant)
'==================================================
' 公開メソッド
'==================================================
'指定されたワークシートの表全体を2次元配列に入れる
Public Sub StoreValues(Optional ByVal TargetSheet As Worksheet)

'データ格納用の2次元配列から指定範囲をシートに戻す
'オプションの指定状況により範囲を変更
Public Sub PutArray(Optional ByVal Row1 As Long = 0, Optional ByVal Col1 As Long = 0, _
          Optional ByVal Row2 As Long = 0, Optional ByVal Col2 As Long = 0)

'データ格納用の2次元配列から指定範囲をシートに戻す
'列1,比較演算子1,値1,列2,比較演算子2,値2・・・
'全てAnd条件です。Orが必要な場合は別途作成
Public Function FilterArray(ParamArray ParamFilter()) As Variant

'Vlookup機能
'検索値(String),検索列(Long),取得列(Long)
'検索値はVariant同士の比較を避けるために型を指定している
Public Function Vlookup(ByVal What As String, _
            ByVal ColSearch As Long, _
            ByVal ColReturn As Long) As Variant
'==================================================
' 非公開メソッド
'==================================================
'列1,比較演算子1,値1,列2,比較演算子2,値2・・・
Private Function IsTargt(ByVal i1 As Long, ParamArray AryFilter()) As Boolean
'==================================================
' クラスのメソッド
'==================================================
'クラス初期処理
Private Sub Class_Initialize()

サンプルとして使うブックの構成


シートは、
顧客マスタ
売上データ
この2シートになります。

マクロ VBA クラス 入門


シート名
顧客マスタ
フィールド
顧客ID
氏名
氏名(カナ)
性別
生年月日
年齢
郵便番号
都道府県
電話番号
Email
備考


VBA クラス 入門


シート名

売上データ

フィールド

伝票番号
顧客ID
売上日
金額
氏名
都道府県


この2シートについては、
前回までにやった、列名のクラスを作成します。


VBAクラスの作り方:列名の入力支援と列移動対応
クラスを使う良さとして、入力支援が使えてコーディングが楽になるという利点があります、列番号をクラスに持てば、列名が候補表示されて非常に便利です。しかし、これを実装するには、かなりの手間がかかります。つまり、クラス作成に手間をかけて、その後を楽にするということになります。

VBAクラスの作り方:列名のプロパティを自動作成する
クラスに列名のプロパティを作成することで、入力支援が使えてコーディングが楽になりますが、列数が多くなればVBAの記述量が増え、コーディングが大変になります。入力支援が使えるのは良いが、その事前準備があまりに大変ではやる気が失せてクラス作るのが面倒になってしまいます。

作成されるクラスは

clsC顧客マスタ
clsC売上データ

以下では、この2つのクラスがインポートされているものとして書いています。


データは適当に入れてください。
ネット上には、無料のテストデータ作成ツールがいろいろありますので活用してください。


標準モジュールでの使い方


標準モジュールに以下のVBAコードを貼り付けてください。

Option Explicit

'clsRangeの使い方サンプル
Sub ClassTestMain()
  Dim clsMst As New clsRange
  Dim colMst As New clsC顧客マスタ
  Dim clsDat As New clsRange
  Dim colDat As New clsC売上データ
  Dim Ws As Worksheet
  
  '顧客マスタClassの初期設定
  With clsMst
    Set .Worksheet = ThisWorkbook.Worksheets("顧客マスタ")
    Set colMst.Worksheet = .Worksheet
    .HeadRow = 1 '列タイトル行
    .DataRow = 2 'データ開始行
    .DataCol = 1 'データ開始列
    .ErrCells = ToNull 'エラー値処理
    Call .StoreValues 'データ全体を配列へ格納
  End With
  
  '売上データClassの初期設定
  With clsDat
    Set .Worksheet = ThisWorkbook.Worksheets("売上データ")
    Set colDat.Worksheet = .Worksheet
    .HeadRow = 1 '列タイトル行
    .DataRow = 2 'データ開始行
    .DataCol = 1 'データ開始列
    .ErrCells = ToEmpty 'エラー値処理
    Call .StoreValues 'データ全体を配列へ格納
  End With
  
  Call 顧客マスタ更新(clsMst, colMst)
  Call 売上データ更新(clsDat, colDat, clsMst, colMst)

  '解放
  Set clsMst = Nothing
  Set colMst = Nothing
  Set clsDat = Nothing
  Set colDat = Nothing
End Sub

Private Sub 顧客マスタ更新(ByVal clsMst As clsRange, ByVal colMst As clsC顧客マスタ)
  Dim i As Long
  'シートの内容を更新する場合
  With clsMst
    'クラスのCellsを使った読み書き
    .Cells(3, colMst.氏名_カナ) = StrConv(.Cells(3, colMst.氏名_カナ), vbHiragana)
    .Cells(4, colMst.氏名_カナ) = Application.GetPhonetic(.Cells(4, colMst.氏名))
    .Cells(5, colMst.氏名_カナ) = Application.GetPhonetic(.Cells(5, colMst.氏名))
    .Cells(5, colMst.備考) = "引っ越し:" & .Cells(5, colMst.都道府県)
    .Cells(5, colMst.都道府県) = "千葉県"
    '全行処理
    For i = 1 To .RowsCount
      .Cells(i, colMst.年齢) = Age(.Cells(i, colMst.生年月日), Date)
    Next

    'シートへの書き出し
    '名前付き引数:[Row1:=開始行][,Col1:=開始列][,Row2:=終了行][,Col2:=終了列]
    Call .PutArray(Row1:=3, Col1:=colMst.氏名_カナ, Row2:=4, Col2:=colMst.氏名_カナ)
    Call .PutArray(Row1:=5)
    Call .PutArray(Col1:=colMst.年齢)
  End With

  'Filterを指定して配列を受け取り別シートへ出力する場合
  Dim Ws As Worksheet
  Dim myArray As Variant
  With clsMst
    myArray = .FilterArray(colMst.性別, "=", "女", _
                colMst.年齢, "<", 30, _
                colMst.電話番号, "Like", "080*")
    Set Ws = Worksheets.Add(After:=Worksheets(Sheets.Count))
    On Error Resume Next
    Application.DisplayAlerts = False
    Worksheets("Filter出力").Delete '2度実行対応
    Application.DisplayAlerts = True
    On Error GoTo 0
    Ws.Name = "Filter出力"
    .Worksheet.Rows(.HeadRow).Copy Destination:=Ws.Rows(1)
    If IsArray(myArray) Then
      Ws.Cells(2, 1).Resize(UBound(myArray, 1), UBound(myArray, 2)) = myArray
    End If
  End With
End Sub

Private Sub 売上データ更新(ByVal clsDat As clsRange, ByVal colDat As clsC売上データ, _
              ByVal clsMst As clsRange, ByVal colMst As clsC顧客マスタ)
  Dim i As Long
  Dim strSearch As String
  '顧客マスタの情報を取得し更新する
  With clsDat
    For i = 1 To .RowsCount
      strSearch = .Cells(i, colDat.顧客ID)
      '検索値(String),検索列(Long),取得列(Long)
      .Cells(i, colDat.氏名) = clsMst.Vlookup(strSearch, colMst.顧客ID, colMst.氏名)
      .Cells(i, colDat.都道府県) = clsMst.Vlookup(strSearch, colMst.顧客ID, colMst.都道府県)
    Next
    Call .PutArray(Col1:=colDat.氏名)
    Call .PutArray(Col1:=colDat.都道府県)
  End With
End Sub

'年齢算出
Public Function Age(FromDate As Variant, ToDate As Variant) As Integer
  Dim intAge As Integer
  intAge = Year(ToDate) - Year(FromDate)
  If Format(ToDate, "mmdd") < Format(FromDate, "mmdd") Then
    intAge = intAge - 1
  End If
  Age = intAge
End Function 

ClassTestMainが起動のプロシージャーです。
ここでやっていることは他愛のないことなので、VBA内のコメントを参考にしてください。
一番下の年齢算出については、おまけみたいなものになりますので参考までに。

以下、ポイントのみ解説します。

Dim clsMst As New clsRange
Dim clsDat As New clsRange
このようにクラスのインスタンスを複数作れる、
つまり、同じ型のオブジェクトが複数作れるのが良いところです。

クラスは型(オブジェクトの設計図)で、型を使って実体化(インスタンス)したものがオブジェクトです。
VBAのクラスとは(Class,Property)
VBAを覚えて、いろいろ作りながらネットで調べたりしていると、クラスやらオブジェクト指向やらという言葉に出くわします。いくら言葉を尽くしても、これらクラスやオブジェクト指向を完全に説明しつくすことは難しいと思われます。オブジェクトとは 操作対象の事ですと説明されたりしますが、まずは何かの物体、つまりは対象物と理解すれば良いでしょう。

Dim colMst As New clsC顧客マスタ
Dim colDat As New clsC売上データ
これらは、各シートの列名表示のクラスになります。
clsRangeの中に入れられれば使いやすいのですが、
クラスの型が違いますし、何より列名入力支援の為なので統一することができません。

入力支援は実際にタイピングして確認
VBA クラス 入門

クラス名が表示されます。
クラス名称にプリフィックスを付けておくと、ここでまとまって表示されます。

VBA クラス 入門

.ピリオドを打つと、クラスのメンバーが表示されます。

VBA クラス 入門

プロパティの引数で列挙体を指定しているので設定値が候補表示されています。
Public Enum ErrValue
・・・
Public Property Let ErrCells(ByVal arg As ErrValue)
これで実現されています。
列挙体はPublicにする必要があります。
クラス内のプロパティ等のプロシーシャーの型として使うことで、
それを外部で指定する場合に列挙体のメンバーが候補表示されるようになります。
後日追記
列挙体はプロジェクト内でユニークでなければなりません。
列挙体はモジュール名で修飾することはできません。
つまり、
クラスのインスタンスを経由して直接列挙体そのものを参照することもできません。
従って clsMst.ErrValue このような参照はできないということです。
列挙体は標準モジュールに書いても同じですが、
このクラス専用なのでクラス内にパッケージしたほうが再利用しやすくなるということです。

VBA クラス 入門

列名も一覧表示されるので悩まずに選択できます。

エラーが出たときはF8で確認
.ErrCells = ToEmpty 'エラー値処理
これは、セル値がエラー値(#○○!)の場合、IfステートメントやVBA関数がエラーとなってしまう事に対応しています。
プロパティCellsの中でエラー値判定して、返す値("",Empty,Null)を制御しています。
ではエラーを出してみましょう。

.ErrCells = ToEmptyをコメントアウトし、
データ3行目(シートでは4行目)の「氏名(カナ)」のセルをエラー値にします。
=NA() や =1/0 等々
そして、ClassTestMainを実行すると、

VBA クラス 入門

VBA クラス 入門

.Cellsにマウスカーソルを当てると、

VBA クラス 入門

では、ステップイン(F8)でクラスの中に入ります。

VBA クラス 入門

さらにF8で進めていくと、

VBA クラス 入門

セル値が入った、Variantの配列の値をそのまま返しているのが分かります。
.ErrCells = ToEmptyを生かすと、
このCells内で値を変更している動きが理解できるはずです。

意図的にクラス内でエラーを起こしてみましょう。
VBA クラス 入門

これなら確実にエラーになります。
ClassTestMainを実行すると、
「型が一致しません」となりますので、「デバッグ」

VBA クラス 入門

クラス内で停止してくれませんでした、、、
そこで、F8で進めると、

VBA クラス 入門

ここまで来て、F8を押すと「型が一致しません」となり「デバッグ」で標準モジュールに戻ります。
なんか不便ですよね、エラー箇所で止まってくれない、、、

VBEの「オプション」→「全般」タブ

VBA クラス 入門

「クラス モジュールで中断(R)」を選択して「OK」
これで、クラス内で停止するようになります。
ただし、クラス内でOn Errorが指定されればスルーします。

「エラー発生時に中断(B)」にすれば、
全モジュールにおいて、On Errorに関係なくエラー停止させることができます。

最後に


今回は練習用・学習用のクラスということで紹介しましたが、
もう少し手を加えれば、実用としても十分耐えうるものにできるはずです。

最初に書きましたが、ここでは、
列挙体、配列、コレクション、Optional、ParamArray、そしてProperty
これらが使われていますが、これに、
構造体や、WithEventsキーワード、RaiseEventステートメント
これらが使えるようになれば、もう文法的には学ぶものはほとんどありません。
(ステートメントやプロパティの細部については別として)

クラスに直接関連するものとして残りは、インターフェースになります。
インフェースは、
VBAでオブジェクト指向の多態性(ポリモーフィズム)を実現する手段と言えます。
スーパークラス(親クラス)には、プロパティ・メソッド等の「枠」「型」のみ定義し、
Implementsステートメントを実装した子クラスで実際の動作を決定するものです。
こんな感じの説明でしょうか、、、これでわかるはずもありません。

身近で、実感できるサンプルもなかなかありません。
インターフェースの説明で良くある「ワンワン」「ニャンニャン」的なものでは、、、
単にサンプルVBAコードを提示しても、あーそうなのか、ということが分かるだけで、
実際の使い道を実感できないと思います。

VBAのンターフェースは、
大規模かつ堅牢なシステムを作るような時でもなければ良さを実感できるものではないと思います。
実装の大変さを上回る利点を実感できないだろうということです。
逆に言えば、VBAでそういうシステムを今後作ろうと考えているのなら是非習得してください。

最初に書いた通り、クラスを使うには慣れが必要です。
どうしてもクラスはVBAコードが多くなりがちで、VBA記述が大変になることが多いものです。
だからと言って、VBAコードを短くしたいだけの理由で、
変数をやたらに公開するようなカプセル化できていないようなクラスでは本末転倒になります。

基本を守りつつ、少しずつ慣れていきながら実践で使ってみてください。
クラスはしっかり使えれば、その大変さを補ってあまりある恩恵があります。



同じテーマ「マクロVBA技術解説」の記事

VBAのクラスとは(Class,Property,Get,Let,Set)
クラスを使って他ブックのイベントを補足する
VBAクラスの作り方:列名の入力支援と列移動対応
VBAクラスの作り方:列名のプロパティを自動作成する
VBAクラスの作り方:独自Rangeっぽいものを作ってみた
クラスとイベントとマルチプロセス並列処理
オートフィルタを退避回復するVBAクラス
オートフィルタ退避回復クラスを複数シート対応させるVBAクラス
コレクション(Collection)の並べ替え(Sort)に対応するクラス
VBAクラスのAttributeについて(既定メンバーとFor Each)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)


新着記事NEW ・・・新着記事一覧を見る

VBAにおける変数のメモリアドレスについて|VBA技術解説(11月8日)
空文字列の扱い方と処理速度について(""とvbNullString)|VBA技術解説(1月7日)
Errオブジェクトとユーザー定義エラー|VBA入門(11月5日)
シングルクォートの削除とコピー(PrefixCharacter)|VBA技術解説(11月4日)
ユーザー定義型の制限とクラスとの使い分け|VBA技術解説(11月3日)
クリップボードに2次元配列を作成してシートに貼り付ける|VBA技術解説(11月1日)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)|VBA技術解説(10月31日)
VBAクラスのAttributeについて(既定メンバーとFor Each)|VBA技術解説(10月19日)
VBAの用語について:ステートメントとは|VBA技術解説(10月16日)
VBAのマルチステートメント(複数のステートメントを同じ行に)|VBA技術解説(10月14日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|ExcelマクロVBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.変数宣言のDimとデータ型|ExcelマクロVBA入門
6.繰り返し処理(For Next)|ExcelマクロVBA入門
7.マクロって何?VBAって何?|ExcelマクロVBA入門
8.ひらがな⇔カタカナの変換|エクセル基本操作
9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説
10.セルに文字を入れるとは(Range,Value)|VBA入門



  • >
  • >
  • >
  • VBAクラスの作り方:独自Rangeっぽいものを作ってみた

  • このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


    記述には細心の注意をしたつもりですが、
    間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
    なお、掲載のVBAコードは自己責任で使ってください。万一データ破損等の損害が発生しても責任は負いません。




    このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
    本文下部へ