PowerPoint VBA-按值传递形状似乎是按引用进行的

PowerPoint VBA-按值传递形状似乎是按引用进行的,vba,powerpoint,Vba,Powerpoint,对于我正在编写的VBA脚本的一部分,我希望迭代当前幻灯片上的所有形状,并在每个形状的顶部插入另一个形状 我有第一个子例程GetShapes(),它获取当前幻灯片上的所有形状,然后将它们按值传递给第二个子例程LabelShapes(),该子例程将新形状添加到顶部 但是,新的形状似乎显示在传递的shapes对象中。似乎情况不应该如此,因为它是通过引用传递的 警告,下面的将快速锁定PowerPoint Sub GetShapes() Dim ss As Shapes Set ss =

对于我正在编写的VBA脚本的一部分,我希望迭代当前幻灯片上的所有形状,并在每个形状的顶部插入另一个形状

我有第一个子例程GetShapes(),它获取当前幻灯片上的所有形状,然后将它们按值传递给第二个子例程LabelShapes(),该子例程将新形状添加到顶部

但是,新的形状似乎显示在传递的shapes对象中。似乎情况不应该如此,因为它是通过引用传递的

警告,下面的将快速锁定PowerPoint

Sub GetShapes()
    Dim ss As Shapes
    Set ss = Application.ActiveWindow.View.Slide.Shapes
    Call LabelShapes(ss)
End Sub


Sub LabelShapes(ByVal ss As Shapes)
    Dim s As Shape
    For Each s In ss
        Debug.Print s.Name
        Application.ActiveWindow.View.Slide.Shapes.AddShape _
            Type:=msoShapeRectangle, Left:=50, Top:=50, Width:=15, Height:=15

    Next

End Sub

我想我可以通过对我的新形状使用特殊的命名约定,然后过滤掉它们来解决这个问题。也许有更好的办法?但是,我真的很想理解为什么这不是我所期望的那样。

不确定您到底想做什么,但是传递对象引用
ByVal
会神奇地创建对象的副本,这是一个常见的误解

通过val传递对象引用意味着传递对象指针的副本,而不是对同一个对象指针的引用

在这两种情况下,传递一个指向同一个对象的对象指针,所以当你<代码> .Ad形形形形色元>代码时,你正在改变你正在迭代的形状集合。

传递对象引用
ByVal
不会传递对象的副本。如果你想传递副本,你需要复制一份

这可能有助于澄清:

Public Sub DoSomething()
    Dim obj As Object
    Set obj = New Collection
    TestByVal obj 'pass a copy of the object reference
    Debug.Assert Not obj Is Nothing
    TestByRef (obj) 'force a copy of the object reference (despite ByRef)
    Debug.Assert Not obj Is Nothing
    TestByRef obj 'pass a reference to the object pointer
    Debug.Assert Not obj Is Nothing ' << assert will fail here
End Sub

Private Sub TestByVal(ByVal obj As Object)
    Set obj = Nothing ' only affects the local copy
End Sub

Private Sub TestByRef(ByRef obj As Object)
    Set obj = Nothing ' DANGER! call site will see this
End Sub
Public Sub DoSomething()
作为对象的Dim obj
Set obj=新集合
TestByVal obj'传递对象引用的副本
Assert Not obj什么都不是
TestByRef(obj)'强制复制对象引用(尽管ByRef)
Assert Not obj什么都不是
TestByRef obj'传递对对象指针的引用

Assert Not obj Is Nothing’解决方案是使用对象,它“表示一个形状范围,它是文档上的一组形状。”

文件中的注释:

如果要使用文档上形状的子集(例如,仅对文档上的自选形状或选定形状执行某些操作),则必须构造包含要使用的形状的ShapeRange集合


我认为你需要使用非常清晰和精确的对象-你在“如果你想传递一份副本,你需要制作一份副本”中有一句令人难忘的话。@BigBen谢谢-在这种情况下,我猜副本就是你的[同样优秀的]答案所指的
shaperage
实例;-)@克里斯:是的,这正是括号所做的——也是为什么你应该避免在不需要括号的地方使用多余的括号!传递对象引用
ByRef
ByVal
的原因与传递任何其他值
ByRef
ByVal
的原因完全相同:当您想要更改过程体中的指针/值,并让调用方看到更新的指针/值,或
ByVal
时,请使用
ByRef
(99%的时候你想要什么)当打电话的人不需要或不应该看到什么(意外?)可以在过程的主体中对其进行更改……这一操作的副作用可以通过
Set obj=Nothing
赋值来说明-它是指针的本地范围副本,带有
ByVal
,如果您传递它
ByRef
,那么您将得到相同的指针,修改它将在调用站点产生后果。@Chr是OOOOO R…你可以给它第666颗星(眨眼,轻推!),让一个VBE插件增强你的VBE工具,帮助你编写更好的代码,静态分析你的代码,告诉你什么时候
ByRef
参数应该是
ByVal
,反之亦然,为什么;-)(注:我拥有/管理那个OSS项目)
Sub GetShapes()
    Dim ss As ShapeRange
    Set ss = Application.ActiveWindow.View.Slide.Shapes.Range
    LabelShapes ss
End Sub


Sub LabelShapes(ByVal ss As ShapeRange)
    Dim s As Shape

    For Each s In ss
        Debug.Print s.Name
        Application.ActiveWindow.View.Slide.Shapes.AddShape _
            Type:=msoShapeRectangle, Left:=50, Top:=50, Width:=15, Height:=15

    Next
End Sub