MemberwiseClone не является хорошим выбором для создания глубокой копии ( MSDN ):
Метод MemberwiseClone создает поверхностную копию, создавая новый объект, а затем копируянестатические поля текущего объекта для нового объекта.Если поле является типом значения, выполняется побитовая копия поля. Если поле является ссылочным типом, ссылка копируется, но указанный объект не является ;следовательно, исходный объект и его клон ссылаются на один и тот же объект.
Это означает, что если клонированный объект имеет открытые поля или свойства ссылочного типа, они будут ссылаться на ту же область памяти, что и поля / свойства исходного объектаТаким образом, каждое изменение в клонированном объекте будет отражено в исходном объекте.Это не настоящая глубокая копия.
Вы можете использовать BinarySerialization для создания полностью независимого экземпляра объекта, см. Страницу MSDN BinaryFormatter класса для примера сериализации.
Пример и тестовый комплект:
Метод расширения для создания глубокой копии данного объекта:
public static class MemoryUtils
{
/// <summary>
/// Creates a deep copy of a given object instance
/// </summary>
/// <typeparam name="TObject">Type of a given object</typeparam>
/// <param name="instance">Object to be cloned</param>
/// <param name="throwInCaseOfError">
/// A value which indicating whether exception should be thrown in case of
/// error whils clonin</param>
/// <returns>Returns a deep copy of a given object</returns>
/// <remarks>Uses BInarySerialization to create a true deep copy</remarks>
public static TObject DeepCopy<TObject>(this TObject instance, bool throwInCaseOfError)
where TObject : class
{
if (instance == null)
{
throw new ArgumentNullException("instance");
}
TObject clonedInstance = default(TObject);
try
{
using (var stream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, instance);
// reset position to the beginning of the stream so
// deserialize would be able to deserialize an object instance
stream.Position = 0;
clonedInstance = (TObject)binaryFormatter.Deserialize(stream);
}
}
catch (Exception exception)
{
string errorMessage = String.Format(CultureInfo.CurrentCulture,
"Exception Type: {0}, Message: {1}{2}",
exception.GetType(),
exception.Message,
exception.InnerException == null ? String.Empty :
String.Format(CultureInfo.CurrentCulture,
" InnerException Type: {0}, Message: {1}",
exception.InnerException.GetType(),
exception.InnerException.Message));
Debug.WriteLine(errorMessage);
if (throwInCaseOfError)
{
throw;
}
}
return clonedInstance;
}
}
NUnit тесты:
public class MemoryUtilsFixture
{
[Test]
public void DeepCopyThrowWhenCopyInstanceOfNonSerializableType()
{
var nonSerializableInstance = new CustomNonSerializableType();
Assert.Throws<SerializationException>(() => nonSerializableInstance.DeepCopy(true));
}
[Test]
public void DeepCopyThrowWhenPassedInNull()
{
object instance = null;
Assert.Throws<ArgumentNullException>(() => instance.DeepCopy(true));
}
[Test]
public void DeepCopyThrowWhenCopyInstanceOfNonSerializableTypeAndErrorsDisabled()
{
var nonSerializableInstance = new CustomNonSerializableType();
object result = null;
Assert.DoesNotThrow(() => result = nonSerializableInstance.DeepCopy(false));
Assert.IsNull(result);
}
[Test]
public void DeepCopyShouldCreateExactAndIndependentCopyOfAnObject()
{
var instance = new CustomSerializableType
{
DateTimeValueType =
DateTime.Now.AddDays(1).AddMilliseconds(123).AddTicks(123),
NumericValueType = 777,
StringValueType = Guid.NewGuid().ToString(),
ReferenceType =
new CustomSerializableType
{
DateTimeValueType = DateTime.Now,
StringValueType = Guid.NewGuid().ToString()
}
};
var deepCopy = instance.DeepCopy(true);
Assert.IsNotNull(deepCopy);
Assert.IsFalse(ReferenceEquals(instance, deepCopy));
Assert.That(instance.NumericValueType == deepCopy.NumericValueType);
Assert.That(instance.DateTimeValueType == deepCopy.DateTimeValueType);
Assert.That(instance.StringValueType == deepCopy.StringValueType);
Assert.IsNotNull(deepCopy.ReferenceType);
Assert.IsFalse(ReferenceEquals(instance.ReferenceType, deepCopy.ReferenceType));
Assert.That(instance.ReferenceType.DateTimeValueType == deepCopy.ReferenceType.DateTimeValueType);
Assert.That(instance.ReferenceType.StringValueType == deepCopy.ReferenceType.StringValueType);
}
[Serializable]
internal sealed class CustomSerializableType
{
public int NumericValueType { get; set; }
public string StringValueType { get; set; }
public DateTime DateTimeValueType { get; set; }
public CustomSerializableType ReferenceType { get; set; }
}
public sealed class CustomNonSerializableType
{
}
}