The easiest way to clone an object (deep copy) in .NET is to use the serialization functions available:
Public Shared Function CloneObject(ByVal obj As Object) As Object
If Not obj Is Nothing Then
Dim bf = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim fs = New System.IO.MemoryStream()
bf.Serialize(fs, obj)
fs.Seek(0, System.IO.SeekOrigin.Begin)
Dim copy = CType(bf.Deserialize(fs), Object)
fs.Close()
Return copy
Else
Return Nothing
End If
End Function
Though the performance is not very good, for occasional operations it will do the job perfectly. However, I was confronted to the following problem: what if there are events inside the class, to which other objects have subscribed? I found several methods (and functions :-)) on various places over the Internet; they basically were:
- Implement ISerializable yourself (meaning you have to update it each time you modify the class);
- Disconnect from events (retrieved using Reflection), serialize the object, and then reconnect the events (I could not make this working properly);
- Implement a serialization surrogate;
- Implement your events in a separate class that is not serialized;
- Implement your events in a C# base class.
Plenty of potential solutions, but none of them was good enough for me. So I played around with Reflection and found something that nobody else might have done so far. For a cloning interface that does just a shallow copy, like what MemberwiseClone does, but without event, I wrote this:
Public Function Clone() As Object Implements System.ICloneable.Clone
Dim cl = New MyClassName(Me)
'Here we don't capture events, only normal fields, including non public ones (private, protected...)
Dim FldInfos() As Reflection.FieldInfo = Me.GetType.GetFields(Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.NonPublic)
For Each FldInfo As Reflection.FieldInfo In FldInfos
FldInfo.SetValue(cl, FldInfo.GetValue(Me)) 'For serialization purpose we just need not to have events, so no need to perform a deep copy of the fields.
Next
Return cl
End Function
Now if one of your class member is an object with events (or if you want to perform a deep copy), you should call its clone method (to be implemented the same way) when performing the FldInfo.SetValue, like this:
For Each FldInfo As Reflection.FieldInfo In FldInfos
If FldInfo.Name <> "MyObjectWithEvents" Then
FldInfo.SetValue(cl, FldInfo.GetValue(Me)) 'It is not really necessary to clone a possible reference class member here for serialization purpose, we just need not to have events in the clone
Else
FldInfo.SetValue(cl, Me.MyObjectWithEvents.Clone())
End If
Next
If you have an object that is for example a dictionary of objects with events, you can call this:
For Each FldInfo As Reflection.FieldInfo In FldInfos
If FldInfo.Name <> "MyObjectsWithEventsDictionary" Then
FldInfo.SetValue(cl, MyLib.CloneObject(FldInfo.GetValue(Me)))
Else
FldInfo.SetValue(cl, Me.MyObjectsWithEventsDictionary.ToDictionary(Of String, MyObjectWithEvent)(Function(entry) entry.Key, Function(entry) CType(entry.Value.Clone(), MyObjectWithEvent)))
End If
Next
Finally, if you intend to use the Clone interface to serialize objects, you should make sure you don’t include class members marked as NonSerialized():
For Each FldInfo As Reflection.FieldInfo In FldInfos
If Not FldInfo.IsNotSerialized Then
FldInfo.SetValue(cl, FldInfo.GetValue(Me))
End If
Next
I hope this will give you an insight to build something more tailored to your needs. There are other optimizations I can already think of, such as implementing a recursive Clone function where you would just put your original object and a virgin instance of it as a reference, and get a perfect serializable deep copy, whatever the class members and sub class members are! This could become a universal Clone method…