Do I need to pass a worksheet as ByRef or ByVal?

前端 未结 1 471
有刺的猬
有刺的猬 2021-01-13 02:09

I broke out some code from a larger block and need to pass a worksheet to it...

I\'m not assigning any new value to the worksheet, but I am making changes to the Pag

相关标签:
1条回答
  • 2021-01-13 02:58

    Either will work, but for semantically correct code, prefer passing it by value (ByVal).

    When you pass an object variable by value, you're passing a copy of the pointer to the object.

    So what the procedure is working with is the same object (i.e. changed property values will be seen by the caller), except it's not allowed to Set the pointer to something else - well it can, but it'll do that on its own copy and so the caller won't be affected.

    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
    

    On the other hand...

    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
    

    In general, parameters should be passed by value. It's just an unfortunate language quirk that ByRef is the default (VB.NET fixed that).

    The same is true for non-object variables:

    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
    

    And...

    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
    

    If a variable is passed by reference, but is never reassigned in the body of a procedure, then it can be passed by value.

    If a variable is passed by reference, and reassigns it in the body of a procedure, then that procedure could likely be written as a Function, and actually return the modified value instead.

    If a variable is passed by value, and is reassigned in the body of a procedure, then the caller isn't going to see the changes - which makes code suspicious; if a procedure needs to reassign a ByVal parameter value, the intent of the code becomes clearer if it defines its own local variable and assigns that instead of the ByVal parameter:

    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
    

    These are all actual code inspections in Rubberduck, as VBE add-in I'm heavily involved with, that can analyze your code and see these things:

    Parameter is passed by value, but is assigned a new value/reference. Consider making a local copy instead if the caller isn't supposed to know the new value. If the caller should see the new value, the parameter should be passed ByRef instead, and you have a bug.

    http://rubberduckvba.com/Inspections/Details/AssignedByValParameterInspection

    A procedure that only has one parameter passed by reference that is assigned a new value/reference before the procedure exits, is using a ByRef parameter as a return value: consider making it a function instead.

    http://rubberduckvba.com/Inspections/Details/ProcedureCanBeWrittenAsFunctionInspection

    A parameter that is passed by reference and isn't assigned a new value/reference, could be passed by value instead.

    http://rubberduckvba.com/Inspections/Details/ParameterCanBeByValInspection

    That's the case of wsReport here:

    0 讨论(0)
提交回复
热议问题