English 中文(简体)
是否有更好的办法在C#中制造深层和浅衣?
原标题:Is there a much better way to create deep and shallow clones in C#?

我一直在为项目制造标语,在一些情况下,我不得不为这些物体制作一个深厚的复印件,我当时正在使用C#公司,C#是成员。 每当有一个新类别时,我就不得不写像下面的《法典》这样的功能。 请允许我帮助我改进这一部分,并给我一份比第2行法典要好的副本。 感谢:

第三届会议

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

DEEP 缔约方会议:

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);
    }
}
最佳回答

成员 Clone不是做深版的好选择():

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.

这意味着,如果受磨损的物体有参照型公共田地或特性,它们会重复与原物体的田地/财产相同的记忆位置,那么受磨损的物体的每一变化都将反映在初始物体中。 这不是一份真实的深度。

您可以使用平级Serialization来制造完全独立的物体,见,作为序列化的例子。


<>Example and Test Harness:

www.un.org/Depts/DGACM/index_spanish.htm 1. 深造物体的延伸方法:

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

www.un.org/Depts/DGACM/index_spanish.htm 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
    {            
    }
}
问题回答

你也可以利用思考来制作该物体的复印件,这应当是最快的方式,因为序列化也使用了这种思考。

在这里,一些法典(测试):

public static T DeepClone<T>(this T original, params Object[] args)
{
    return original.DeepClone(new Dictionary<Object, Object>(), args);
}

private static T DeepClone<T>(this T original, Dictionary<Object, Object> copies, params Object[] args)
{
    T result;
    Type t = original.GetType();

    Object tmpResult;
    // Check if the object already has been copied
    if (copies.TryGetValue(original, out tmpResult))
    {
        return (T)tmpResult;
    }
    else
    {
        if (!t.IsArray)
        {
            /* Create new instance, at this point you pass parameters to
                * the constructor if the constructor if there is no default constructor
                * or you change it to Activator.CreateInstance<T>() if there is always
                * a default constructor */
            result = (T)Activator.CreateInstance(t, args);
            copies.Add(original, result);

            // Maybe you need here some more BindingFlags
            foreach (FieldInfo field in t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance))
            {
                /* You can filter the fields here ( look for attributes and avoid
                    * unwanted fields ) */

                Object fieldValue = field.GetValue(original);

                // Check here if the instance should be cloned
                Type ft = field.FieldType;

                /* You can check here for ft.GetCustomAttributes(typeof(SerializableAttribute), false).Length != 0 to 
                    * avoid types which do not support serialization ( e.g. NetworkStreams ) */
                if (fieldValue != null && !ft.IsValueType && ft != typeof(String))
                {
                    fieldValue = fieldValue.DeepClone(copies);
                    /* Does not support parameters for subobjects nativly, but you can provide them when using
                        * a delegate to create the objects instead of the Activator. Delegates should not work here
                        * they need some more love */
                }

                field.SetValue(result, fieldValue);
            }
        }
        else
        {
            // Handle arrays here
            Array originalArray = (Array)(Object)original;
            Array resultArray = (Array)originalArray.Clone();
            copies.Add(original, resultArray);

            // If the type is not a value type we need to copy each of the elements
            if (!t.GetElementType().IsValueType)
            {
                Int32[] lengths = new Int32[t.GetArrayRank()];
                Int32[] indicies = new Int32[lengths.Length];
                // Get lengths from original array
                for (int i = 0; i < lengths.Length; i++)
                {
                    lengths[i] = resultArray.GetLength(i);
                }

                Int32 p = lengths.Length - 1;
                if (p > -1) indicies[indicies.Length - 1] = -1;

                /* Now we need to iterate though each of the ranks
                    * we need to keep it generic to support all array ranks */
                while (Increment(indicies, lengths, p))
                {
                    Object value = resultArray.GetValue(indicies);
                    if (value != null)
                       resultArray.SetValue(value.DeepClone(copies), indicies);

                }
            }
            result = (T)(Object)resultArray;
        }
        return result;
    }
}

private static Boolean Increment(Int32[] indicies, Int32[] lengths, Int32 p)
{
    if (p > -1)
    {
        indicies[p]++;
        if (indicies[p] < lengths[p])
        {
            return true;
        }
        else
        {
            if (Increment(indicies, lengths, p - 1))
            {
                indicies[p] = 0;
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    return false;
}

页: 1

添加了一些代码,现在你可以使用这种方法复制复杂的物体(甚至具有多个层面的阵列)。 注:代表们尚未执行。

如果您希望得到全面执行,你需要处理<代码>。 ISerializable cross, which is not reallyhard but take some time to reflected over the existing Code. 这样做是为了重新推动执行工作。

如果你试图掩饰的类型不是序列性的,则使用序列化的解决办法是逐体的。

费利克斯·凯尔的法典是一个很好的选择,但我发现几个问题。 这里是一份订正版本,它解决了我发现的一些问题。 我也取消了一些功能性功能(例如建筑参数)。

/// <summary>
/// A DeepClone method for types that are not serializable.
/// </summary>
public static T DeepCloneWithoutSerialization<T>(this T original)
{
    return original.deepClone(new Dictionary<object, object>());
}

static T deepClone<T>(this T original, Dictionary<object, object> copies)
{
    return (T)original.deepClone(typeof(T), copies);
}

/// <summary>
/// Deep clone an object without using serialisation.
/// Creates a copy of each field of the object (and recurses) so that we end up with
/// a copy that doesn t include any reference to the original object.
/// </summary>
static object deepClone(this object original, Type t, Dictionary<object, object> copies)
{
    // Check if object is immutable or copy on update
    if (t.IsValueType || original == null || t == typeof(string) || t == typeof(Guid)) 
        return original;

    // Interfaces aren t much use to us
    if (t.IsInterface) 
        t = original.GetType();

    object tmpResult;
    // Check if the object already has been copied
    if (copies.TryGetValue(original, out tmpResult))
        return tmpResult;

    object result;
    if (!t.IsArray)
    {
        result = Activator.CreateInstance(t);
        copies.Add(original, result);

        // Maybe you need here some more BindingFlags
        foreach (var field in t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance))
        {
            var fieldValue = field.GetValue(original);
            field.SetValue(result, fieldValue.deepClone(field.FieldType, copies));
        }
    }
    else
    {
        // Handle arrays here
        var originalArray = (Array)original;
        var resultArray = (Array)originalArray.Clone();
        copies.Add(original, resultArray);

        var elementType = t.GetElementType();
        // If the type is not a value type we need to copy each of the elements
        if (!elementType.IsValueType)
        {
            var lengths = new int[t.GetArrayRank()];
            var indicies = new int[lengths.Length];
            // Get lengths from original array
            for (var i = 0; i < lengths.Length; i++)
                lengths[i] = resultArray.GetLength(i);

            var p = lengths.Length - 1;

            /* Now we need to iterate though each of the ranks
             * we need to keep it generic to support all array ranks */
            while (increment(indicies, lengths, p))
            {
                var value = resultArray.GetValue(indicies);
                if (value != null)
                    resultArray.SetValue(value.deepClone(elementType, copies), indicies);
            }
        }
        result = resultArray;
    }
    return result;
}

static bool increment(int[] indicies, int[] lengths, int p)
{
    if (p > -1)
    {
        indicies[p]++;
        if (indicies[p] < lengths[p])
            return true;

        if (increment(indicies, lengths, p - 1))
        {
            indicies[p] = 0;
            return true;
        }
    }
    return false;
}




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签