2009-04-28 24 views
3

我们有一个应用程序对数据对象执行比较以确定对象的一个​​版本是否与另一个版本不同。我们的应用程序还对这些对象进行了大量缓存,并且在进行这些比较时遇到了一些性能问题。缓存对象的高效克隆

这里的工作流程:

  1. 数据项1是在内存中当前的项目。这个项目最初是从缓存中检索和深入克隆(所有子对象,如字典等)。数据项1然后被编辑,并且其属性被修改。
  2. 然后,我们将此对象与存储在缓存中的原始版本进行比较。由于数据项1被克隆并且其属性发生改变,这些对象应该不同。

这里有几个问题。

主要问题是我们的深度克隆方法非常昂贵。我们对它进行了简单分析,速度慢了10倍。这是废话。下面是我们的方法来深克隆:

public object Clone()  
    { 
     using (var memStream = new MemoryStream()) 
     { 
      var binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); 
      binaryFormatter.Serialize(memStream, this); 
      memStream.Seek(0, SeekOrigin.Begin); 
      return binaryFormatter.Deserialize(memStream); 
     } 
    } 

我们最初使用下列克隆:

public object Clone() 
{ 
    return this.MemberwiseClone(); 
} 

这是更好的性能,但因为它的浅表副本所有的人的性质的复杂对象这个对象,比如字典等,没有被克隆。该对象仍然包含与缓存中的对象相同的引用,因此在比较时属性是相同的。

那么,有没有人有一个有效的方式来做一个克隆整个对象图的C#对象的深层克隆?

+0

假设你想要的通用clone()方法,因为你不希望实现ICloneable的一切? – 2009-04-28 22:11:00

+0

这是克隆一个特定的对象。这个对象是我们的应用程序中的核心数据对象。这回答了你的问题了吗? – 2009-04-28 22:18:24

回答

6

如果没有在需要克隆的所有数据对象上明确实现ICloneable,您将无法比通用二进制序列化好得多。另一种可能的途径是反思,但是如果你正在寻找表现,你也不会感到满意。

我会考虑采用ICloneable进行深层复制和/或IComparable命中,以便比较对象是否不同......如果性能对您来说是一个大问题。

1

也许你不应该深入克隆呢?

其他选项:

1)让你的“缓存”的对象记住它的原始状态,使更新“改变”标志,每次有什么变化。

2)不记得原来的状态,只是标志对象一旦有变化曾经变脏。然后从原始来源重新加载对象进行比较。我敢打赌,你的物体变化比不变化的频率更少,甚至更少变化回到相同的值。

1

我的回答可能不适用于您的案件,因为我不知道您的限制和要求是什么,但我的感觉是通用目的克隆可能存在问题。正如你已经遇到的那样,性能可能是一个问题。有些东西需要在对象图中标识唯一的实例,然后创建一个精确的副本。这是二进制串行器为你做的,但它也做了更多的事情(序列化本身)。我并不感到惊讶,看到它的速度比预期的要慢。我有类似的经历(顺便也涉及到缓存)。我的做法是自己实施克隆;即为实际需要克隆的类实现IClonnable。您正在缓存的应用程序中有多少类?如果数量太多(要手动编码克隆),考虑一些代码生成是否合理?

0

可以使深克隆有两种方式:通过实施ICloneable(并调用Object.MemberwiseClone方法),或通过二进制序列化。

第一种方式

第一个(可能更快,但并不总是最好的)方法是实现每种类型ICloneable接口。下面的示例说明。 C类实现ICloneable,并且因为这个类引用了其他类D和E,所以后者也实现了这个接口。在C的Clone方法中,我们调用其他类型的Clone方法。

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 

现在,当你调用了一个C的实例的克隆方法,你会得到该实例的深克隆:

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. 

注意:如果班d和E引用类型,你必须像我们对类C所做的那样实施他们的克隆方法。等等。

警告: 1将上述样品是有效的,只要不存在循环引用。例如,如果C类具有自参照(例如,是C型的场),实施ICloneable接口并不容易,因为在C克隆方法可以进入无限循环。

2,另外一个需要注意的事情是,MemberwiseClone方法是Object类的一个受保护的方法。这意味着您只能从类的代码中使用此方法,如上所示。这意味着你不能将它用于外部类。

因此,在实现ICloneable是有效的,只有当两个警告以上不存在。否则,你应该使用二进制序列化技术。

第二种方式

的二进制序列可用于不上面列出的问题深克隆(特别是循环引用)。下面是使用二进制序列进行深克隆的通用方法:

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 

下面是如何使用此方法:

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!