6

I've tried to attempt something that was answered by JaredPar ByRef vs ByVal Clarification

ByVal in VB.NET means that a copy of the provided value will be sent to the function. For value types (Integer, Single, etc.) this will provide a shallow copy of the value. With larger types this can be inefficient. For reference types though (String, class instances) a copy of the reference is passed. Because a copy is passed in mutations to the parameter via = it won't be visible to the calling function.

ByRef in VB.NET means that a reference to the original value will be sent to the function (1). It's almost like the original value is being directly used within the function. Operations like = will affect the original value and be immediately visible in the calling function.

And I've tried to test it with the following code and I can't seem to get it to work use the ByRef to change the value of the cell to 0 if it's 1

Here's my below code that I'm testing with, what am I doing wrong? The value of Range("A1") is still 1

Sub test1()

    Dim trythis As Boolean

    trythis = False

    If (Sheets("TESTING").Range("A1").value = 1) Then
        testingRoutine (trythis)
        If (trythis) Then
            Debug.Print "Value changed to 0"
            Sheets("TESTING").Range("A1").value = 0
        End If
    End If

End Sub

Private Function testingRoutine(ByRef trythis As Boolean)

    Debug.Print "Ran Function"
    trythis = True

End Function
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Maldred
  • 1,074
  • 4
  • 11
  • 33
  • 2
    Sad.. this was all thoroughly documented in Documentation.SO - which is now gone. Your use of superfluous parentheses will bite you in the @$$ one day or another if you don't drop that habit. Also a `Function` procedure should have a return type - `Private Function testingRoutine(ByRef tryThis As Boolean) As Boolean` - otherwise you're wrapping your return value into a `Variant`. Note that in VBA the implicit default is `ByRef` (which was stupid, which is why VB.NET fixed that). See [this answer](https://stackoverflow.com/a/46841174/1188513) for more `ByRef`/`ByVal` madness involving arrays. – Mathieu Guindon Oct 26 '17 at 17:52
  • @Mat'sMug Thanks for the information; if I don't plan on returning a value with the function what should I be using? A sub then? – Maldred Oct 26 '17 at 18:10
  • 1
    Yes. A `Function` takes some input and *returns a result*. A `Sub` *does something*, usually with side-effects (changes to object, application, or global state). – Mathieu Guindon Oct 26 '17 at 18:11
  • The thing I found annoying about that is every time I go to look at my macro list, every sub-function was listed here... Is there a way to avoid this? – Maldred Oct 26 '17 at 18:13
  • This is extremely useful information, thank you so much! I was just going with a lot trial and error and see what works and what doesn't. If you can put this information in an answer format, I'd gladly provide you with the Answered mark on it :) – Maldred Oct 26 '17 at 18:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/157605/discussion-between-mats-mug-and-maldred). – Mathieu Guindon Oct 26 '17 at 18:20

4 Answers4

12

VB subroutines don't require braces around the argument list. However, if you pass a single argument and you enclose that in braces, you are passing an expression . Expressions cannot be passed by reference. They are evaluated and their result is passed. Therefore you must remove the braces in the call testingRoutine (trythis) and write testingRoutine trythis

Note: if you call a function without using its return value, it must be written as a procedure call (without braces around the argument list). As an example:

myVal = myFunction (trythis)   ' trythis will be passed by reference
myFunction (trythis)           ' trythis will be seen as an expression
myFunction trythis             ' trythis will be passed by reference

myVal = mySub (trythis)        ' invalid: mySub is not a function
mySub (trythis)                ' trythis will be seen as an expression
mySub trythis                  ' trythis will be passed by reference

Of course, the problem will be clear immediately when a function or sub has more than one parameter because a comma cannot appear in an expression.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
3

Change this:

testingRoutine (trythis)

to this:

testingRoutine trythis

then try it.

Also, look what happens if I change it to this, where the function is being used as a function (returning something)

Sub test1()

    Dim trythis As Boolean
    Dim testit As Boolean

    trythis = False

    If (Sheets("Sheet1").Range("A1").Value = 1) Then
        testit = testingRoutine(trythis)
        If (trythis) Then
            Debug.Print "Value changed to 0"
            Sheets("TESTING").Range("A1").Value = 0
        End If
    End If

End Sub

Private Function testingRoutine(ByRef trythis As Boolean) As Boolean

    Debug.Print "Ran Function"
    trythis = True

End Function
braX
  • 11,506
  • 5
  • 20
  • 33
  • So why does that work? But with the `()` it doesn't? – Maldred Oct 26 '17 at 17:09
  • 1
    with parens means it's attempting to return a value but there's no variable to assign it to. Without parens means that it's just performing the requested functions and not attempting to return anything. – n8. Oct 26 '17 at 17:12
  • Very interesting... As much as I appreciate your assistance on my question it does answer what I was doing wrong, but I'd like to know why. Paul Ogilvie was able to answer my question a little more thoroughly, so I gave the answer check to him! Sorry :( – Maldred Oct 26 '17 at 17:19
  • 2
    No, that's cool - I dont mind. His answer was better, I was just faster. :P – braX Oct 26 '17 at 17:19
1

I would do it this way.

Sub test1()

    Dim trythis As Boolean

    trythis = False

    If (Sheets("TESTING").Range("A1").value = 1) Then
        tr = testingRoutine(trythis)
        If tr Then
            Debug.Print "Value changed to 0"
            Sheets("TESTING").Range("A1").value = 0
        End If
    End If

End Sub

Private Function testingRoutine(ByRef trythis As Boolean)

    Debug.Print "Ran Function"
    testingRoutine = True

End Function

Because trythis is not a global variable, changing it in the function will do nothing in the sub. It will just define trythis as True in the function and stay in that scope. To get trythis to be affected by the function and read by the sub you have to assign it to a variable within the sub.

n8.
  • 1,732
  • 3
  • 16
  • 38
0

i can't remember where i read this, but i know that if you add the bracets () to arguments when calling a sub/function, it is a specific (not an error) way microsoft implented to workaround byref calls (to byval).

example : lets say you call a sub who has ONLY byref, and 2 arguments:

call Sub1 (a), (b)

is a proper way to force Byvalarguments, even if they were initially coded as Byval

Patrick Lepelletier
  • 1,596
  • 2
  • 17
  • 24