NigelH
NigelH

Reputation: 109

Class property as ByRef argument not working

I am using a function to modify a series of strings, passing them ByRef as arguments to the modifying function. The caller's string variables are all modified as expected but the one argument which is a class property does not change, should this be possible? The essentials of the class are:-

Private cRptRef As String

Public Property Get TestRefID() As String
    TestRefID = cRptRef
End Property

Public Property Let TestRefID(Test_Ref As String)
    cRptRef = Test_Ref
End Property

The function for modifying the strings has the following declaration

Public Function GetTestFileNames(ByRef hdrFile As String, _
                                 ByRef calFile As String, _
                                 ByRef dataFile As String, _
                                 ByRef testRef As String _
                                 ) As Boolean

The call to GetTestFilenames is as follows:

If GetTestFileNames(HEADERpath, CALpath, RAWDATApath, _
                    ref) = False Then

All the string arguments are declared as global strings and are empty ("") before the call. After the call they typicaly have content like "d:{path to file{filename.csv}. So all these statements in the function populate the target strings OK.

hdrFile = Replace(userFile, "##", PT_Rpt.Info.FindNode(TEST_REF_HDRsuffix).data, , , vbTextCompare)
dataFile = Replace(userFile, "##", PT_Rpt.Info.FindNode(TEST_REF_DATAsuffix).data, , , vbTextCompare)
calFile = Replace(userFile, "##", PT_Rpt.Info.FindNode(TEST_REF_CALsuffix).data, , , vbTextCompare)

But this statement fails to assign anything to its target string

testRef = Mid(userFile, InStrRev(userFile, Application.PathSeparator) + 1)
testRef = Left(testRef, InStrRev(testRef, "_") - 1)
Debug.Print "Class.TestRefID="; testRef

The Debug.Print statement prints the expected string, but the external reference is not affected. Is this something to do with it being a property? The target string being Class.TestRefID in place of the testRef argument. If I replace the class property in the argument list with a standard string variable, and then assign that to the class property then it I get the expected result, which seems unnecessary work. Is there something I'm missing or is this not possible in VBA?

Upvotes: 1

Views: 1069

Answers (1)

Mathieu Guindon
Mathieu Guindon

Reputation: 71207

A member access expression is an expression that must first be evaluated by VBA before its result can be passed around.

If I have a Class1 module like this:

Option Explicit
Public Foo As String

And then a quick caller procedure:

Sub test()
    With New Class1
        bla .Foo
        Debug.Print .Foo
    End With
End Sub

Sub bla(ByRef bar As String)
    bar = "huh"
End Sub

The test procedure will output an empty string.

The reason for this is because when you pass a member to a ByRef parameter of a procedure in VBA, you're not passing a reference to the member - you're passing a reference to the value held by that member.

So the member access expression is evaluated, evaluates to "", so "" is passed ByRef to the procedure, which assigns it to "huh", but the caller isn't holding a reference to the "" value, so it never gets to see the "huh" string that was assigned.

If I replace the class property in the argument list with a standard string variable, and then assign that to the class property then it I get the expected result, which seems unnecessary work

It's not unnecessary work, it's mandated, otherwise nothing is holding a reference to the result of the member expression.

Then again the real problem is a design issue, pointed out by Warcupine: the function doesn't want to byref-return 4 values, it wants to take a reference to this object, and assign its properties.

Upvotes: 5

Related Questions