2009-04-28 10 views
3

データオブジェクトに対する比較を実行して、オブジェクトの1つのバージョンが別のバージョンであるかどうかを判断するアプリケーションがあります。私たちのアプリケーションは、これらのオブジェクトの広範なキャッシングも行います。これらの比較を行う際には、パフォーマンス上の問題が少しあります。キャッシュされたオブジェクトの効率的なクローニング

はここで、ワークフローの:

  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

すべてのICloneable? –

+0

これは特定のオブジェクトだけを複製しています。このオブジェクトは、アプリの中核となるデータオブジェクトです。これはあなたの質問に答えますか? –

答えて

6

複製が必要なすべてのデータオブジェクトに明示的にICloneableを実装せずに、Genericバイナリシリアル化よりも優れたパフォーマンスを得ることはできません。もう1つの可能なルートは反射ですが、パフォーマンスを探している場合でも、それに満足できません。

ディープコピーの場合はICloneableでヒットし、オブジェクトが異なるかどうかを比較する場合はIComparableを使用することを検討します。パフォーマンスがそれほど大きな問題であれば。

1

あなたは深くクローンしてはいけないのでしょうか?

その他のオプション:

1)あなたのオブジェクトは元の状態を覚えていて、を作る「キャッシュされた」作り、それ更新するたびに何の変更フラグを「変更」。

2)元の状態を覚えておらず、何かが変更されたらオブジェクトを汚れたものにするだけです。オブジェクトを元のソースからリロードして比較します。あなたのオブジェクトは、変更しないよりも頻繁に変更され、同じ頻度で変更する頻度は少なくなります。

+1

DirtyFlag?これは大変な作業で、自動プロパティ、SetIsDirty()で囲まれたコード、フラグの設定を忘れがちなバグ(バグの作成が容易)などが失われます。彼はソートなどでIComparableを実装することにより多くの利益を得ることができます。 –

+0

PostSharpを使用しない限り、何も変更する必要はなく、PropertyChangedのすべての利点を得ることができます。はるかに。 – MBoros

1

あなたの制限と要件がわからないため、私の応答があなたのケースに当てはまらない可能性がありますが、一般的な目的のクローニングが問題になるかもしれません。すでに遭遇しているように、パフォーマンスが問題になる可能性があります。オブジェクトグラフ内の一意のインスタンスを特定してから、正確なコピーを作成する必要があります。これは、バイナリシリアライザがあなたのために行うものですが、より多くのこと(シリアライゼーション自体)も行います。私はそれがあなたが期待したより遅いことを見て驚くことはありません。私は同様の経験を持っています(それに付随してキャッシングにも関係します)。私のアプローチは、自分でクローンを実装することです。実際にクローンを作成する必要があるクラスに対してはIClonnableを実装します。キャッシュしているアプリケーションにいくつのクラスがありますか?手動でクローニングをコードするには多すぎる場合、コード生成を検討するのは理にかなっていますか?

0

深いクローン作成は、ICloneableの実装(およびObject.MemberwiseCloneメソッドの呼び出し)またはバイナリシリアル化の2つの方法で行うことができます。

最初の方法

最初の(そしておそらくより速く、常にではないが最善)の方法は、各タイプで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のインスタンスのCloneメソッドを呼び出すときに今、あなたはそのインスタンスのディープクローンを得る:

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のように、Cloneメソッドを実装する必要があります。

警告: 1-上記のサンプルは、循環参照がない限り有効です。例えば、クラスCが自己参照(例えば、タイプCのフィールド)を持つ場合、CのCloneメソッドは無限ループに入る可能性があるため、ICloneableインターフェイスの実装は容易ではありません。

2もう1つ注目すべきことは、MemberwiseCloneメソッドがObjectクラスのProtectedメソッドであることです。これは、上記のように、クラスのコード内からのみこのメソッドを使用できることを意味します。つまり、外部クラスには使用できません。

したがって、ICloneableの実装は、上記の2つの警告が存在しない場合にのみ有効です。それ以外の場合は、バイナリシリアル化手法を使用する必要があります。

第二の方法

バイナリシリアライゼーション(特に循環参照)上記問題なく深いクローニングのために使用することができます。ここでは、バイナリシリアル化を使用して、深いクローニングを行い、一般的な方法があります:

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! 
関連する問題