そのまんまです
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の加算の際に整数へのキャストが不要。良いんだか悪いんだか、、、よく知った上で使いましょう。