有没有更好的方式来创建在C#中深深浅浅的克隆?方式

2023-09-02 20:43:30 作者:谷欠

我一直在创造了一个项目对象,有一些情况下,我必须创建一个深度复制此对象我想出了利用一个内置的功能,C#这是MemberwiseClone()。困扰我的问题是每当有,我创建了一个新的类,我会写像下面浅copy..Can有人请帮助我提高这部分,给我一个浅浅的副本code函数这是比code中的第二行更好。感谢:)

浅拷贝:

 公共静态RoomType CreateTwin(RoomType roomType)
{
    返程(roomType.MemberwiseClone()为RoomType);
}
 

深度复制:

 公共静态牛逼CreateDeepClone< T>(T源)
{
    如果(!typeof运算(T).IsSerializable)
    {
        抛出新的ArgumentException(类型必须是可序列化,源);
    }

    如果(Object.ReferenceEquals(源,NULL))
    {
        返回默认(T);
    }

    IFormatter格式化=新的BinaryFormatter();
    流流=新的MemoryStream();
    使用(流)
    {
        formatter.Serialize(流来源);
        stream.Seek(0,SeekOrigin.Begin);
        返程(T)formatter.Deserialize(流);
    }
}
 

解决方案

MemberwiseClone是不是一个好的选择做一个深拷贝(的 MSDN ):

  

的MemberwiseClone方法创建一个新的创建一个浅表副本   对象,然后复制当前对象的非静态字段   新的对象。如果字段是值类型,对一个逐位复制   场进行。 如果一个字段是引用类型,引用   复制的,但被引用的对象不是;因此,原   对象,并将其克隆指代相同的对象。

如何使用C 操作Sqlite

这意味着,如果克隆的对象引用类型的公共字段或属性,他们将reffer同一个内存位置与原始对象的字段/属性,因此克隆的对象每一次变化都会反映在初始对象。这是不是一个真正的深层副本。

您可以使用BinarySerialization创建对象的一个​​完全独立的情况下,看到的MSDN页的BinaryFormatter类一个序列化的例子。

示例和测试工具:

扩展方法来创建一个给定对象的深层副本:

 公共静态类MemoryUtils
{
    ///<总结>
    ///创建一个给定对象实例的深副本
    ///< /总结>
    ///&所述; typeparam名称=TObject的>一种给定对象与所述类型; / typeparam>
    被克隆的对象< ///< PARAM NAME =实例&GT /参数>
    ///< PARAM NAME =throwInCaseOfError>
    ///一个值,该值指示是否应该例外的情况下被抛出
    ///错误whils clonin< /参数>
    ///<返回>返回给定对象℃的深拷贝; /回报>
    ///<说明>采用BInarySerialization创建一个真正的深拷贝< /说明>
    公共静态TObject中的DeepCopy< TObject的>(这TObject的情况下,布尔throwInCaseOfError)
        其中,TObject的:类
    {
        如果(例如== NULL)
        {
            抛出新ArgumentNullException(实例);
        }

        TObject的clonedInstance =默认(TObject的);

        尝试
        {
            使用(VAR流=新的MemoryStream())
            {
                的BinaryFormatter的BinaryFormatter =新的BinaryFormatter();
                binaryFormatter.Serialize(流实例);

                //重置位置到流的开头,以便
                //反序列化将能够反序列化一个对象实例
                stream.Position = 0;

                clonedInstance =(TObject中)binaryFormatter.Deserialize(流);
            }
        }
        赶上(例外的例外)
        {
            字符串的errorMessage =的String.Format(CultureInfo.CurrentCulture,
                            异常类型:{0},消息:{1} {2},
                            exception.GetType(),
                            exception.Message,
                            exception.InnerException == NULL?的String.Empty:
                            的String.Format(CultureInfo.CurrentCulture,
                                        的InnerException类型:{0},消息:{1},
                                        exception.InnerException.GetType(),
                                        exception.InnerException.Message));
            的Debug.WriteLine(的errorMessage);

            如果(throwInCaseOfError)
            {
                扔;
            }
        }

        返回clonedInstance;
    }
}
 

NUnit的测试:

 公共类MemoryUtilsFixture
{
    [测试]
    公共无效DeepCopyThrowWhenCopyInstanceOfNonSerializableType()
    {
        VAR nonSerializableInstance =新CustomNonSerializableType();
        Assert.Throws< SerializationException>(()=> nonSerializableInstance.DeepCopy(真));
    }

    [测试]
    公共无效DeepCopyThrowWhenPassedInNull()
    {
        对象实例= NULL;
        Assert.Throws< ArgumentNullException>(()=> instance.DeepCopy(真));
    }

    [测试]
    公共无效DeepCopyThrowWhenCopyInstanceOfNonSerializableTypeAndErrorsDisabled()
    {
        VAR nonSerializableInstance =新CustomNonSerializableType();
        对象result = NULL;

        Assert.DoesNotThrow(()=>结果= nonSerializableInstance.DeepCopy(假));
        Assert.IsNull(结果);
    }

    [测试]
    公共无效DeepCopyShouldCreateExactAndIndependentCopyOfAnObject()
    {
        VAR实例=新CustomSerializableType
                        {
                            DateTimeValueType =
                                DateTime.Now.AddDays(1).AddMilliseconds(123).AddTicks(123),
                            NumericValueType = 777,
                            StringValueType = Guid.NewGuid()。toString()方法,
                            引用类型=
                                新CustomSerializableType
                                    {
                                        DateTimeValueType = DateTime.Now,
                                        StringValueType = Guid.NewGuid()的ToString()
                                    }
                        };

        VAR的DeepCopy = instance.DeepCopy(真正的);

        Assert.IsNotNull(deepcopy的意思);
        Assert.IsFalse(的ReferenceEquals(例如,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);
    }

    [可序列化]
    内部密封类CustomSerializableType
    {
        公众诠释NumericValueType {获得;组; }
        公共字符串StringValueType {获得;组; }
        公开日期时间DateTimeValueType {获得;组; }

        公共CustomSerializableType引用类型{获取;组; }
    }

    公共密封类CustomNonSerializableType
    {
    }
}
 

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 :)

SHALLOW COPY:

public static RoomType CreateTwin(RoomType roomType)
{
    return (roomType.MemberwiseClone() as RoomType);
}

DEEP COPY:

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 is not a good choice to do a Deep Copy (MSDN):

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.

You can use BinarySerialization to create a completely independent instance of the object, see MSDN Page of the BinaryFormatter class for an serialization example.

Example and Test Harness:

Extension method to create a deep copy of a given object:

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 tests:

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
    {            
    }
}