문제

우리는 데이터 객체를 비교하여 한 버전의 객체가 다른 버전과 다른지 확인하는 응용 프로그램이 있습니다. 우리의 응용 프로그램은 또한 이러한 객체의 광범위한 캐싱을 수행하며 이러한 비교를 수행 할 때 약간의 성능 문제가 발생했습니다.

워크 플로는 다음과 같습니다.

  1. 데이터 항목 1은 메모리의 현재 항목입니다. 이 항목은 처음에 캐시와 딥 클로닝 (사전과 같은 모든 하위 객체)에서 검색되었습니다. 그런 다음 데이터 항목 1을 편집하고 그 속성이 수정됩니다.
  2. 그런 다음이 객체를 캐시에 저장된 원래 버전과 비교합니다. 데이터 항목 1이 복제되고 특성이 변경되었으므로 이러한 객체는 달라야합니다.

여기에는 몇 가지 문제가 있습니다.

주요 문제는 우리의 깊은 클론 방법이 매우 비싸다는 것입니다. 우리는 얕은 클론에 대해 그것을 프로파일 링했고 10 배 느 렸습니다. 그것은 쓰레기입니다. 다음은 Deep Clone의 방법입니다.

    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# 객체에서 깊은 클론을 수행하는 효율적인 방법이 있습니까?

도움이 되었습니까?

해결책

클로닝 해야하는 모든 데이터 객체에서 iCloneable을 명시 적으로 구현하지 않고 일반 이진 직렬화보다 훨씬 나아질 수 없습니다. 또 다른 가능한 경로는 반사이지만 성능을 검색하는 경우에도 행복하지 않을 것입니다.

나는 딥 카피에 대한 iclonable로 인기를 얻는 것을 고려하고/또는 객체가 다른지 비교하는 데 비교할 수있는 IComparten을 고려할 것입니다 ... 성능이 큰 문제라면 큰 문제가 있습니다.

다른 팁

어쩌면 당신은 깊은 복제하지 말아야합니까?

다른 옵션:

1) "캐시 된"객체를 원래 상태를 기억하고 그것 모든 것이 변경 될 때마다 "변경"플래그를 업데이트하십시오.

2) 원래 상태를 기억하지 못하고 단지 무엇이든 변경되면 더러운 객체를 더럽게 깃발로 표시하십시오. 그런 다음 원래 소스에서 객체를 다시로드하여 비교하십시오. 나는 당신의 객체가 변경되지 않는 것보다 덜 자주 변경되며, 동일한 값으로 덜 자주 변경됩니다.

귀하의 제한과 요구 사항이 무엇인지 모르기 때문에 내 응답이 귀하의 경우에 적용되지 않을 수 있지만, 범용 복제가 문제가 될 수 있다는 내 느낌이드립니다. 이미 겪었으므로 성능이 문제가 될 수 있습니다. 객체 그래프에서 고유 한 인스턴스를 식별 한 다음 정확한 사본을 작성해야합니다. 이것이 바이너리 세리어 라이저가 당신을 위해하는 일이지만 더 많은 일을합니다 (직렬화 자체). 나는 그것이 당신이 기대했던 것이 느리다는 것을 알게된다는 사실에 놀라지 않습니다. 비슷한 경험이 있습니다 (우연히 캐싱과 관련이 있습니다). 저의 접근 방식은 혼자 클로닝을 구현하는 것입니다. 즉, 실제로 복제 해야하는 클래스에 ICLonnable을 구현합니다. 캐싱중인 응용 프로그램에 몇 개의 수업이 있습니까? 너무 많으면 (복제를 수동으로 코딩하기 위해) 일부 코드 생성을 고려하는 것이 합리적입니까?

두 가지 방법으로 깊은 복제를 할 수 있습니다. ICLoneable 구현 (및 Object.MemberWiseclone 방법을 호출) 또는 이진 직렬화를 통해 두 가지 방법으로 클로닝 할 수 있습니다.

첫 번째 방법

첫 번째 (그리고 아마도 가장 빠르지 만 항상 가장 적합한 것은 아님)는 각 유형에서 iClonable 인터페이스를 구현하는 것입니다. 아래 샘플은 설명합니다. 클래스 C는 icloneable을 구현 하고이 클래스는 다른 클래스 D와 E를 참조하기 때문에 후자는이 인터페이스도 구현합니다. C의 복제 방법 내부에서는 다른 유형의 클론 방법을 호출합니다.

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에 기준 유형이있는 경우 Class C 등과 같이 복제 방법을 구현해야합니다.

경고 : 1- 위의 샘플은 원형 기준이없는 한 유효합니다. 예를 들어, 클래스 C에 자체 참조 (예 : 유형 C 인 필드)가있는 경우 C의 클론 메소드가 끝없는 루프로 들어갈 수 있으므로 ICLoneable 인터페이스를 구현하는 것은 쉽지 않습니다.

2- 주목할만한 것은 Memberwiseclone 방법이 클래스 객체의 보호 된 방법이라는 점입니다. 이것은 위에 표시된 것처럼 클래스 코드 내에서만이 메소드를 사용할 수 있음을 의미합니다. 이것은 당신이 외부 클래스에 사용할 수 없다는 것을 의미합니다.

따라서 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!
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top