2009-04-28 21 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!