Efficient cloning of cached objects

元气小坏坏 提交于 2019-12-06 03:12:07

You're not going to be able to get much better than your Generic Binary Serialization without Explicitly implementing ICloneable on all your data objects that need to be cloned. Another possible route is reflection, but you won't be happy with it either if you are searching for performance.

I would consider taking the hit with ICloneable for deep copy and/or IComparable for comparing if the objects are different ... if the performance is that big of an issue for you.

Maybe you should not deep clone then?

Other options:

1) Make your "cached" object remember its original state and make it update "changed" flag every time anything changes.

2) Do not remember original state and just flag object as dirty once anything has changed ever. Then reload object from the original source to compare. I bet your objects change less frequently than don't change, and even less frequently change back to the same value.

It is possible that my response may not apply to your case because I don’t know what your restrictions and requirement are, but my feeling would that a general purpose cloning may be problematic. As you have already encountered, performance may be an issue. Something needs to identify unique instances in the object graph and then create an exact copy. This is what the binary serializer does for you, but it also does more (the serialization itself). I am not surprised that to see that it is slower that you expected. I have similar experience (incidentally also related to caching). My approach would be to implement cloning by myself; i.e. implement IClonnable for classes which actually need to be cloned. How many classes are there in your application which you are caching? If there are too many (to manually code the cloning), would it make sense to consider some code generation?

You can make deep cloning in two ways: Through implementing ICloneable (and calling the Object.MemberwiseClone method), or through binary serialization.

First Way

The first (and probably faster, but not always the best) way is to implement the ICloneable interface in each type. The sample below illustrates. Class C implements ICloneable, and because this class references other classes D and E, then the latters also implement this interface. Inside the Clone method of C, we call the Clone method of the other types.

Public Class C
Implements ICloneable

    Dim a As Integer
    ' Reference-type fields:
    Dim d As D
    Dim e As E

    Private Function Clone() As Object Implements System.ICloneable.Clone
        ' Shallow copy:
        Dim copy As C = CType(Me.MemberwiseClone, C)
        ' Deep copy: Copy the reference types of this object:
        If copy.d IsNot Nothing Then copy.d = CType(d.Clone, D)
        If copy.e IsNot Nothing Then copy.e = CType(e.Clone, E)
        Return copy
    End Function
End Class

Public Class D
Implements ICloneable

    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return Me.MemberwiseClone()
    End Function
End Class

Public Class E
Implements ICloneable

    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return Me.MemberwiseClone()
    End Function
End Class

Now when you call the Clone method for an instance of C, you get a deep-cloning of that instance:

Dim c1 As New C
Dim c2 As C = CType(c1.Clone, C)   ' Deep cloning.  c1 and c2 point to two different 
                                   ' locations in memory, while their values are the 
                                   ' same at the moment.  Changing a value of one of
                                   ' these objects will NOT affect the other.

Note: If classes D and E have reference-types, you must implement their Clone method as we did for class C. And so on.

Warnings: 1-The sample above is valid as long as there is no circular reference. For instance, if class C has a self-reference (eg, a field that is of type C), implementing the ICloneable interface would not be easy, as the Clone method in C may enter an endless loop.

2-Another thing to note is that the MemberwiseClone method is a Protected method of class Object. This means you can use this method only from within the code of the class, as shown above. This means you cannot use it for external classes.

Therefore, implementing ICloneable is valid only when the two warnings above do not exist. Otherwise, you should use the binary serialization technique.

Second Way

The binary serialization can be used for deep cloning without the problems listed above (especially the circular reference). Here's a generic method that performs deep-cloning using binary serialization:

Public Class Cloning
    Public Shared Function DeepClone(Of T)(ByVal obj As T) As T
        Using MStrm As New MemoryStream(100)    ' Create a memory stream.
            ' Create a binary formatter:
            Dim BF As New BinaryFormatter(Nothing, New StreamingContext(StreamingContextStates.Clone))

            BF.Serialize(MStrm, obj)    ' Serialize the object into MStrm.
            ' Seek the beginning of the stream, and then deserialize MStrm:
            MStrm.Seek(0, SeekOrigin.Begin)
            Return CType(BF.Deserialize(MStrm), T)
        End Using
    End Function
End Class

Here's how to use this method:

Dim c1 As New C
Dim c2 As C = Cloning.DeepClone(Of C)(c1)   ' Deep cloning of c1 into c2.  No need to 
                                            ' worry about circular references!
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!