2017-06-12 100 views
0

我試圖運行下面的代碼來創建一個字典,然後在函數中使用該字典來根據字典鍵來分配函數參數值。Sub運行後的VBA字典<out of context>

Option Explicit 

Public b1 As Object 
Public var1 As Variant 
Public var2 As String 
Public var3 As Variant 

Sub CreateDictionaries() 
    Set b1 = CreateObject("Scripting.Dictionary") 
    b1.Add "key1", 0.009 
    b1.Add "key2", 0.011 
    b1.Add "key3", 0.014 
    b1.Add "key4", 0.025 
    b1.Add "key5", 0.045 
End Sub 

Public Function MyFunction(var1, var2, var3) 
    If var1 <= 5 Then 
     MyFunction = b1.Item(var2) * var1* var3 
    ElseIf var1 > 5 And var1 <= 10 Then 
     MyFunction = b1.Item(var2) * (var1 - 5) * var3 
    ElseIf var1 > 10 Then 
     MyFunction = b1.Item(var2) * (var1 - 10) * var3  
    End If 
End Function 

雖然這個工作最初,我暫時改變後的Sub到Static Sub它停止工作。將其更改回到上次工作狀態時尚未解決問題。重新啓動VBA並將代碼作爲新模塊運行也沒有奏效。

在調試過程中,我可以在Watch窗口中看到字典b1是按照它應該創建的,但是在Sub完成之後,它需要值「out of context」。它沒有任何意義,現在它讓我發瘋了!誰能幫忙?

回答

0

幾個要點:

  • 添加到Microsoft腳本運行時庫的引用;這將允許您使用的實際類型,而不是簡單的對象:

    Public b1 As Scripting.Dictionary 
    
  • 可以使用New語法初始化詞典:

    Public b1 As New Scripting.Dictionary 
    

    雖然這隻會造成字典;它不會填補它;這給我們帶來了下一個問題。

  • AFAIK,在VBA中沒有模塊的構造函數方法。所以你必須驗證字典已經被填滿,也許測試的初始化,以及,你嘗試使用它之前:

    Public b1 As Scripting.Dictionary 
    
    Sub InitializeDictionary() 
        If Not b1 Is Nothing Then Exit Sub 
        Set b1 = New Scripting.Dictionary 
        b1.Add "key1", 0.009 
        b1.Add "key2", 0.011 
        b1.Add "key3", 0.014 
        b1.Add "key4", 0.025 
        b1.Add "key5", 0.045 
    End Sub 
    
    Public Function MyFunction(var1, var2, var3) 
        InitializeDictionary 
        If var1 <= 5 Then 
         MyFunction = b1.Item(var2) * var1* var3 
        ElseIf var1 > 5 And var1 <= 10 Then 
         MyFunction = b1.Item(var2) * (var1 - 5) * var3 
        ElseIf var1 > 10 Then 
         MyFunction = b1.Item(var2) * (var1 - 10) * var3  
        End If 
    End Function 
    
  • Static關鍵字不會在這裏做一個區別。按照documentation,在FunctionSubStatic關鍵字:

    表明Sub過程的局部變量調用之間保留。 Static屬性不影響在Sub之外聲明的變量,即使它們在過程中使用。

    ,並在這種情況下,b1變量已申報Sub之外。

    使用Static語句聲明模塊級變量(如b1)也將沒有任何效果 - 在這兩種情況下的價值將持續到代碼復位。在程序中聲明變量時,它只會有所不同。

  • 關於監視窗口和變量<out of context>,當你在VBA IDE添加關注,你需要指定上下文<all procedures><all modules>。否則,僅當調試器在所選模塊內停止並在所選過程內纔會對Watch變量進行評估。

+0

這不是一個好主意來自動實例化對象變量:http://www.cpearson.com/excel/declaringvariables.aspx – CallumDA

+0

@CallumDA如果你想要確保變量引用一個'Dictionary'而不是'Nothing',那麼我沒有看到任何理由不使用自動實例語法 - 每一個你sage將用新的'Dictionary'填充變量。只有當初始化比簡單地創建一個新的'Dictionary'更復雜 - 例如用某些值填充它 - 自動實例化是無用的;正如我在答覆中指出的那樣。 –

+0

在你的代碼中,你寫'If Not b1 Is Nothing' - 這是不行的,因爲你還沒有創建字典。此外,如果您更改了「Public b1 As New Dictionary」,它仍然不會出於同樣的原因(請參閱鏈接)。僅供參考我不只是在猜測這個,我測試了你的代碼,它不起作用。 – CallumDA

0

創建一個StaticDictionary裏面的函數。字典將在第一次使用該函數時填充,但在此之後,值將被保留並跳過相關代碼。

Option Explicit 

Public Function MyFunction(var1, var2, var3) 

    Static b1 As Object 
    If b1 Is Nothing Then 
     Set b1 = CreateObject("Scripting.Dictionary") 
     b1.Add "key1", 0.009 
     b1.Add "key2", 0.011 
     b1.Add "key3", 0.014 
     b1.Add "key4", 0.025 
     b1.Add "key5", 0.045 
    End If 

    If var1 <= 5 Then 
     MyFunction = b1.Item(var2) * var1* var3 
    ElseIf var1 > 5 And var1 <= 10 Then 
     MyFunction = b1.Item(var2) * (var1 - 5) * var3 
    ElseIf var1 > 10 Then 
     MyFunction = b1.Item(var2) * (var1 - 10) * var3  
    End If 
End Function 

正如其他人所指出的那樣,通常最好早期綁定的變量。這意味着如果你知道你想要一個Dictionary對象,首先應避免先聲明Object,然後將其轉換爲Dictionary,此時你可以立即聲明它爲Dictionary。要做到這一點,你需要包括參考腳本運行時庫和使用:

Dim b1 As Scripting.Dictionary 
Set b1 = New Scripting.Dictionary 

不要使用下面的單As New語句,因爲它可能會導致不必要的錯誤(more here)

Dim b1 As New Scripting.Dictionary 
+0

謝謝你的回覆。在函數中構建字典的工作非常好。 – PhysFreak