copy by reference VS copy by value

  • Thread starter Thread starter Martin Ortiz
  • Start date Start date
M

Martin Ortiz

Ugh....

All classes are copy by reference, even if you use "ByVal" and NOT "ByRef"
it's still a copy by reference.
Of course, as a consequence, if you change any values of the object you
passed in, the original object is affected.
I expect this with arrays, but not with single objects being passed "ByVal"
to functions....

Is there a sane to get "ByVal" passing of parameters? (copying fields is not
so hot an idea, since they too may be class objects)
 
Ugh....

All classes are copy by reference, even if you use "ByVal" and NOT "ByRef"
it's still a copy by reference.
Of course, as a consequence, if you change any values of the object you
passed in, the original object is affected.
I expect this with arrays, but not with single objects being passed "ByVal"
to functions....

Is there a sane to get "ByVal" passing of parameters? (copying fields is not
so hot an idea, since they too may be class objects)

This is the same bahavior that existed in VB.CLASSIC as well. Objects
variables are not objects, but references to memory located on the heap.
When you pass an object ByVal, your passing it's address by value not
the object. That means that the callee can not change the reference,
but the certainly can change the values of it's properties.

Anyway, here is a little demo of a possible solution for you:

Option Strict On
Option Explicit On

Imports System.IO
Imports System.Collections
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary

Module Module1

Sub Main()
Dim cm1 As New CloneMe

cm1.I = 5
cm1.S = "Bob"
cm1.List.Add(New Child(1))
cm1.List.Add(New Child(200))

Console.WriteLine("cm1.I = {0}", cm1.I)
Console.WriteLine("cm1.S = {0}", cm1.S)

Console.WriteLine("Children In List:")
For Each c As Child In cm1.List
Console.WriteLine("{0}{1}", vbTab, c.X)
Next

Console.WriteLine("cm1.Child.X = {0}", cm1.Child.X)

Console.WriteLine("============== Creating Clone =============")
Dim cm2 As CloneMe = DirectCast(cm1.Clone, CloneMe)

Console.WriteLine("cm2.I = {0}", cm2.I)
Console.WriteLine("cm2.S = {0}", cm2.S)

Console.WriteLine("Children In List:")
For Each c As Child In cm2.List
Console.WriteLine("{0}{1}", vbTab, c.X)
Next

Console.WriteLine("cm2.Child.X = {0}", cm2.Child.X)

Console.WriteLine("================= Prove that it's a clone
============")
cm2.I = 40
cm2.S = "Susan"
cm2.List.Clear()
cm2.List.Add(New Child(1000))
cm2.Child = New Child(25)

Console.WriteLine("cm1.I = {0}", cm1.I)
Console.WriteLine("cm1.S = {0}", cm1.S)

Console.WriteLine("Children In List:")
For Each c As Child In cm1.List
Console.WriteLine("{0}{1}", vbTab, c.X)
Next

Console.WriteLine("cm1.Child.X = {0}", cm1.Child.X)

Console.WriteLine()


Console.WriteLine("cm2.I = {0}", cm2.I)
Console.WriteLine("cm2.S = {0}", cm2.S)

Console.WriteLine("Children In List:")
For Each c As Child In cm2.List
Console.WriteLine("{0}{1}", vbTab, c.X)
Next

Console.WriteLine("cm2.Child.X = {0}", cm2.Child.X)
End Sub

<Serializable()> _
Private Class CloneMe
Implements ICloneable

Private m_i As Integer
Private m_s As String
Private m_a As ArrayList
Private m_c As Child

Public Sub New()
Me.m_a = New ArrayList
Me.m_c = New Child(5)
End Sub

Public Property I() As Integer
Get
Return Me.m_i
End Get
Set(ByVal Value As Integer)
Me.m_i = Value
End Set
End Property

Public Property S() As String
Get
Return Me.m_s
End Get
Set(ByVal Value As String)
Me.m_s = Value
End Set
End Property

Public ReadOnly Property List() As ArrayList
Get
Return Me.m_a
End Get
End Property

Public Property Child() As Child
Get
Return Me.m_c
End Get
Set(ByVal Value As Child)
Me.m_c = Value
End Set
End Property

Public Function Clone() As Object Implements
System.ICloneable.Clone
Dim bf As New BinaryFormatter
Dim ms As New MemoryStream
Dim nc As CloneMe

bf.Serialize(ms, Me)
ms.Seek(0, SeekOrigin.Begin)
nc = DirectCast(bf.Deserialize(ms), CloneMe)
ms.Close()

Return nc
End Function
End Class

<Serializable()> _
Private Class Child
Private m_x As Integer

Public Sub New(ByVal x As Integer)
Me.m_x = x
End Sub

Public ReadOnly Property X() As Integer
Get
Return Me.m_x
End Get
End Property
End Class
End Module

Now, this is pretty sloppy since I threw it together - but it does what
you want. It creates a deep copy of your object. The gotchas are that
all members have to be serializable or you end up just implmenting the
ISerializable interfact manually. And that leads to the memeber by
member copy.

But you can use this method to create a temporary object to be passed to
routines you don't want to modify your object.
 
Hi Martin,

Tom's shown you how to do the deep copy which will copy objects referred
to by the object being passed. If you <know> that there aren't any, or that
these members won't be touched, you create a copy using MemberwiseClose and
pass that instead.

Foo (oMan.MemberwiseClone)
'Needs casting if Option Strict is on.

Another way would be to implement the class with a flag which sets it
read-only.

Lol. There are more ideas but they're getting wackier. ;-)

Regards,
Fergus
 
Back
Top