有没有更好的方法在 C# 中创建深浅克隆? [英] Is there a much better way to create deep and shallow clones in C#?
问题描述
我一直在为一个项目创建对象,在某些情况下,我必须为这个对象创建一个深层副本,我想出了使用 C# 的内置函数 MemberwiseClone().困扰我的问题是每当我创建一个新类时,我都必须编写一个类似于下面代码的函数来进行浅拷贝..有人可以帮我改进这部分并给我一个更好的浅拷贝比第二行代码.谢谢:)
I have been creating object for a project and there are some instances that I have to create a deep copy for this objects I have come up with the use of a built in function for C# which is MemberwiseClone(). The problem that bothers me is whenever there is a new class that i created , I would have to write a function like the code below for a shallow copy..Can someone please help me improve this part and give me a shallow copy that is better than the second line of code. thanks :)
浅拷贝:
public static RoomType CreateTwin(RoomType roomType)
{
return (roomType.MemberwiseClone() as RoomType);
}
深度复制:
public static T CreateDeepClone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
推荐答案
MemberwiseClone 不是进行深度复制的好选择 (MSDN):
MemberwiseClone is not a good choice to do a Deep Copy (MSDN):
MemberwiseClone 方法通过创建一个新的对象,然后将当前对象的非静态字段复制到新对象.如果字段是值类型,则是场进行.如果字段是引用类型,则引用为复制但引用的对象不是;因此,原对象及其克隆引用同一个对象.
The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
这意味着如果克隆对象具有引用类型的公共字段或属性,它们将引用与原始对象的字段/属性相同的内存位置,因此克隆对象中的每个更改都将反映在初始对象中.这不是真正的深拷贝.
This mean if cloned object has reference type public fields or properties they would reffer to the same memory location as the original object's fields/properties, so each change in the cloned object will be reflected in the initial object. This is not a true deep copy.
您可以使用 BinarySerialization 创建完全独立的对象实例,请参阅 BinaryFormatter 类 用于序列化示例.
You can use BinarySerialization to create a completely independent instance of the object, see MSDN Page of the BinaryFormatter class for an serialization example.
示例和测试工具:
用于创建给定对象的深层副本的扩展方法:
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
{
}
}
这篇关于有没有更好的方法在 C# 中创建深浅克隆?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!