Pergunta

Nós temos um aplicativo que realiza comparações sobre objetos de dados para determinar se uma versão do objeto é diferente do outro. Nosso aplicativo também faz algumas extensa cache desses objetos, e nós funcionamos em um pouco de um problema de desempenho quando se trata de fazer essas comparações.

Aqui está o fluxo de trabalho:

  1. O item de dados 1 é o item atual na memória. Este artigo foi inicialmente recuperado do cache e profunda clonado (todos os sub objetos como dicionários etc). um item de dados é então editada, e as suas propriedades são modificadas.
  2. Estamos em seguida, comparando este objeto contra a versão original que foi armazenada em cache. Desde item de Dados 1 foi clonado e as suas propriedades alteradas, esses objetos devem ser diferentes.

Há um par de questões aqui.

A questão principal é o nosso método de clone profundo é muito caro. Nós perfilado-lo contra um clone rasa e foi 10x mais lento. Isso é porcaria. Aqui é o nosso método de clone profundo:

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

Nós inicialmente usando o seguinte para clone: ??

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

Esta foi mais elevada performance, mas porque faz um clone raso todos os objectos que eram complexos propriedades deste objecto, tal como dicionários etc, não foram clonados. O objecto ainda conter a mesma referência que o objecto que foi na cache, por conseguinte, as propriedades seria a mesma em cima da comparação.

Então, alguém tem uma maneira eficiente de fazer uma profunda clone em C # objetos que cobriria a clonagem de todo o gráfico do objeto?

Foi útil?

Solução

Você não vai ser capaz de obter muito melhor do que o seu genérico Binário serialização sem explicitamente implementar ICloneable em todos os seus objetos de dados que precisam ser clonado. Outro caminho possível é reflexão, mas você não vai ser feliz com ele tanto se você está à procura de desempenho.

Eu consideraria tomar o hit com ICloneable para cópia em profundidade e / ou IComparable para comparar se os objetos são diferentes ... se o desempenho é tão grande de um problema para você.

Outras dicas

Talvez você não deve profunda clone então?

Outras opções:

1) Faça o seu objeto "em cache" lembrar seu estado original e make it update "mudou" bandeira cada vez que algo muda.

2) Não me lembro estado original e apenas objeto marcar como sujo uma vez nada mudou nunca. Em seguida, recarregue objeto da fonte original para comparar. Aposto que seus objetos mudam com menos frequência do que não mudam, e até mesmo com menos frequência mudar de volta para o mesmo valor.

É possível que a minha resposta pode não se aplicar ao seu caso, porque eu não sei o que suas restrições e requisitos são, mas meu sentimento seria que um propósito clonagem geral pode ser problemático. Como você já encontrou, o desempenho pode ser um problema. Algo precisa identificar instâncias exclusivas no gráfico de objeto e, em seguida, criar uma cópia exata. Isto é o que o serializador binário faz para você, mas também faz mais (o próprio serialização). Não me surpreende que, para ver que é mais lento do que o esperado. Tenho experiência semelhante (aliás, também relacionado a um cache). Minha abordagem seria implementar clonagem por mim mesmo; isto é implementar IClonnable para as classes que realmente necessitam de ser clonado. Quantas aulas estão lá na sua aplicação que você está cache? Se há muitos (para código manualmente a clonagem), faria sentido considerar alguma geração de código?

Você pode fazer profunda clonagem de duas maneiras:. Através da implementação ICloneable (e chamar o método Object.MemberwiseClone), ou através de serialização binária

Primeiro Way

O primeiro (e provavelmente mais rápido, mas nem sempre o melhor) maneira é implementar a interface ICloneable em cada tipo. O exemplo abaixo ilustra. Classe C implementa ICloneable, e porque esta classe outras referências classes D e E, em seguida, os dois Estados também implementar esta interface. Dentro do método Clone de C, chamamos o método Clone dos outros tipos.

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

Agora, quando você chamar o método Clone para uma instância de C, você tem uma profunda clonagem dessa instância:

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 as classes D e E têm-referência tipos, você deve implementar seu método Clone como fizemos para a classe C. E assim por diante

Advertências: 1-O exemplo acima é válido desde que não há nenhuma referência circular. Por exemplo, se a classe C tem um auto-referência (por exemplo, um campo que é do tipo C), implementando a interface ICloneable não seria fácil, como o método Clone em C podem entrar num ciclo sem fim.

2-Outra coisa a notar é que o método MemberwiseClone é um método protegido da classe Object. Isto significa que você pode usar esse método somente de dentro do código da classe, como mostrado acima. Isto significa que você não pode usá-lo para classes externas.

Portanto, a implementação ICloneable só é válida quando acima não existem as duas advertências. Caso contrário, você deve usar a técnica de serialização binária.

Segunda Way

O binário de serialização pode ser usado para clonagem profunda sem os problemas indicados acima (especialmente a referência circular). Aqui está um método genérico que executa deep-clonagem usando serialização binária:

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

Veja como usar esse método:

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!
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top