You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Excel VBA Dictionary添加方法、值/引用传递及内部变更机制的技术问询

关于VBA Dictionary的传递机制与内部运作详解

先贴出你提供的测试代码方便参考:

Sub test()
    Dim diz As New Dictionary
    Dim vett(1 To 2) As Integer
    Dim val As Integer
    Dim dvett As New Dictionary
    Dim a As Variant
    
    Select Case 3 '修改此值以运行不同分支
        Case 1
            val = 1
            diz.Add "a", val
            Debug.Print diz("a") '1
            diz("a") = 2
            Debug.Print diz("a") '2
            Debug.Print val '1 => 按值复制,而非按引用
            val = 3
            Debug.Print diz("a") '2 => 按值复制,而非按引用
        Case 4
            diz.Add "a", [{"a1","b1","c1"}]
            Debug.Print diz("a")(1) 'a1 - 正常
            diz.Items(0)(1) = "2" '无错误
            Debug.Print diz("a")(1) 'a1 !!!!!!!!!!!!! 无变化
            diz("a")(1) = "2"
            Debug.Print diz("a")(1) 'a1 !!!!!!!!!!!!! 无变化
            diz("a") = [{4,5,6}]
            Debug.Print diz("a")(1) '4 - 正常
        Case 2
            vett(1) = 1
            diz.Add "a", vett
            Debug.Print diz("a")(1) '1
            diz("a")(1) = 2 '无错误
            Debug.Print diz("a")(1) '1 !!!!!! 无变化
            Debug.Print vett(1) '1
            vett(1) = 3
            Debug.Print diz("a")(1) '1
            diz("a") = vett
            Debug.Print diz("a")(1) '3
        Case 3
            dvett(1) = 1
            diz.Add "a", dvett
            Debug.Print diz("a")(1) '1
            diz("a")(1) = 2
            Debug.Print diz("a")(1) '2
            Debug.Print dvett(1) '2 此场景下为按引用复制!!!!!
            dvett(1) = 3
            Debug.Print diz("a")(1) '3 此场景下为按引用复制!!!!!
    End Select
End Sub

我来逐一拆解你的疑问,结合上面的测试代码解释每种场景的行为逻辑:

1. 向Dictionary添加条目时,是按值还是按引用传递?

这个问题的核心完全取决于你存入的数据类型:

  • 值类型(比如Integer、String、固定大小数组等):按值传递。Dictionary会创建一份数据的完整副本存入内部哈希表,和原始变量彻底独立,两边的修改互不影响。
  • 引用类型(比如Dictionary对象、自定义类实例、Excel对象如Worksheet等):按引用传递。Dictionary只存储对象的内存地址指针,你通过Dictionary访问的就是原对象本身,修改会同步到原对象。

看测试代码的典型表现:

  • Case1里的val是Integer(值类型):Add后修改val,Dictionary里的"a"值没变;修改diz("a"),val也没变化,完全符合按值传递的特征。
  • Case3里的dvett是Dictionary对象(引用类型):Add后不管改diz("a")(1)还是dvett(1),两边都会同步变化,这就是引用传递的表现。

2. Dictionary的Add方法工作原理,以及测试代码中不同行为的原因

Dictionary.Add Key, Item的核心步骤其实很直白:

  1. 先检查Key是否已经存在,存在就直接抛错;
  2. 计算Key的哈希值,在内部哈希表中找到对应的存储位置;
  3. 把Key和Item存入该位置——这里的Item如果是值类型就存副本,如果是引用类型就存对象的内存指针。

你测试代码里不同Case的差异,本质就是值类型和引用类型的存储逻辑不同:

  • Case2的vett是固定大小Integer数组(值类型):Add时Dictionary复制了整个数组的副本。当你写diz("a")(1) = 2时,其实是先从Dictionary里取出数组的临时副本,修改的是这个副本的元素,副本用完就被销毁了,根本碰不到Dictionary里存的那个数组,所以打印出来还是1。只有重新执行diz("a") = vett,才会把vett的新副本存入Dictionary,这时候才会看到变化。
  • Case4的数组字面量[{"a1","b1","c1"}]:本质也是值类型数组,逻辑和Case2完全一样——存入的是副本,修改临时取出的数组元素不会影响Dictionary内部的存储,只有重新赋值数组才会替换条目。
  • Case3的Dictionary对象:存入的是引用指针,所以diz("a")直接指向原dvett对象,修改它的条目自然会同步到原对象。

3. 修改Dictionary中的值时,内部是怎么处理的?

分两种情况来看:

针对值类型条目

当你执行diz(Key) = NewValue时,Dictionary会直接定位到哈希表中对应Key的位置,用新值覆盖原来存储的副本——不是删除原有元素再添加新的,就是直接更新内存里的值,效率很高。比如Case1里diz("a") = 2,就是把哈希表中"a"对应的Integer值从1改成2,没有额外的删除操作。

针对引用类型条目

  • 如果是修改引用对象的内部内容(比如diz("a")(1) = 2,这里diz("a")是Dictionary对象):Dictionary本身没有任何变化,它存储的还是原来的对象指针,你修改的是指针指向的对象的内部数据,所以原对象也会跟着变。
  • 如果是给Key赋值新的对象(比如diz("a") = New Dictionary):这时候Dictionary会替换哈希表中对应Key的指针,原来的对象如果没有其他引用指向它,就会被VBA的垃圾回收机制清理——这一步是替换条目,但也不是删除再添加,只是更新指针地址。

顺便提下你关注的执行速度:

  • 值类型的添加和修改速度通常更快,因为只是复制或更新简单数据;
  • 引用类型的添加只是存个指针,速度也很快,但操作对象内部数据的速度取决于对象本身的复杂度;
  • 尽量避免像Case2那样频繁修改Dictionary里的值类型数组——每次取出都会生成副本,效率很低,不如先把数组取出来改完再存回去。

内容的提问来源于stack exchange,提问作者tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 19:28:11