Setとは? オブジェクト変数の使い方とNothingの意味を解説

今回の目標

  • Set について理解を深める
  • オブジェクト変数を使用できるようになる。
  • Nothing について理解する。

説明

Setとは

Workbook型やWorksheet型等のオブジェクト型の変数にオブジェクトを代入(=関連付け)するときに使います。変数にオブジェクト(正確には、オブジェクトのアドレス)を格納してオブジェクトを操作できるようにします。Setを使わないと変数はオブジェクトとつながらないため【実行時エラー 91】が生じます。(例外として、Dictionaryなどの一部クラスではSetが不要ですが、原則として必須です)

Setが必要なオブジェクト型一覧(一例です)

オブジェクトのデータ型型名
ワークブックオブジェクトWorkbook
ワークシートオブジェクトWorksheet
レンジオブジェクトRange
グラフオブジェクトChart
図形オブジェクトShape
テーブルオブジェクトListObject
コレクションオブジェクトCollection
ピボットテーブルオブジェクトPivotTable
名前定義オブジェクトName
ファイルオブジェクトFile ※1
フォルダオブジェクトFolder ※1
ディクショナリーオブジェクトDictionary ※1
(オブジェクト型なら格納可能)Object ※2

※1 File, Folder, Dictionary は参照設定より「Microsoft Scripting Runtime」の有効化が必要です。
※2 Object型は、Workbook型やRange型等のオブジェクト型を格納できますが、メソッドやプロパティの候補一覧が表示されません。そのため、メソッド名等を手入力する必要があり、作業効率の低下、メソッド名のタイプミス等を招きます。


初心者の方は「(下表の)値型"以外"の変数は、Setが必要」と覚えておくとよいでしょう( •̀ㅂ•́)و 。最初は難しく感じても、何度か使っていくうちに自然と慣れていきます。

Setが不要な値型一覧

データ型型名
整数型Integer
長整数型Long
単精度浮動小数点数型Single
倍精度浮動小数点数型Double
通貨型Currency
日付型Date
文字列型String
ブーリアン型Boolean

リンク:変数の型の格納可能範囲

Setを使うタイミングは

オブジェクト変数にオブジェクトを格納する

変数の宣言後、オブジェクトを代入する際、左辺の変数の前に"Set"を付ける必要があります。

Dim wb as Workbook
Set wb = ThisWorkbook

Workbook型やWorksheet型の変数の最後に半角ドットをつけると、メソッドやプロパティの候補一覧が表示されます。
メソッドとプロパティは(語弊はありますが)簡単に言うと、メソッドはオブジェクト変数でのみ使用できる専用の関数、プロパティはオブジェクト変数でのみ使用できる専用の変数と覚えてください。
ただし、Object型で宣言した後は候補一覧が表示されないため、注意が必要です。
Setを使用するのは、変数とオブジェクトを紐づける時のみで、メソッドやプロパティを使用する時はSetを付ける必要はありません。
文字通り、Set=設定する、ということですね。 

Functionプロシージャでオブジェクトを返す場合

Functionプロシージャでオブジェクトを返す場合、Function名で返す時と、呼び出し元で受け取る際の2か所でSetを使用する必要があります。

Function SampleSet1()
    Set SampleSet1 = ThisWorkbook.Worksheets(1)
End Function

Sub Main1()
    Dim ws As Worksheet
     Set ws = SampleSet1
End Sub
【例外】引数でオブジェクト変数に参照先を格納する場合

Functionプロシージャの引数をオブジェクトを渡すことが出来ます。
なお、オブジェクト(オブジェクト変数)を引数にする場合、"Set"を付ける必要がありません。

Function SampleSet2(wb as Workbook) '← ここは Set 不要
    '何らかの処理
End Function

Sub Main()
    ’マクロを実行しているブックオブジェクトを送る
    SampleSet2 ThisWorkbook 
End Sub
オブジェクト変数の参照を解除する

オブジェクト変数を初期化する時、Set が必要です。

Set 変数 = Nothing

Set 変数 = Nothing を使うと、オブジェクト変数の初期化(メモリの解放)を行うことができます。ただし、VBAでは自動的に初期化がありますので、通常は不要となります。詳細は、後述します。

☆ページ内リンク:Set 変数 = Nothing は必要?

Setを使用してみる

Setは先に述べた通り、変数の宣言後、オブジェクトを代入する際、左辺の変数の前に"Set"を付ける必要があります。

  1. 開いているブックを閉じる

    Sub sample1_1()
        'Workbook型の変数wbを作成する
        Dim wb As Workbook
        '変数wbに実行しているブックオブジェクトを参照させる
        Set wb = ThisWorkbook
        '実行しているブックを閉じる
        wb.Close     '←ThisWorkbook.Close と同じ
    End Sub
    
  2. 実行しているブック名をメッセージボックスで表示する

    Sub SetSample1_2()
        Dim ws As Worksheet
        Set ws = ThisWorkbook.Worksheets(1)
        '実行しているシート名を表示する
        MsgBox ws.Name  '←MsgBox ThisWorkbook.Worksheets(1).Name と同じ
    End Sub
    

Setを使うメリットは?

Setを使うメリットについてです。
主に次の2つがあります。

  1. 見やすさ(可読性)が大きく向上する

    Set を使用するとプログラムが短くなり、読みやすくなります。
    Setを使用しないパターンと使用するパターンを比較してみます。
    まずは、Set を使用しないパターンです。

    Sub SetSample2_1()
        ThisWorkbook.Worksheets("Sheet1").Range("A1") = _
            ThisWorkbook.Worksheets("Sheet2").Range("B2")
        ThisWorkbook.Worksheets("Sheet1").Range("C1") = _
            ThisWorkbook.Worksheets("Sheet2").Range("D2")
        ThisWorkbook.Worksheets("Sheet2").Range("D1") = _
            ThisWorkbook.Worksheets("Sheet1").Range("D3")
        ThisWorkbook.Worksheets("Sheet2").Range("D3") = _
            ThisWorkbook.Worksheets("Sheet1").Range("D6")
    End Sub
    

    次に、Set を使用するパターンです。

    Sub SetSample2_2()
        Dim wsMain As Worksheet
        Dim wsConf As Worksheet
        Set wsMain = ThisWorkbook.Worksheets("Sheet1")
        Set wsConf = ThisWorkbook.Worksheets("Sheet2")
        wsMain.Range("A1") = wsConf.Range("B2")
        wsMain.Range("C1") = wsConf.Range("D2")
        wsConf.Range("D1") = wsMain.Range("D3")
        wsConf.Range("D3") = wsMain.Range("D6")
        Set wsMain = Nothing
        Set wsConf = Nothing
    End Sub
    

    Setを用いて変数にすることで、役割を明示することができます。

  2. Functionプロシージャの戻り値を設定する
    Functionプロシージャの戻り値とする場合にSetが必要です。

    Function GetRangeObj(ws As Worksheet, ByVal 範囲 As String) As Range
        Set GetRangeObj = ws.Range(範囲)
    End Function
    
    Sub SetSample2_3()
        Dim rng As Range
        Set rng = GetRangeObj(ThisWorkbook.Worksheets(1), "C2:C5")
        rng.Interior.Color = RGB(0, 255, 0)   ' 背景色を緑にする
        Set rng = Nothing
    End Sub
    

    引数でオブジェクト変数を渡せば、戻り値にしなくても大丈夫だったりします。
    ただし、可読性が著しく下がるため、お勧めしません。

    ’長いプログラムだと、どこでrngの参照を設定したか分からない
    Sub SetRangeObj(ws As Worksheet, rng As Range, ByVal 範囲 As String)
        Set rng = ws.Range(範囲)
    End Sub
    
    Sub SetSample2_4()
        Dim rng As Range
        SetRangeObj ThisWorkbook.Worksheets(1), rng, "D2:D5"
        rng.Interior.Color = RGB(0, 255, 255)  ' 背景色を水色にする
        Set rng = Nothing
    End Sub
    

Setのありがちなミス

Setを使用する際のありがちなミス3選です。

  1. Setのつけ忘れ
    これまで述べてきた通り、オブジェクトの参照を変数に格納する場合、変数の前に"Set"を付ける必要があります。
    なお、この場合「実行時エラー91」が発生します。

    '実行時エラー 91になる(Sheet1のセルA1に"a"を格納したかった)
    Sub SetSample3_1()
        Dim ws As Worksheet
        '先頭に"Set" を忘れているため、エラーが発生する _
          ※「Set ws = Worksheets("Sheet1")」が正しい
        ws = Worksheets("Sheet1")
        ws.Cells(1, 1) = "a"
    End Sub
    

    リンク:実行時エラー91 の原因と解消方法について

  2. 違うオブジェクト型を参照している
    Object型は、WorkbookオブジェクトやWorksheetオブジェクト等を格納できます。そのため、参照しようとしていたオブジェクトとは別のオブジェクトを参照してしまった、というケースです。
    なお、この場合「実行時エラー 438」が発生します。

    '実行時エラー 438になる(ブックを閉じたかった)
    Sub SetSample3_2()
        Dim wb As Object
        '"Worksheets" は不要(ミス)
        Set wb = ThisWorkbook.Worksheets
        'WorksheetsオブジェクトにCloseメソッドはない
        wb.Close    ' ←ここでエラーが発生する
    End Sub
    
  3. 2つのオブジェクトを作成し参照先が同じオブジェクト
    コンパイルエラーや実行時エラー等にならない論理エラー(プログラムは実行できるが想定と異なること)です。オブジェクト変数を2つ作り、同じ参照先を設定した場合、片方のオブジェクトを変更すると、もう片方のオブジェクトも変更されます。なぜなら、Long型等とは違い、オブジェクト変数は1つのオブジェクトを参照し、操作しているだけで実体は1つであるためです。

    Sub SetSample3_3_1()
        Dim wb1 As Workbook
        Dim wb2 As Workbook
        Set wb1 = ThisWorkbook
        Set wb2 = wb1
        '↓wb1のSheet1のセルA1も"Hello"となる
        wb2.Worksheets("Sheet1").Range("A1") = "Hello!"
    End Sub
    

    SetSample3_3_1 と SetSample3_3_2は同じ結果になります。

    Sub SetSample3_3_2()
        Dim wb1 As Workbook
        Dim wb2 As Workbook
        Set wb1 = ThisWorkbook
        Set wb2 = wb1
        '↓【wb2】のSheet1のセルA1も"Hello"となる
        wb1.Worksheets("Sheet1").Range("A1") = "Hello!"
    End Sub
    

Set 変数 = Nothing は必要?

普通にVBAを使用する分には、不要となります。
なぜなら、VBAには、参照カウント方式という、オブジェクト変数の参照が完全になくなると、自動的にメモリが解放される仕組みがあります。
ただし、大きいプロシージャや大量のデータ、大規模な配列を使用する場合、メモリを解放する必要があります。 また、循環参照など特殊なケースではメモリが残ってしまうこともあるため、Set = Nothingを使って明示的に解放することが推奨される場面があります。

'Nothingの使用例
Sub SetSample4_2()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets(1)
    MsgBox ws.Name  '←MsgBox ThisWorkbook.Worksheets(1).Name と同じ
    'オブジェクト変数を初期化(解放)する
    Set ws = Nothing
End Sub

確認テスト

  1. 次の条件を満たすプログラムを作成しなさい。

    • マクロを実行しているブックの左から1番目のシートオブジェクトを取得する

    • シートオブジェクトより、シート名を「hoge」と変更する

      【解答例】

      Sub SetAnswer1()
          Dim ws As Worksheet
          Set ws = ThisWorkbook.Worksheets(1)
          ws.Name = "hoge"
          Set ws = Nothing
      End Sub
      
  2. 次の条件を満たすプログラムを作成しなさい。

    • マクロを実行しているブックの左から2番目のシート名をメッセージボックスで表示させる

    • シートオブジェクトを返すFunctionプロシージャを作成する
      (引数は、シート名とする)

      【解答例】

      ' シートオブジェクトを返すだけのプロシージャ
      Function GetSheetObj(Byval sName As String) As Worksheet
          Set GetSheetObj = ThisWorkbook.Worksheets(sName)
      End Function
      
      Sub SetAnswer2()
          Dim ws As Worksheet
          Set ws = GetSheetObj(ThisWorkbook.Worksheets(2).Name)
          MsgBox ws.Name
          Set ws = Nothing
      End Sub
      

関連リンク

ページの先頭へ