我是否需要将工作表作为ByRef或ByVal传递?

时间:2017-01-23 18:16:15

标签: vba excel-vba pass-by-reference excel

我从一个更大的块中打破了一些代码,需要将工作表传递给它......

我没有为工作表分配任何新值,但我正在更改该工作表的分页符设置。我需要将其作为ByRef传递,还是ByVal足够好?

Private Sub SetPageBreaks(ByRef wsReport As Worksheet)
Dim ZoomNum As Integer

  wsReport.Activate
  ActiveWindow.View = xlPageBreakPreview
  ActiveSheet.ResetAllPageBreaks
  ZoomNum = 85
  With ActiveSheet
    Select Case wsReport.Name
      Case "Compare"
        Set .VPageBreaks(1).Location = Range("AI1")
        ZoomNum = 70
      Case "GM"
        .VPageBreaks.Add before:=Range("X1")
      Case "Drift"
        .VPageBreaks.Add before:=Range("T1")
      Case Else
        .VPageBreaks.Add before:=Range("U1")
    End Select
  End With
  ActiveWindow.View = xlNormalView
  ActiveWindow.Zoom = ZoomNum

End Sub

1 个答案:

答案 0 :(得分:6)

两者都可以,但对于语义正确的代码,更喜欢按值(ByVal)传递它。

当您按值传递对象变量时,您将指针副本传递给对象。

那么使用的过程是同一个对象(即调用者可以看到更改的属性值 ),除非它不被允许Set指向其他内容的指针 - 好吧可以,但它会在自己的副本上执行此操作,因此来电者赢了受到影响。

Public Sub DoSomething()
    Dim target As Worksheet
    Set target = ActiveSheet
    Debug.Print ObjPtr(target)
    DoSomethingElse target
    Debug.Print ObjPtr(target)
End Sub

Private Sub DoSomethingElse(ByVal target As Worksheet)
    Debug.Print ObjPtr(target)
    Set target = Worksheets("Sheet12")
    Debug.Print ObjPtr(target)
    'in DoSomething, target still refers to the ActiveSheet
End Sub

另一方面......

Public Sub DoSomething()
    Dim target As Worksheet
    Set target = ActiveSheet
    Debug.Print ObjPtr(target)
    DoSomethingElse target
    Debug.Print ObjPtr(target)
End Sub

Private Sub DoSomethingElse(ByRef target As Worksheet)
    Debug.Print ObjPtr(target)
    Set target = Worksheets("Sheet12")
    Debug.Print ObjPtr(target)
    'in DoSomething, target now refers to Worksheets("Sheet12")
End Sub

通常,参数应按值传递。这只是一个不幸的语言怪癖,ByRef是默认的(VB.NET修复了)。

非对象变量也是如此:

Public Sub DoSomething()
    Dim foo As Long
    foo = 42
    DoSomethingElse foo
End Sub

Private Sub DoSomethingElse(ByVal foo As Long)
    foo = 12
    'in DoSomething, foo is still 42
End Sub

和...

Public Sub DoSomething()
    Dim foo As Long
    foo = 42
    DoSomethingElse foo
End Sub

Private Sub DoSomethingElse(ByRef foo As Long)
    foo = 12
    'in DoSomething, foo is now 12
End Sub

如果变量通过引用传递,但从未在过程体中重新分配,则可以按值传递。

如果变量通过引用传递,并在过程的主体中重新分配,那么该过程可能会写为Function,并且实际上返回修改后的值

如果一个变量按值传递,并在一个过程的主体中重新分配,那么调用者就不会看到这些变化 - 这会使代码变得可疑;如果某个过程需要重新分配ByVal参数值,那么如果代码定义了自己的局部变量并且分配 而不是ByVal参数,则代码的意图会变得更清晰: / p>

Public Sub DoSomething()
    Dim foo As Long
    foo = 42
    DoSomethingElse foo
End Sub

Private Sub DoSomethingElse(ByVal foo As Long)
    Dim bar As Long
    bar = foo
    '...
    bar = 12
    '...
End Sub

这些都是Rubberduck中的实际代码检查,因为我非常重视VBE插件,可以分析您的代码并查看这些内容:

ByVal parameter is assigned

  

参数按值传递,但会分配一个新值/引用。如果调用者不应该知道新值,请考虑制作本地副本。如果调用者应该看到新值,那么该参数应该通过ByRef传递,并且你有一个错误。

     

<子> http://rubberduckvba.com/Inspections/Details/AssignedByValParameterInspection

Procedure can be written as a function

  

只有一个参数通过引用传递的过程在过程退出之前分配了一个新的值/引用,使用ByRef参数作为返回值:考虑将其改为函数。

     

<子> http://rubberduckvba.com/Inspections/Details/ProcedureCanBeWrittenAsFunctionInspection

Parameter can be passed by value

  

通过引用传递并且未分配新值/引用的参数可以通过值传递。

     

<子> http://rubberduckvba.com/Inspections/Details/ParameterCanBeByValInspection

这是wsReport的情况:

Parameter 'wsReport' can be passed by value