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.