t-hom’s diary

主にVBAネタを扱っているブログです。

VBA Newキーワードの使い方は2種類だけではない。 ~ With New Objectという使い方。

オブジェクトを扱うときに登場するNewキーワード。

おそらく以下の2種類の使い方が一般的かと思う。

まずは、変数へ新規インスタンスをセットする際に使うNew。
(以下のObjectには任意のオブジェクトが入る。例:Collection)

Dim obj As Object
Set obj = New Object

次に、上記を省略した記法。

Dim obj As New Object

マニュアルを見ると、特定の構文で使用されることが明記されている。
Set以外はすべて変数宣言時の使用なので、実質は2種類である。
f:id:t-hom:20151214030910p:plain

しかし実際のところNewキーワードは特定の構文に縛られるものではなく、単に新規オブジェクト生成を表しているので、別な使い方もできる。

例えばWith文と組み合わせる方法。
以下はクリップボードを扱うDataObjectを変数を作らずに使用しているサンプルである。
(実行にはMicrosoft Forms 2.0 Object Libraryの参照設定が必要。)

Sub ClipBoardSample()
    With New DataObject
        .GetFromClipboard
        If .GetFormat(1) Then
            For Each x In Split(.GetText, vbTab)
                Debug.Print x
            Next
        End If
    End With
End Sub

以下のように表のヘッダーをコピーして実行すると、イミディエイトウインドウに縦並びで出力される。
f:id:t-hom:20151214031456p:plain

f:id:t-hom:20151214031607p:plain

With文を使えば、場合によってはわざわざ変数を用意しなくてもオブジェクトを使用できるのだ。

Newキーワードはオブジェクトの参照を保持できる場所であれば大体どこでも使用できる。
例えばCollectionへの追加。

まずはテスト用データとして「なんちゃって個人情報」から名前と生年月日をとってきたものを使用する。
f:id:t-hom:20151214032033p:plain
※ダミーです。実在の人物情報ではありません。

次にクラスモジュールでPersonクラスを作成した。

Public 名前 As String
Public 生年月日 As Date
Public Property Get 年齢() As Integer
    年齢 = DateDiff("yyyy", 生年月日, Date)
End Property

そして、コレクションに追加するコードは以下のとおり。

Sub hoge()
    最終行 = Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Row
    Dim C As New Collection
    
    For i = 2 To 最終行
        C.Add New Person
        C.Item(C.Count).名前 = Sheet1.Cells(i, 1).Value
        C.Item(C.Count).生年月日 = Sheet1.Cells(i, 2).Value
    Next
    
    Dim P2 As Person
    For Each P2 In C
        Debug.Print P2.名前, P2.年齢
    Next
End Sub

C.Add New Personで新規Personオブジェクトがコレクションに追加される。
C.Countはコレクションに入っているアイテムの数、つまり最終アイテムのインデックスと同じなので、C.Item(C.Count)は追加されたオブジェクトを表す。

さて、コレクションに保持してしまうとプロパティとメソッドの自動補完が利かないので、With文を使いたいケースもある。
名前と生年月日の設定までは問題ない。
しかしいざコレクションに追加しようと思ったら、どう書けば良いのだろうか。。

    For i = 2 To 最終行
        With New Person
            .名前 = Sheet1.Cells(i, 1).Value
            .生年月日 = Sheet1.Cells(i, 2).Value
            C.Add '???
        End With
    Next

「C.Add .」とやってみてもうまくいかない。
With文単体では、オブジェクト自身を示すことができないのだ。
ではやはり変数を使うしかないかというと、そうでもない。

PersonクラスにSelfプロパティを追加してやる。

Public 名前 As String
Public 生年月日 As Date
Public Property Get 年齢() As Integer
    年齢 = DateDiff("yyyy", 生年月日, Date)
End Property
Public Property Get Self() As Person
    Set Self = Me
End Property

Selfは自己参照を返すプロパティで、これを使えば以下のようにWith文でコレクションに追加できるようになる。

Sub hoge2()
    最終行 = Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Row
    Dim C As New Collection
    
    For i = 2 To 最終行
        With New Person
            .名前 = Sheet1.Cells(i, 1).Value
            .生年月日 = Sheet1.Cells(i, 2).Value
            C.Add .Self
        End With
    Next
    
    Dim P As Person
    For Each P In C
        Debug.Print P.名前, P.年齢
    Next
End Sub

余談であるが、既存のオブジェクトをWith文で参照している際に自己参照したいときがある。
Selfプロパティは用意されていないのでどうするかというと、親子関係になっているものであれば、いったん子を指定してから.Parentオブジェクトを参照すれば自己参照となる。

以下、Withでシートオブジェクトを指定した際に自己参照するテクニック。

Sub hoge3()
    Dim C As New Collection
    Dim sh As Worksheet
    For Each sh In Worksheets
        With sh
            If .Name Like "Sheet*" Then
                C.Add .Range("A1").Parent
            End If
        End With
    Next
    
    For Each sh In C
        Debug.Print sh.Name
    Next
End Sub

RangeプロパティのParentで、Sheet1自身を参照している。
あまりうまい使い方は思いつかなかったが、覚えておくと何かの役に立つかもしれない。


最後に、プロシージャの引数としてもNewキーワードが使用できる。

Sub fuga()
    Call fugafuga(New Person)
End Sub

Function fugafuga(x As Person)
    x.名前 = "A"
    x.生年月日 = #1/1/1900#
    Debug.Print x.年齢
End Function

何か有効な使い方があるかは不明。

ただ引き出しを沢山もっておくことはアイデアの源泉になるので、これも覚えておくと何かの役に立つかもしれない。


色々な使い方を紹介したが、要するにNewキーワードは特定の構文のみで使用される特殊なキーワードなどではなく、単に新規オブジェクトを生成するキーワードであり、オブジェクトへの参照を保持できる場所なら大体使えるということ。

以上

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。