41

この問題と同様に、VBA でオブジェクトを使用するScripting.Dictionary場合、以下のコードの結果は予期しないものになります。

Option Explicit

Sub test()

    Dim d As Variant
    Dim i As Integer
    Dim s As String
    Set d = CreateObject("Scripting.Dictionary")

    d.Add "a", "a"
    Debug.Print d.Count ' Prints '1' as expected

    For i = 1 To d.Count
        s = d.Item(i)
        Debug.Print s ' Prints ' ' (null) instead of 'a'
    Next i

    Debug.Print d.Count ' Prints '2' instead of '1'

End Sub

ゼロから始まるインデックスを使用すると、同じ結果が得られます。

For i = 0 To d.Count - 1
    s = d.Item(i)
    Debug.Print s
Next i

オブジェクトを見ると、実際には 2 つのアイテムがあることがわかります。新しく追加されたキーは1、 から追加されたようにiです。このループをより大きな数に増やすと、ディクショナリ内の項目の数がループごとに 1 回ずつ増えます。

Office/VBA 2003、2010、および 2013 でこれをテストしました。すべて同じ動作を示し、他のバージョン (2007) も同様であると予想します。

他のループ メソッドを使用してこれを回避できますが、オブジェクトを格納しようとしているときに不意を突かれ、その行でオブジェクトの予期されたエラーs = d.Item(i)が発生しました。

記録のために、私は次のようなことができることを知っています:

For Each v In d.Keys
    Set o = d.item(v)
Next v

しかし、なぜアイテムを番号で反復できないように見えるのか、もっと興味があります。

4

4 に答える 4

41

プロパティのドキュメントにItemよると:

Dictionary オブジェクトの指定されたキーの項目を設定または返します。

あなたの場合、キーが1そうしているアイテムはありません:

s = d.Item(i)

実際には辞書に新しいキーと値のペアを作成します。オプションのnewItem引数を使用していないため、値は空です。

Dictionary には、インデックスをループできるItemsメソッドもあります。

a = d.Items
For i = 0 To d.Count - 1
    s = a(i)
Next i
于 2012-07-02T15:31:26.977 に答える
38

assylias の回答に追加 - assylias は、D.ITEMS が配列を返すメソッドであることを示しています。それを知っていれば、バリアント配列 a(i) は必要ありません [以下の警告を参照してください]。適切な配列構文を使用する必要があるだけです。

For i = 0 To d.Count - 1
    s = d.Items()(i)
    Debug.Print s
Next i()

KEYS も同様に機能します

For i = 0 To d.Count - 1
    Debug.Print d.Keys()(i), d.Items()(i)
Next i

この構文は、これを明確にするのに役立つ SPLIT 関数にも役立ちます。SPLIT は、下限が 0 の配列も返します。したがって、次の例では "C" が出力されます。

Debug.Print Split("A,B,C,D", ",")(2)

SPLIT は関数です。そのパラメーターは、括弧の最初のセットにあります。メソッドと関数は、パラメーターが必要ない場合でも、常に最初の括弧のセットをパラメーターに使用します。この例では、SPLIT は配列 {"A","B","C","D"} を返します。これは配列を返すため、2 番目の括弧セットを使用して、配列の場合と同様に、返された配列内の要素を識別できます。

警告: この短い構文は、辞書全体を反復処理するときにバリアント配列 a() を使用する場合ほど効率的ではない可能性があります。これは、短い構文が各反復で辞書の Items メソッドを呼び出すためです。短い構文は、ディクショナリから番号で 1 つの項目を抽出するのに最適です。

于 2012-12-05T03:37:12.100 に答える
2

メソッドを使用d.Keys()(i)することは非常に悪い考えです。呼び出しのたびに新しい配列が再作成されるためです (速度が大幅に低下します)。

これは、Scripting.Dictionary@TheTrick の「Hash Table」クラスと呼ばれるもので、そのような列挙子をサポートしています: http://www.cyberforum.ru/blogs/354370/blog2905.html

Dim oDict As clsTrickHashTable

Sub aaa()
    Set oDict = New clsTrickHashTable

    oDict.Add "a", "aaa"
    oDict.Add "b", "bbb"

    For i = 0 To oDict.Count - 1
        Debug.Print oDict.Keys(i) & " - " & oDict.Items(i)
    Next
End Sub
于 2018-12-12T10:41:34.990 に答える
0

Craig Hatmaker のコードを試し、インデックス i をループして辞書 d の値を変更しました。d.Items(i) = s動作しません。しかし、d.Item(d.Keys(i)) = sうまくいきました。つまり、インデックスでキーを取得し、キーを使用してアイテムにアクセスすることで回避する必要があります。最初に s を使用して Items() を試し、次に s を使用せずに Item(Keys) を試したことに注意してください。これが優れたプログラミングであり、すべての場合に機能するかどうかはわかりませんが、誰かを助けることができるかもしれません.

于 2021-05-07T04:04:14.053 に答える