ByRef behaviour

I know if an object instance is passed as an argument to a method, this is always done ByRef.
However, I have a simple example showing that specifying ByRef or not, the behaviour change.

I have a class named MyClass, with only a destructor and a property:

[code]Public Sub Destructor()
break
End Sub

Public Property value as Integer[/code]

now I have a method (let’s say a Window method):

Private Sub TestMethod(c as MyClass) c = new MyClass c.value = 2 End Function

finally in whatever place (let’s say the Window.Open event) I have the following code:

[code]dim c as new MyClass
c.value = 1

TestMethod©

if c isa MyClass and c.value = 1 then
break
end if
[/code]

Running the code I’d expect when in TestMethod the object is inited again, the MyClass.Destructor is called and when it goes back to the calling code, the instance contained in c has value of 2.

But this is not what’s happening.

Instead the c.Destructor is called when the method ends and back to the calling code, I have back my untouched instance with value = 1. Like if the argument was passed by value.

Now, if I declare the TestMethod explicitly telling is ByRef with Private Sub TestMethod(byRef c as MyClass) then everything starts working as expected. But as the LR says all objects are passed byRef by default, so it should not make a difference:

Xojo LR:
All arrays and objects are passed by reference, not copied, so changes to that array or object will be reflected in the calling method. This is no different than assigning an array or object to a second variable or property.

Can anyone explain why is this weird behaviour?

This may help:

https://blog.xojo.com/2018/12/12/what-kind-of-variable-are-you/

[quote=421423:@Jason Parsley]This may help:

https://blog.xojo.com/2018/12/12/what-kind-of-variable-are-you/[/quote]

The only interesting (and ambiguous) thing on that article is this sentence:

“Note: This is not the same as “ByRef” in a method parameter declaration although it’s related.”

but the fact remains: the LR talks explicitly about passing arguments and there is not an explanation on why this happens.

The wording around this is difficult, and the problem is in your first paragraph:

This is not true, but only because “passed by reference” is not the same as ByRef. In fact, all parameters are passed ByVal unless ByRef is specified.

With a scalar (double, integer, etc.), it’s easy because they are immutable anyway. But with an object (or array), it’s a harder concept because they can be changed.

Look at is this way: when you pass an object, you are actually passing the address of the object into a new variable. Any changes to the object are done in place, but if you replace it with “new”, you are assigning a new object to that variable, leaving the variable that holds the original object untouched.

When you use ByRef, you are passing the address of the variable that holds the object, so the parameter becomes a stand-in for the original variable in the caller. This means that changes to the object can be made in place, but also the object can be replaced in the caller too.

dim myVar as new MyClass
Foo( myVar )

// If
Sub Foo (v As MyClass)
  // v holds the same address as myVar

// Else
Sub Foo (ByRef v As MyClass)
  // v holds the address of myVar, which currently points to some MyClass

HTH.

My guess is this:

1.Private Sub TestMethod(c as MyClass)
A copy of the object’s reference is passed. If you redefine ‘c’ via New it will just create a new local object. Setting ‘c’ to Nil will just clear the copy of the reference and not Nil the object itself.

2.Private Sub TestMethod(byRef c as MyClass)
The object reference is passed which means that you can redefine the object within the method.

This is the key answer. Everything else was understandable for me, but I can’t figure out why the different behaviour.
I guess the LR should be a bit more clear about this fact, to avoid such misunderstanding.

Thanks Kem for your help to understand.