2009-04-28 5 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() метод, потому что вы не хотите, чтобы реализовать Всеобъемлющее? –

+0

Это клонирование только определенного объекта. Этот объект является основным объектом данных в нашем приложении. Отвечает ли это на ваш вопрос? –

ответ

6

Вы не сможете получить намного лучше, чем ваш общий двоичный сериализационный процесс без явного внедрения ICloneable на все ваши объекты данных, которые необходимо клонировать. Другой возможный маршрут - это отражение, но вы не будете этому довольны, если будете искать производительность.

Я бы подумал о том, чтобы принять удар с помощью ICloneable для глубокой копии и/или IComparable для сравнения, если объекты разные ... если производительность для вас большая проблема.

1

Возможно, вам не следует делать глубокий клоун?

Других вариантов:

1) Сделайте свой «кэшировать» объект помнит свое первоначальное состояние и сделать это обновления «изменились» флаг каждый раз, когда-либо изменения.

2) Не помните оригинальное состояние и просто объект флага, как грязный, как только что-либо изменилось. Затем перезагрузите объект из исходного источника для сравнения. Бьюсь об заклад, ваши объекты меняются реже, чем не меняются, и даже реже возвращаются к одному и тому же значению.

+1

DirtyFlag? Это тяжелая работа, и вы потеряете автоматические свойства, код, замусоренный с помощью SetIsDirty(), и легко забыть установить флаг (легко создавать ошибки). Он получал бы гораздо больше преимуществ от реализации IComparable с помощью Sorting и т. Д. –

+0

Если вы не используете PostSharp, вам не нужно ничего менять, и вы получаете все преимущества PropertyChanged, и вы можете использовать его для намного больше. – MBoros

1

Возможно, мой ответ может не применяться к вашему делу, потому что я не знаю, каковы ваши ограничения и требования, но мое чувство было бы, что клонирование общего назначения может быть проблематичным. Как вы уже сталкивались, производительность может быть проблемой. Что-то нужно идентифицировать уникальные экземпляры в графе объектов, а затем создать точную копию.Это то, что делает для вас двоичный сериализатор, но он также делает больше (сама сериализация). Я не удивлен, что вижу, что это медленнее, чем вы ожидали. У меня есть аналогичный опыт (кстати, также связанный с кэшированием). Мой подход заключался бы в том, чтобы реализовать клонирование самостоятельно; т. е. реализовать IClonnable для классов, которые на самом деле необходимо клонировать. Сколько классов есть в вашем приложении, которое вы кешируете? Если их слишком много (чтобы вручную закодировать клонирование), было бы ли смысл рассматривать некоторое генерирование кода?

0

Вы можете сделать глубокое клонирование двумя способами: посредством реализации ICloneable (и вызова метода Object.MemberwiseClone) или посредством двоичной сериализации.

Первый способ

Первый (и, вероятно, быстрее, но не всегда лучший) способ заключается в реализации ICloneable интерфейса в каждом типе. Пример ниже иллюстрирует. Класс C реализует ICloneable, и потому, что этот класс ссылается на другие классы D и E, то последние также реализуют этот интерфейс. Внутри метода Clone C мы называем метод 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 

Теперь при вызове метода Clone для экземпляра 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 имеют референс-типы, вы должен реализовать свой метод Clone, как это было сделано для класса C. И так далее.

Предупреждения: 1 -Пример выше действителен до тех пор, пока нет циркулярной ссылки. Например, если класс C имеет самооценку (например, поле типа C), реализовать интерфейс ICloneable будет непросто, так как метод Clone в 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! 
Смежные вопросы