サーチ…


暗黙的かつ明示的な宣言

コードモジュールにモジュールの先頭にOption Explicitが含まれていない場合、コンパイラは自動的に(暗黙的に)作成変数を使用します。それらは、デフォルトでVariantVariantます。

Public Sub ExampleDeclaration()    

    someVariable = 10                  ' 
    someOtherVariable = "Hello World"
    'Both of these variables are of the Variant type.

End Sub

上記のコードでOption Explicitが指定されている場合、 someVariableおよびsomeOtherVariable必要なDimステートメントがないため、コードが中断します。

Option Explicit

Public Sub ExampleDeclaration()   

    Dim someVariable As Long 
    someVariable = 10

    Dim someOtherVariable As String
    someOtherVariable = "Hello World"

End Sub

コードモジュールでOption Explicitを使用して、すべての変数を確実に宣言することをお勧めします。

このオプションをデフォルトで設定する方法については、 VBAのベストプラクティスを参照してください。

変数

範囲

変数を宣言することができます(可視性レベルを上げる):

  • プロシージャ・レベルでは、任意のプロシージャでDimキーワードを使用します。 ローカル変数
  • モジュールレベルでは、どのタイプのモジュールでもPrivateキーワードを使用します。 プライベートフィールド
  • インスタンスレベルでは、どのタイプのクラスモジュールでもFriendキーワードを使用します。 友人のフィールド
  • インスタンス・レベルでは、どのタイプのクラス・モジュールでもPublicキーワードを使用します。 パブリックフィールド
  • 標準モジュールPublicキーワードを使用してグローバルにグローバル変数

変数は常に最小限のスコープで宣言する必要があります。グローバル変数を宣言するのではなく、プロシージャにパラメータを渡すことを優先します。

詳細については、 アクセス修飾子を参照してください。


ローカル変数

Dimキーワードを使用してローカル変数を宣言します

Dim identifierName [As Type][, identifierName [As Type], ...]

宣言構文の[As Type]部分はオプションです。これを指定すると、変数のデータ型が設定され、その変数に割り当てられるメモリ量が決まります。これはString変数を宣言します:

Dim identifierName As String

型が指定されていない場合、型は暗黙的に型Variant

Dim identifierName 'As Variant is implicit

VBA構文では、1つのステートメントで複数の変数を宣言することもできます。

Dim someString As String, someVariant, someValue As Long

各変数([Variant]以外)に対して[As Type]を指定する必要があることに注意してください。これは比較的一般的なトラップです:

Dim integer1, integer2, integer3 As Integer 'Only integer3 is an Integer. 
                                            'The rest are Variant.

静的変数

ローカル変数はStaticでもStaticません。 VBAでは、 Staticキーワードを使用して、変数に前回プロシージャが呼び出されたときの値を「記憶」します。

Private Sub DoSomething()
    Static values As Collection
    If values Is Nothing Then
        Set values = New Collection
        values.Add "foo"
        values.Add "bar"
    End If
    DoSomethingElse values
End Sub

ここでvaluesコレクションはStaticローカルとして宣言されています。これはオブジェクト変数であるため、 Nothing初期化されます。宣言に続く条件は、オブジェクト参照が前にSetかどうかを検証します。プロシージャが初めて実行される場合、コレクションは初期化されます。 DoSomethingElseがアイテムを追加または削除している可能性があります。次回DoSomethingが呼び出されたときにコレクションに残ります。

オルタナティブ

VBAのStaticキーワードを簡単に誤解することができます- 特に 、通常は他の言語で働く経験豊かなプログラマーで。多くの言語では、クラスメンバ(フィールド、プロパティ、メソッドなど)をインスタンスではなくタイプにするためにstaticが使用されていますstaticコンテキスト内のコードは、 インスタンスコンテキスト内のコードを参照できません。 VBAのStaticキーワードは、大きく違うものを意味します。

しばしば、 Staticローカルは、モジュールレベルのPrivate変数(フィールド)として実装することもできますが、変数を最小限のスコープで宣言する必要があります。あなたの本能を信頼し、好きな方を使う - どちらもうまくいきますが、それが何を理解することなくStaticを使用すると、面白いバグにつながる可能性があります。


暗い対私的

Dimキーワードは、プロシージャとモジュールレベルでは合法です。モジュールレベルでのその使用法は、 Privateキーワードの使用と同じです:

Option Explicit
Dim privateField1 As Long 'same as Private privateField2 as Long
Private privateField2 As Long 'same as Dim privateField2 as Long

Privateキーワードは、モジュールレベルでのみ有効です。これはDimをローカル変数に予約し、モジュール変数をPrivateで宣言します。特にパブリックメンバを宣言するために使用しなければならない対照的なPublicキーワードを使用します。代わりにDim どこでも使用しください。 一貫性が重要です。

"プライベートフィールド"

  • 使用います Privateモジュールレベルの変数を宣言します。
  • 使うのですか Dimローカル変数を宣言します。
  • Dimを使用してモジュールレベルの変数を宣言しないでください

"どこにでも暗い"

  • 使うのですか Dimローカル/プライベート何かを宣言します。
  • Privateを使用してモジュールレベルの変数を宣言しないでください
  • Publicフィールドの宣言を避ける *

*一般的には、とにかくPublicまたはGlobalフィールドを宣言しないでください。


フィールド

モジュール本体の上部にある宣言セクションのモジュールレベルで宣言された変数は、 フィールドです。 標準モジュールで宣言されたPublicフィールドは、 グローバル変数です

Public PublicField As Long

グローバルスコープの変数は、宣言されているプロジェクトを参照する他のVBAプロジェクトを含め、どこからでもアクセスできます。

変数をグローバル/パブリックにするには、プロジェクト内からしか見ることができないようにするには、 Friend修飾子を使用します。

Friend FriendField As Long

これは、他のVBAプロジェクトがアドインプロジェクトを参照し、パブリックAPIを使用できるというアドインでは特に便利です。

Friend FriendField As Long 'public within the project, aka for "friend" code
Public PublicField As Long 'public within and beyond the project

Friendフィールドは、標準モジュールでは使用できません。


インスタンスフィールド

クラスモジュール( ThisWorkbookThisDocumentWorksheetUserFormクラスモジュールを含む)の本体の上部にある宣言セクションのモジュールレベルで宣言された変数は、 インスタンスフィールドです。 インスタンスが存在する限り存在します周りのクラス。

'> Class1
Option Explicit
Public PublicField As Long
'> Module1
Option Explicit
Public Sub DoSomething()
    'Class1.PublicField means nothing here
    With New Class1
        .PublicField = 42
    End With
    'Class1.PublicField means nothing here
End Sub

カプセル化フィールド

インスタンスデータはしばしばPrivateに保たれ、 カプセル化されたダビングされます 。プライベートフィールドは、 Propertyプロシージャを使用して公開することができます。呼び出し元への書き込みアクセスを与えずにプライベート変数を公開するために、クラスモジュール(または標準モジュール)はProperty Getメンバを実装します:

Option Explicit
Private encapsulated As Long

Public Property Get SomeValue() As Long
    SomeValue = encapsulated
End Property

Public Sub DoSomething()
    encapsulated = 42
End Sub

クラス自体はカプセル化された値を変更することができますが、呼び出し元のコードはPublicメンバー(および呼び出し元が同じプロジェクトにある場合はFriendメンバー)にのみアクセスできます。

発信者が以下を変更できるようにするには:

  • カプセル化されたであるモジュールは、 Property Letメンバーを公開します。
  • カプセル化されたオブジェクト参照で、モジュールはProperty Setメンバを公開します。

定数(Const)

アプリケーションで決して変更されない値がある場合は、名前付き定数を定義してリテラル値の代わりに使用できます。

Constは、モジュールまたはプロシージャ・レベルでのみ使用できます。つまり、変数の宣言コンテキストは、クラス、構造体、モジュール、プロシージャ、またはブロックでなければならず、ソースファイル、名前空間、またはインタフェースにすることはできません。

Public Const GLOBAL_CONSTANT As String = "Project Version #1.000.000.001"
Private Const MODULE_CONSTANT As String = "Something relevant to this Module"

Public Sub ExampleDeclaration()    

    Const SOME_CONSTANT As String = "Hello World"
    
    Const PI As Double = 3.141592653

End Sub

Constant型を指定することをお勧めしますが、厳密には必須ではありません。タイプを指定しないと正しいタイプになります:

Public Const GLOBAL_CONSTANT = "Project Version #1.000.000.001" 'Still a string
Public Sub ExampleDeclaration()

    Const SOME_CONSTANT = "Hello World"           'Still a string
    Const DERIVED_CONSTANT = SOME_CONSTANT        'DERIVED_CONSTANT is also a string
    Const VAR_CONSTANT As Variant = SOME_CONSTANT 'VAR_CONSTANT is Variant/String
    
    Const PI = 3.141592653        'Still a double
    Const DERIVED_PI = PI         'DERIVED_PI is also a double
    Const VAR_PI As Variant = PI  'VAR_PI is Variant/Double
    
End Sub

これは定数に固有であり、タイプを指定しない変数とは対照的に、バリアント型の結果となることに注意してください。

定数を明示的にStringとして宣言することは可能ですが、固定幅の文字列構文を使用して定数を文字列として宣言することはできません

'This is a valid 5 character string constant
Const FOO As String = "ABCDE"

'This is not valid syntax for a 5 character string constant
Const FOO As String * 5 = "ABCDE"

アクセス修飾子

Dimステートメントは、ローカル変数用に予約する必要があります。モジュールレベルでは、明示的なアクセス修飾子を使用することをお勧めします。

  • PrivateフィールドのPrivate 。プライベートフィールドは、宣言されたモジュール内でしかアクセスできません。
  • Publicフィールドとグローバル変数のパブリック。任意の呼び出しコードからアクセスできます。
  • プロジェクト内のパブリック変数はFriendですが、他の参照用VBAプロジェクトにはアクセスできません(アドインに関連しています)
  • Globalは、標準モジュールのPublicフィールドにも使用できますが、クラスモジュールでは不正です。代わりにPublic修飾子を使用することをお勧めします。この修飾子は、プロシージャにとっても合法ではありません。

アクセス修飾子は、変数およびプロシージャにも同様に適用できます。

Private ModuleVariable As String
Public GlobalVariable As String

Private Sub ModuleProcedure()

    ModuleVariable = "This can only be done from within the same Module"

End Sub

Public Sub GlobalProcedure()

    GlobalVariable = "This can be done from any Module within this Project"

End Sub

オプションプライベートモジュール

公開パラメータなし標準モジュールのSubプロシージャは、マクロとして公開され、ホスト文書のコントロールおよびキーボードショートカットにアタッチできます。

逆に、標準モジュールのpublic Functionプロシージャは、ホストアプリケーション内のユーザ定義関数(UDF)として公開されます。

標準モジュールの上部にOption Private Moduleを指定すると、メンバーがマクロおよびUDFとしてホストアプリケーションに公開されなくなります。

タイプヒント

タイプヒントはあまりお勧めできません。それらは存在し、歴史的および後方互換性の理由からここに文書化されています。代わりにAs [DataType]構文を使用する必要がありAs [DataType]

Public Sub ExampleDeclaration()

        Dim someInteger% '% Equivalent to "As Integer"
        Dim someLong&    '& Equivalent to "As Long"
        Dim someDecimal@ '@ Equivalent to "As Currency"
        Dim someSingle!  '! Equivalent to "As Single"
        Dim someDouble#  '# Equivalent to "As Double"
        Dim someString$  '$ Equivalent to "As String"

        Dim someLongLong^  '^ Equivalent to "As LongLong" in 64-bit VBA hosts
End Sub

タイプヒントは大幅にコードの可読性を低下させ、従来の奨励ハンガリアン記法 読みやすさを妨げます。

Dim strFile$
Dim iFile%

代わりに、使用法に近い変数を宣言し、使用したものの名前を型の後ではなく名前にします。

Dim path As String
Dim handle As Integer

型ヒントは、特定の型を強制するためにリテラルでも使用できます。デフォルトでは、32,768より小さい数値リテラルはIntegerリテラルとして解釈されますが、ヒントタイプを使用してそれを制御できます。

Dim foo 'implicit Variant
foo = 42& ' foo is now a Long
foo = 42# ' foo is now a Double
Debug.Print TypeName(42!) ' prints "Single"

型ヒントは、明示的な型で宣言された変数に代入されるか、またはパラメータとして渡されたときに暗黙的に適切な型に変換されるため、通常はリテラルには必要ありません。暗黙的な変換は、明示的な型変換関数の1つを使用して回避できます。

'Calls procedure DoSomething and passes a literal 42 as a Long using a type hint
DoSomething 42&

'Calls procedure DoSomething and passes a literal 42 explicitly converted to a Long
DoSomething CLng(42)

文字列を返す組み込み関数

文字列を処理する組み込み関数の大部分は、 Variantを返す緩やかに型付けされたバージョンと、 Stringを返す厳密に型指定されたバージョン( $終わる)の2つのバージョンがあります。返り値をVariantに代入している場合を除き、 Stringを返すバージョンを選ぶ必要があります。そうでなければ、返り値の暗黙の変換があります。

Debug.Print Left(foo, 2)  'Left returns a Variant
Debug.Print Left$(foo, 2) 'Left$ returns a String

これらの機能は次のとおりです。

  • VBA.Conversion.Error - > VBA.Conversion.Error $
  • VBA.Conversion.Hex - > VBA.Conversion.Hex $
  • VBA.Conversion.Oct - > VBA.Conversion.Oct $
  • VBA.Conversion.Str - > VBA.Conversion.Str $
  • VBA.FileSystem.CurDir - > VBA.FileSystem.CurDir $
  • VBA。[_ HiddenModule] .Input - > VBA。[_ HiddenModule] .Input $
  • VBA。[_ HiddenModule] .InputB - > VBA。[_ HiddenModule] .InputB $
  • VBA.Interaction.Command - > VBA.Interaction.Command $
  • VBA.Interaction.Environ - > VBA.Interaction.Environ $
  • VBA.Strings.Chr - > VBA.Strings.Chr $
  • VBA.Strings.ChrB - > VBA.Strings.ChrB $
  • VBA.Strings.ChrW - > VBA.Strings.ChrW $
  • VBA.Strings.Format - > VBA.Strings.Format $
  • VBA.Strings.LCase - > VBA.Strings.LCase $
  • VBA.Strings.Left - > VBA.Strings.Left $
  • VBA.Strings.LeftB - > VBA.Strings.LeftB $
  • VBA.Strings.LTtrim - > VBA.Strings.LTrim $
  • VBA.Strings.Mid - > VBA.Strings.Mid $
  • VBA.Strings.MidB - > VBA.Strings.MidB $
  • VBA.Strings.Right - > VBA.Strings.Right $
  • VBA.Strings.RightB - > VBA.Strings.RightB $
  • VBA.Strings.RTrim - > VBA.Strings.RTrim $
  • VBA.Strings.Space - > VBA.Strings.Space $
  • VBA.Strings.Str - > VBA.Strings.Str $
  • VBA.Strings.String - > VBA.Strings.String $
  • VBA.Strings.Trim - > VBA.Strings.Trim $
  • VBA.Strings.UCase - > VBA.Strings.UCase $

これらは関数のエイリアスであり、 型ヒントではないことに注意してください。 Left関数は隠れたB_Var_Left関数に対応し、 Left$は隠れたB_Str_Left関数に対応します。

VBAの非常に初期のバージョンでは、 $記号は許可された文字ではなく、関数名は角括弧で囲む必要がありました。 Word Basicでは、$で終わった文字列を返す関数が数多くありました。

固定長文字列の宣言

VBAでは、文字列は特定の長さで宣言できます。宣言されている長さに維持されるように自動的にパディングまたは切り捨てられます。

Public Sub TwoTypesOfStrings()

    Dim FixedLengthString As String * 5 ' declares a string of 5 characters
    Dim NormalString As String

    Debug.Print FixedLengthString       ' Prints "     "
    Debug.Print NormalString            ' Prints ""

    FixedLengthString = "123"           ' FixedLengthString now equals "123  "
    NormalString = "456"                ' NormalString now equals "456"

    FixedLengthString = "123456"        ' FixedLengthString now equals "12345"
    NormalString = "456789"             ' NormalString now equals "456789"

End Sub

静的変数を使用する場合

ローカルで宣言されたStatic変数は破棄されず、Subプロシージャが終了したときに値が失われません。それ以降のプロシージャの呼び出しでは、再初期化や割り当ては必要ありませんが、記憶された値は「0」にすることもできます。

これらは、繰り返し呼び出される 'ヘルパー'サブオブジェクトにレイトバインドするときに特に便利です。

スニペット1:多くのワークシートにわたるScripting.Dictionaryオブジェクトの再利用

Option Explicit

Sub main()
    Dim w As Long
    
    For w = 1 To Worksheets.Count
        processDictionary ws:=Worksheets(w)
    Next w
End Sub

Sub processDictionary(ws As Worksheet)
    Dim i As Long, rng As Range
    Static dict As Object
    
    If dict Is Nothing Then
        'initialize and set the dictionary object
        Set dict = CreateObject("Scripting.Dictionary")
        dict.CompareMode = vbTextCompare
    Else
        'remove all pre-existing dictionary entries
        ' this may or may not be desired if a single dictionary of entries
        ' from all worksheets is preferred
        dict.RemoveAll
    End If
    
    With ws
        
        'work with a fresh dictionary object for each worksheet
        ' without constructing/destructing a new object each time
        ' or do not clear the dictionary upon subsequent uses and 
        ' build a dictionary containing entries from all worksheets
    
    End With
End Sub

スニペット2: VBScript.RegExpオブジェクトを後でバインドするワークシートUDFを作成する

Option Explicit

Function numbersOnly(str As String, _
                     Optional delim As String = ", ")
    Dim n As Long, nums() As Variant
    Static rgx As Object, cmat As Object

    'with rgx as static, it only has to be created once
    'this is beneficial when filling a long column with this UDF
    If rgx Is Nothing Then
        Set rgx = CreateObject("VBScript.RegExp")
    Else
        Set cmat = Nothing
    End If
    
    With rgx
        .Global = True
        .MultiLine = True
        .Pattern = "[0-9]{1,999}"
        If .Test(str) Then
            Set cmat = .Execute(str)
            'resize the nums array to accept the matches
            ReDim nums(cmat.Count - 1)
            'populate the nums array with the matches
            For n = LBound(nums) To UBound(nums)
                nums(n) = cmat.Item(n)
            Next n
            'convert the nums array to a delimited string
            numbersOnly = Join(nums, delim)
        Else
            numbersOnly = vbNullString
        End If
    End With
End Function

static_UDF
50万行に渡って静的オブジェクトを埋め込んだUDFの例

* UDFで500Kの行を満たす経過時間:
- Dim rgx Asオブジェクト :148.74秒
- スタティックrgxオブジェクト :26.07秒
* これらは相対比較のみを考慮してください。あなた自身の結果は複雑さと
実行される操作の範囲。

ワークブックの存続期間中はUDFは一度も計算されません。不揮発性UDFであっても、参照する範囲内の値が変更されるたびに再計算されます。後続の再計算イベントが発生するたびに、静的に宣言された変数の利点が増えます。

  • 静的変数は、宣言され割り当てられたプロシージャまたは関数ではなく、モジュールの存続期間中に使用できます。
  • 静的変数はローカルでのみ宣言できます。
  • 静的変数は、プライベートモジュールレベル変数の同じプロパティの多くを保持しますが、スコープは制限されています。

関連資料: 静的(Visual Basic)



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow