IT系/VBA/基本/オブジェクト

Last-modified: 2020-08-08 (土) 08:57:04

目次


概要

オブジェクトに関するVBAのTIPS。

オブジェクトとは

値と手続きをまとめたものであり、何か操作をしようとする対象となるものがオブシェクト。

  • ブックもシートもセルも、全てオブジェクト。
  • 数値や文字ではなく、オブジェクトを入れる変数がオブジェクト変数。
  • オブジェクト変数にオブジェクトを入れる時には、Setステートメントを使った=代入を使用する。

オブジェクト変数と宣言

  • オブジェクト型は、Workbookオブジェクト、Worksheetオブジェクト、RangeオブジェクトとExcelで定義されているものだけでも沢山ある。
  • オブジェクトなら何でも入れられるデータ型としてObject型があり、総称オブジェクト型とも言う*1
  • Variant(バリアント型) > Object(総称オブジェクト型) > 固有オブジェクト型
    Variantは、何でも入る
    Objectは、オブジェクトなら何でも入る
    個別のオブジェクト型は、そのオブジェクトのみ
  • 宣言は、他のデータ型と同じ。
    Dim bk As Workbook    ' ワークブック
    Dim ws As Worksheet   ' ワークシート
    Dim rng As Range      ' セル及びセル範囲
    Dim obj As Object     ' 総称オブジェクト型
  • オブジェクト変数(As Object または、As 固有オブジェクト型)の初期値はNothing。

Setステートメント

オブジェクト変数に代入するには、Setステートメントを使用する。

Set オブジェクト変数 = オブジェクト
  • Setステートメントでオブジェクトへの参照(オブジェクトのアドレス)を変数に代入する。
  • オブジェクトの実体を変数に入れるのではなく、オブジェクトのある場所(アドレス)を変数に入れておくということ。
  • よって、2つの変数に同じオブジェクトを入れた場合、それぞれの変数の中にはオブジェクトの同じメモリアドレスが入る。
     
  • Setステートメントの使用例
    Dim ws As Worksheet
    Set ws = Worksheets("シート名")
    ws.Cells(1, 1) = 1

オブジェクト変数のメモリアドレスを確認する

  • 変数のメモリアドレスを確認するには、以下の関数を使う。
    VarPtr関数:変数のメモリアドレスを返す。
    ObjPtr関数:オブジェクトのメモリアドレスを返す。
  • 以下のように確認可能。
    Dim 変数A As Range
    Set 変数A = Range("A1")
    Dim 変数B As Range
    Set 変数B = 変数A
    Debug.Print "変数A:VarPtr="; VarPtr(変数A)
    Debug.Print "変数A:ObjPtr="; ObjPtr(変数A)
    Debug.Print "変数B:VarPtr="; VarPtr(変数B)
    Debug.Print "変数B:ObjPtr="; ObjPtr(変数B)
  • VarPtr関数の戻り値は、変数そのもののメモリアドレスのため、別々になる。
  • ObjPtr関数の戻り値で、オブジェクトのメモリアドレスが同じであることが確認できる。
  • これは自作したクラスでも同じ。
  • ただし、RangeオブジェクトやWorksheetオブジェクト等のVBAにあらかじめ用意されているオブジェクト(Newで作ることの出来ないクラス)の場合は、
    別々に定義したからといって、別々のオブジェクトが作成されるわけではない。
    Dim 変数A As Range
    Set 変数A = Range("A1")
    Dim 変数B As Range
    Set 変数B = Range("A1")
  • この場合のObjPtrは別々の値になるが、Range("A1")が複数あるわけではない。
    これらはさらにその先で管理されていて、同じオブジェクトにたどり着くようになっている。

Withステートメント

With ステートメントを使うことで、Withに指定したオブジェクトに対してオブジェクト名を再度記述することなく、プロパティやメソッドを記述することが可能。

With object
    [statements]
    ・・・
End With
  • With~End Withの間では、ピリオドから書き始めることでオブシェクト名を省略した書き方ができる。
    ' Withを使用しない場合
    Worksheets(1).Cells(1, 1) = 1
    Worksheets(1).Cells(2, 1) = 2
    ' Withを使用した場合
    With Worksheets(1)
        .Cells(1, 1) = 1
        .Cells(2, 1) = 2
    End With
  • 上記のWithを使った記述は以下の記述とほぼ同等と考えられる。オブジェクト変数の宣言を省略した記法ともいえる。
    Dim ws As Worksheet
    Set ws = Worksheets(1)
    ws.Cells(1, 1) = 1
    ws.Cells(2, 1) = 2
    Set ws = Nothing
  • Withのネストも可能。
    Withがネストされている場合に、.の前の省略されているオブジェクトは、直前(そのステートメントが含まれる最も内側)のWithステートメントに指定したオブジェクトになる。
    With Range("A1")
        With .Font
            .Bold = True
            .Color = vbRed
            .Size = 12
        End With
    End With
  • Withのネストは、可読性が悪いので多用するのは避けたほうがよい。

WithとSetの使い分け方

VBAの個々の場所において、SetとWithのどちらを使ったほうが良いかというような基準も決まりはない。
可読性・保守性の高い記述を考えて記述することになる。

  • 一例として、ワークシートは、プロシージャの先頭でオブジェクト変数に入れてから使い、主なワークシートにWithを適用するような使い方があげられる。
    Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
    Set ws1 = Worksheets("Sheet1")
    Set ws2 = Worksheets("Sheet2")
    Set ws3 = Worksheets("Sheet3")
    '
    With ws1
        .Cells(1, 1) = ws2.Cells(1, 1)
        .Cells(1, 2) = ws3.Cells(1, 1)
    End With

Is演算子によるオブジェクトの比較

Is演算子は、2つのオブジェクト参照変数を比較するために使用される。

If オブジェクト1 Is オブジェクト2 Then
  • Object1とobject2の両方が同じオブジェクトを参照する場合はTrueになる。
    それ以外はFalseになる。
  • Is演算子による比較は参照が同じかどうか判定をする。つまり、ObjPtr関数の取得値の比較と同じ意味になる。
    Dim 変数A As Worksheet
    Debug.Print 変数A Is Nothing '→ True
    Set 変数A = Worksheets(1)
    Dim 変数B As Worksheet
    Set 変数B = 変数A
    Dim 変数C As Worksheet
    Set 変数C = Worksheets(1)
    Debug.Print 変数A Is 変数B '→ True
    Debug.Print 変数A Is 変数C '→ True
    Set 変数C = Worksheets(2)
    Debug.Print 変数A Is 変数C '→ False
  • Rangeオブジェクトでは、下記の最後がFalse判定になる点に注意。
    Dim 変数A As Range
    Set 変数A = Range("A1")
    Dim 変数B As Range
    Set 変数B = 変数A
    Dim 変数C As Range
    Set 変数C = Range("A1")
    Debug.Print 変数A Is 変数B '→ True
    Debug.Print 変数A Is 変数C '→ False
  • したがって、同じセルを参照しているかの判定には、以下の値(文字列)を比較するようにする。
    .Address(External:=True)

TypeOf演算子

TypeOf演算子は、指定された型との間で型と互換性があるかどうかを確認する。
result = TypeOf objectexpression IsNot typename

  • objectexpressionの実行時の型がtypenameと互換性があるかどうかを調べる。
    互換性は、typenameの型のカテゴリに依存する。
  • VBAでの型の判定は、通常はTypeName関数で行う。
  • Implements(インターフェイス)使用時には、TypeName関数での判定とTypeOf演算子での判定で違いが出る。
    TypeName関数: Implementsとは関係なくそれぞれのクラス名が返される。
    TypeOf演算子 : 互換性を確認するので元のクラスと同一判定される。

オブジェクト変数を使う時の注意点

ByValでもオブジェクトの中は変更される

  • ByRefは参照渡し、つまり、変数そのものを渡している。
  • ByValは値渡し、つまり、変数の中の値を渡している。
  • 言い換えると、
    ByRefは、VarPtrを渡している。
    ByValは、ObjPtrを渡している。
  • 以下の結果は、「test_sub」と出力される。
    Sub test1()
        Dim myRng As Range
        Set myRng = Range("A1")
        myRng.Value = "test"
        Call test1_sub(myRng)
        Debug.Print myRng.Value
    End Sub
    Sub test1_sub(ByVal arg As Range)
        arg.Value = "test_sub"
    End Sub
  • ByValだからと言って、オブジェクトの中身を渡せるわけではない。あくまでオブジェクトのアドレスを渡しているだけ。
    そのオブジェクトに対する操作はByRefと同じ結果となる。
  • ByRefの場合は、変数の中の参照値(アドレス)を渡しているため、それを書き換えると呼出し元に影響する。
    以下の結果は、「$A$2」と出力される。
    Sub test2()
        Dim myRng As Range
        Set myRng = Range("A1")
        myRng.Value = "test"
        Call test2_sub(myRng)
        Debug.Print myRng.Address
    End Sub
    Sub test2_sub(ByRef arg As Range)
        Set arg = Range("A2")
    End Sub
  • オブジェクト変数でのByRefとByValの違いは、呼び出された中でオブジェクト変数に別のオブジェクトを入れたとき、呼び出し元のオブジェクト変数がいれかわるかどうかだけ。

オブジェクトの破棄

  • 1つのオブジェクトを複数のオブジェクト変数に入れた時、
    オブジェクトが破棄されるのは、全てのオブジェクト変数が解放(= Nothing)されたときに実行される。
    Sub test3()
        Dim 変数A As Class1
      Set 変数A = New Class1
      変数A.gValue = "変数A"
      Dim 変数B As Class1
      Set 変数B = 変数A
      Set 変数A = Nothing
      Set 変数B = Nothing    ' このタイミングでClass1が破棄
    End Sub
  • RangeオブジェクトやWorksheetオブジェクトはブックを閉じない限り破棄されることはない。

オブジェクトとプロパティ

  • Sheetモジュールでは、以下のようにExcelに付随するオブジェクトは省略して記述できるようになっている(ようにみえる)。
    Range("A1").Value    ’ 省略形
    Application.ActiveWorkbook.ActiveSheet.Range("A1").Value '
  • 実際は、上記のRangeはRangeオブジェクトそのものではなく、Rangeオブジェクトを返すRangeプロパティである。
  • 省略形も、以下のようにMe(=SheetモジュールのWorksheetオブジェクト)のメンバであるRangeプロパティを参照している。
    Me.Range("A1").Value

TIPS

別ページの一覧を入れる。

'IT系/VBA/基本/オブジェクト/' には、下位層のページがありません。

リンク集

重複を恐れないリンク集。

動画

その他メモ



*1 Excel95までは、Object型1種類だったらしい