Domanda

Abbiamo un'applicazione che esegue confronti su oggetti dati per determinare se una versione dell'oggetto è diversa da un'altra. La nostra applicazione esegue anche una notevole memorizzazione nella cache di questi oggetti e abbiamo riscontrato un problema di prestazioni quando si tratta di fare questi confronti.

Ecco il flusso di lavoro:

  1. L'elemento dati 1 è l'elemento corrente in memoria. Questo oggetto è stato inizialmente recuperato dalla cache e clonato in profondità (tutti gli oggetti secondari come i dizionari ecc.). L'elemento dati 1 viene quindi modificato e le sue proprietà vengono modificate.
  2. Stiamo quindi confrontando questo oggetto con la versione originale che è stata memorizzata nella cache. Poiché l'elemento dati 1 è stato clonato e le sue proprietà modificate, questi oggetti dovrebbero essere diversi.

Ci sono un paio di problemi qui.

Il problema principale è che il nostro metodo di clonazione profonda è molto costoso. Lo abbiamo profilato su un clone superficiale ed era 10 volte più lento. Questa è merda. Ecco il nostro metodo per clonare in profondità:

    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);
        }
    }

Inizialmente stavamo usando il seguente per clonare:

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

Questo è stato più performante, ma poiché fa un clone superficiale tutti gli oggetti complessi che erano proprietà di questo oggetto, come i dizionari ecc., non sono stati clonati. L'oggetto conterrebbe comunque lo stesso riferimento dell'oggetto che si trovava nella cache, pertanto le proprietà sarebbero le stesse al confronto.

Quindi, qualcuno ha un modo efficace di fare un clone profondo su oggetti C # che coprirebbe la clonazione dell'intero grafico oggetto?

È stato utile?

Soluzione

Non sarai in grado di ottenere molto meglio della tua serializzazione binaria generica senza implementare esplicitamente ICloneable su tutti i tuoi oggetti dati che devono essere clonati. Un altro possibile percorso è la riflessione, ma non ne sarai contento nemmeno se stai cercando prestazioni.

Vorrei prendere in considerazione il successo con ICloneable per deep copy e / o IComparable per confrontare se gli oggetti sono diversi ... se le prestazioni sono un grosso problema per te.

Altri suggerimenti

Forse non dovresti clonare in profondità allora?

Altre opzioni:

1) Crea il tuo " memorizzato nella cache " l'oggetto ricorda il suo stato originale e fa esso aggiorna " modificato " flag ogni volta che cambia qualcosa.

2) Non ricordare lo stato originale e contrassegna l'oggetto come sporco una volta che qualcosa è cambiato. Quindi ricaricare l'oggetto dalla fonte originale per il confronto. Scommetto che i tuoi oggetti cambiano meno frequentemente di quanto non cambino, e ancora meno frequentemente tornano allo stesso valore.

È possibile che la mia risposta non si applichi al tuo caso perché non so quali siano le tue restrizioni e i tuoi requisiti, ma ho la sensazione che una clonazione per scopi generali possa essere problematica. Come hai già riscontrato, le prestazioni potrebbero essere un problema. Qualcosa deve identificare istanze univoche nel grafico degli oggetti e quindi creare una copia esatta. Questo è ciò che il serializzatore binario fa per te, ma fa anche di più (la serializzazione stessa). Non mi sorprende che vedere che è più lento di quanto ti aspettassi. Ho un'esperienza simile (per inciso anche legata alla memorizzazione nella cache). Il mio approccio sarebbe quello di implementare la clonazione da solo; cioè implementare IClonnable per le classi che devono effettivamente essere clonate. Quante classi ci sono nella tua applicazione che stai memorizzando nella cache? Se ce ne sono troppi (per codificare manualmente la clonazione), avrebbe senso prendere in considerazione la generazione di codice?

Puoi eseguire la clonazione profonda in due modi: implementando ICloneable (e chiamando il metodo Object.MemberwiseClone) o serializzazione binaria.

First Way

Il primo (e probabilmente il più veloce, ma non sempre il migliore) metodo è implementare l'interfaccia ICloneable in ogni tipo. Il seguente esempio illustra. La classe C implementa ICloneable e poiché questa classe fa riferimento ad altre classi D ed E, anche queste ultime implementano questa interfaccia. All'interno del metodo Clone di C, chiamiamo il metodo Clone degli altri tipi.

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

Ora quando chiami il metodo Clone per un'istanza di C, ottieni una clonazione profonda di quell'istanza:

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.

Nota: se le classi D ed E hanno tipi di riferimento, è necessario implementare il loro metodo Clone come abbiamo fatto per la classe C. E così via.

Avvertenze: 1-Il campione sopra è valido fino a quando non vi è alcun riferimento circolare. Ad esempio, se la classe C ha un auto-riferimento (ad es. Un campo di tipo C), implementare l'interfaccia ICloneable non sarebbe facile, poiché il metodo Clone in C può entrare in un ciclo infinito.

2-Un'altra cosa da notare è che il metodo MemberwiseClone è un metodo protetto della classe Object. Ciò significa che è possibile utilizzare questo metodo solo all'interno del codice della classe, come mostrato sopra. Questo significa che non puoi usarlo per classi esterne.

Pertanto, l'implementazione di ICloneable è valida solo quando non esistono i due avvertimenti precedenti. Altrimenti, è necessario utilizzare la tecnica di serializzazione binaria.

Seconda via

La serializzazione binaria può essere utilizzata per la clonazione profonda senza i problemi sopra elencati (in particolare il riferimento circolare). Ecco un metodo generico che esegue la clonazione profonda usando la serializzazione binaria:

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

Ecco come utilizzare questo metodo:

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!
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top