1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【VBA小ネタ】Dictionaryのアイテムに配列を突っ込むと読み取り専用になる

Posted at

そのまんまです

VBAのDictionaryのアイテムに配列を突っ込むと読み取り専用になってしまうという話。

Module1
Sub Hoge()
    Dim dic As New Dictionary
    Dim i As Integer
    For i = 0 To 3
        Dim arr(1) As Variant
        arr(0) = "文字列" & i
        arr(1) = i
        Call dic.Add("キー" & i, arr)
    Next
    i = 3 ' 最後のアイテムを書き換えてみる
    Debug.Print "---書き換え前----------"
    Debug.Print dic("キー" & i)(0) & ": " & dic("キー" & i)(1)
    Debug.Print "---書き換え後----------"
    dic("キー" & i)(0) = dic("キー" & i)(0) & "_追記"
    dic("キー" & i)(1) = dic("キー" & i)(1) + 1
    For i = 0 To 3
        Debug.Print dic("キー" & i)(0) & ": " & dic("キー" & i)(1)
    Next
End Sub
結果1
---書き換え前----------
文字列3: 3
---書き換え後----------
文字列0: 0
文字列1: 1
文字列2: 2
文字列3: 3

インデックス3のアイテムは、文字列3_追記: 4となることを期待したが結果は上記のとおりだった。何も変わっていない。配列の各要素を取得できるが設定はできない。
そして解せないのが、代入の際にエラーにならないことだ。

しかし、これはアイテムを丸ごと置き換えることで解決する。

Module2
Sub Huge()
    Dim dic As New Dictionary
    Dim i As Integer
    For i = 0 To 3
        Dim arr(1) As Variant
        arr(0) = "文字列" & i
        arr(1) = i
        Call dic.Add("キー" & i, arr)
    Next
    i = 3 ' 最後のアイテムを書き換えてみる
    Debug.Print "---書き換え前----------"
    Debug.Print dic("キー" & i)(0) & ": " & dic("キー" & i)(1)
    Debug.Print "---書き換え後----------"
    arr(0) = dic("キー" & i)(0) & "_追記"
    arr(1) = dic("キー" & i)(1) + 1
    dic("キー" & i) = arr ' <-値を設定した配列を丸ごと代入する
    For i = 0 To 3
        Debug.Print dic("キー" & i)(0) & ": " & dic("キー" & i)(1)
    Next
End Sub
結果2
---書き換え前----------
文字列3: 3
---書き換え後----------
文字列0: 0
文字列1: 1
文字列2: 2
文字列3_追記: 4

ちなみに

当然ながら、VB.NETやC#では、この現象は起らない。

App1.vb
    Sub Main()
        Dim dic As New Dictionary(Of String, Object())
        Dim i As Integer
        For i = 0 To 3
            Dim arr(1) As Object
            arr(0) = "文字列" & i
            arr(1) = i
            dic.Add("キー" & i, arr)
        Next
        i = 3 ' 最後のアイテムを書き換えてみる
        Console.WriteLine("---書き換え前----------")
        Console.WriteLine($"{dic("キー" & i)(0)}: {dic("キー" & i)(1)}")
        Console.WriteLine("---書き換え後----------")
        dic("キー" & i)(0) = dic("キー" & i)(0) & "_追記"
        dic("キー" & i)(1) = dic("キー" & i)(1) + 1
        For i = 0 To 3
            Console.WriteLine($"{dic("キー" & i)(0)}: {dic("キー" & i)(1)}")
        Next
    End Sub
App1.cs
var dic = new Dictionary<string, object[]>();
int i;
for (i = 0; i < 4; i++)
{
    var arr = new object[2];
    arr[0] = "請求書" + i;
    arr[1] = i;
    dic.Add("キー" + i, arr);
}
i = 3; // 最後のアイテムを書き換えてみる
Console.WriteLine("---書き換え前----------");
Console.WriteLine($"{dic["キー" + i][0]}: {dic["キー" + i][1]}");
Console.WriteLine("---書き換え後----------");
dic["キー" + i][0] = dic["キー" + i][0] + "_追記";
dic["キー" + i][1] = (int)dic["キー" + i][1] + 1;
for (i = 0; i < 4; i++)
{
    Console.WriteLine($"{dic["キー" + i][0]}: {dic["キー" + i][1]}");
}

VB.NETはObject型配列宣言でNewがいらない。また、配列要素1の加算の際に整数へのキャストが不要。良いんだか悪いんだか、、、よく知った上で使いましょう。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?