【问题标题】:How do you do a deep copy of an object in .NET? [duplicate]如何在 .NET 中对对象进行深层复制? [复制]
【发布时间】:2019-08-21 05:17:45
【问题描述】:

我想要一个真正的深拷贝。在 Java 中,这很容易,但在 C# 中如何做到这一点?

【问题讨论】:

  • 深拷贝有什么作用?它会复制比特流吗?
  • 深拷贝是复制对象的每个字段的东西。浅拷贝只会创建一个新对象并将所有字段指向原始对象。
  • 复制/克隆 .NET 对象的框架:github.com/havard/copyable
  • 深拷贝会创建具有相同值的对象的第二个实例。浅拷贝(过于简单)就像创建对对象的第二个引用。
  • 使用Mapper,我建议UltraMapper github.com/maurosampietro/UltraMapper

标签: c# .net serialization clone


【解决方案1】:

重要提示

BinaryFormatter 已被弃用,2023 年 11 月后将不再在 .NET 中可用。请参阅 BinaryFormatter Obsoletion Strategy


我已经看到了几种不同的方法,但我使用了一个通用的实用方法:

public static T DeepClone<T>(this T obj)
{
 using (var ms = new MemoryStream())
 {
   var formatter = new BinaryFormatter();
   formatter.Serialize(ms, obj);
   ms.Position = 0;

   return (T) formatter.Deserialize(ms);
 }
}

注意事项:

  • 您的班级必须标记为 [Serializable] 才能正常工作。

  • 您的源文件必须包含以下代码:

     using System.Runtime.Serialization.Formatters.Binary;
     using System.IO;
    

【讨论】:

  • 如果对象有事件会发生什么,他们会因为序列化而丢失一切吗?
  • 事件订阅包含在序列化图中,因为 BinaryFormatter 通过反射使用字段,而事件只是委托类型的字段加上添加/删除/调用方法。您可以在事件上使用 [field: NonSerialized] 来避免这种情况。
  • @Sean87:在类声明上方,添加[Serializable]。所以[Serializable]public class Foo { } 将使Foo 标记为可序列化。
  • Recursive MemberwiseClone 也可以进行深度复制,它的工作速度比 BinaryFormatter 快 3 倍,不需要默认构造函数或任何属性。看我的回答:stackoverflow.com/a/11308879/235715
  • 这会在 UserControlTestContainer 中使用此实用程序代码时创建一个奇怪的异常“未找到程序集”。这真的很奇怪,因为程序集已加载...
【解决方案2】:

wrote a deep object copy extension method,基于递归“MemberwiseClone”。它速度很快(比 BinaryFormatter 快三倍),并且适用于任何对象。您不需要默认构造函数或可序列化的属性。

源代码:

using System.Collections.Generic;
using System.Reflection;
using System.ArrayExtensions;

namespace System
{
    public static class ObjectExtensions
    {
        private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);

        public static bool IsPrimitive(this Type type)
        {
            if (type == typeof(String)) return true;
            return (type.IsValueType & type.IsPrimitive);
        }

        public static Object Copy(this Object originalObject)
        {
            return InternalCopy(originalObject, new Dictionary<Object, Object>(new ReferenceEqualityComparer()));
        }
        private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
        {
            if (originalObject == null) return null;
            var typeToReflect = originalObject.GetType();
            if (IsPrimitive(typeToReflect)) return originalObject;
            if (visited.ContainsKey(originalObject)) return visited[originalObject];
            if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
            var cloneObject = CloneMethod.Invoke(originalObject, null);
            if (typeToReflect.IsArray)
            {
                var arrayType = typeToReflect.GetElementType();
                if (IsPrimitive(arrayType) == false)
                {
                    Array clonedArray = (Array)cloneObject;
                    clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
                }

            }
            visited.Add(originalObject, cloneObject);
            CopyFields(originalObject, visited, cloneObject, typeToReflect);
            RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
            return cloneObject;
        }

        private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
        {
            if (typeToReflect.BaseType != null)
            {
                RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
                CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
            }
        }

        private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
        {
            foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags))
            {
                if (filter != null && filter(fieldInfo) == false) continue;
                if (IsPrimitive(fieldInfo.FieldType)) continue;
                var originalFieldValue = fieldInfo.GetValue(originalObject);
                var clonedFieldValue = InternalCopy(originalFieldValue, visited);
                fieldInfo.SetValue(cloneObject, clonedFieldValue);
            }
        }
        public static T Copy<T>(this T original)
        {
            return (T)Copy((Object)original);
        }
    }

    public class ReferenceEqualityComparer : EqualityComparer<Object>
    {
        public override bool Equals(object x, object y)
        {
            return ReferenceEquals(x, y);
        }
        public override int GetHashCode(object obj)
        {
            if (obj == null) return 0;
            return obj.GetHashCode();
        }
    }

    namespace ArrayExtensions
    {
        public static class ArrayExtensions
        {
            public static void ForEach(this Array array, Action<Array, int[]> action)
            {
                if (array.LongLength == 0) return;
                ArrayTraverse walker = new ArrayTraverse(array);
                do action(array, walker.Position);
                while (walker.Step());
            }
        }

        internal class ArrayTraverse
        {
            public int[] Position;
            private int[] maxLengths;

            public ArrayTraverse(Array array)
            {
                maxLengths = new int[array.Rank];
                for (int i = 0; i < array.Rank; ++i)
                {
                    maxLengths[i] = array.GetLength(i) - 1;
                }
                Position = new int[array.Rank];
            }

            public bool Step()
            {
                for (int i = 0; i < Position.Length; ++i)
                {
                    if (Position[i] < maxLengths[i])
                    {
                        Position[i]++;
                        for (int j = 0; j < i; j++)
                        {
                            Position[j] = 0;
                        }
                        return true;
                    }
                }
                return false;
            }
        }
    }

}

【讨论】:

  • 谢谢亚历克斯,是的,我需要改为调用 copy 并且有效!
  • 关于IsPrimitive:您为字符串返回 true 的原因是什么。另外,您是否有任何理由在声明中使用单个 &amp; 而不是 &amp;&amp;return (type.IsValueType &amp; type.IsPrimitive);
  • @MattSmith 它为代表工作,但我故意禁用它(通过设置 null),请参阅github.com/Burtsev-Alexey/net-object-deep-copy/issues/7,订阅者被克隆,最后如果你有两个对象 A 和 B 连接(通过事件订阅)您将连接对象 A' 和 B',这是正确的,但这不是大多数人在克隆对象时想要的。
  • @Alex141 - 刚刚遇到了同样的困惑。所有相关代码都在引用的文件中,下方有一个 ArrayExtensions 命名空间。
  • 这是一个非常聪明和强大的实现,但是在决定它是否适合您的数据模型之前,您必须考虑一些事情。 Memberwiseclone() 非常快,因为它不调用构造函数。因此,如果您的构造函数正在做繁重的工作,例如事件订阅,那么您就不走运了。它依赖于复制对象的私有字段,绕过属性和方法中的业务逻辑。例如,我看到 hashCode 字段被复制到 HashSet 集合中,即使所有实例都已更改。
【解决方案3】:

基于 Kilhoffer 的解决方案...

使用 C# 3.0,您可以按如下方式创建扩展方法:

public static class ExtensionMethods
{
    // Deep clone
    public static T DeepClone<T>(this T a)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, a);
            stream.Position = 0;
            return (T) formatter.Deserialize(stream);
        }
    }
}

使用 DeepClone 方法扩展任何标记为 [Serializable] 的类

MyClass copy = obj.DeepClone();

【讨论】:

  • 添加“public static T DeepClone(this T a) where T : ISerializable”
  • @Amir - 类不需要实现 ISerializable,使用 SerializableAttribute 标记就足够了。该属性使用反射进行序列化,而接口允许您编写自定义序列化器
  • 我同意你的说法,但我喜欢 Amir 的建议,因为它提供了编译时检查。有什么办法可以调和这两者?
  • 通过单元测试 var stringbuilder = new StringBuilder("TestData"); var copy = stringbuilder.DeepClone(); Assert.IsFalse(Equals(stringbuilder,copy));非常感谢。
  • @Neil 此方法比 NestedMemberwiseClone 方法慢 10 倍,请参阅我在此页面上的帖子。
【解决方案4】:

您可以使用 Nested MemberwiseClone 进行深层复制。它与复制值结构的速度几乎相同,并且比 (a) 反射或 (b) 序列化快一个数量级(如本页其他答案中所述)。

请注意,如果您使用 Nested MemberwiseClone 进行深层复制,您必须为类中的每个嵌套级别手动实现 ShallowCopy,以及调用所有的 DeepCopy表示创建完整克隆的 ShallowCopy 方法。这很简单:总共只有几行,见下面的演示代码。

这是显示相对性能差异的代码输出(深度嵌套的 MemberwiseCopy 为 4.77 秒,序列化为 39.93 秒)。使用嵌套的 MemberwiseCopy 几乎与复制结构一样快,并且复制结构非常接近 .NET 能够达到的理论最大速度,这可能非常接近 C 或 C++ 中相同事物的速度(但会必须运行一些等效的基准来检查此声明)。

    Demo of shallow and deep copy, using classes and MemberwiseClone:
      Create Bob
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Clone Bob >> BobsSon
      Adjust BobsSon details
        BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
      Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Elapsed time: 00:00:04.7795670,30000000
    Demo of shallow and deep copy, using structs and value copying:
      Create Bob
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Clone Bob >> BobsSon
      Adjust BobsSon details:
        BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
      Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
        Bob.Age=30, Bob.Purchase.Description=Lamborghini
      Elapsed time: 00:00:01.0875454,30000000
    Demo of deep copy, using class and serialize/deserialize:
      Elapsed time: 00:00:39.9339425,30000000

要了解如何使用 MemberwiseCopy 进行深度复制,以下是演示项目:

// Nested MemberwiseClone example. 
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
    public Person(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    [Serializable] // Not required if using MemberwiseClone
    public class PurchaseType
    {
        public string Description;
        public PurchaseType ShallowCopy()
        {
            return (PurchaseType)this.MemberwiseClone();
        }
    }
    public PurchaseType Purchase = new PurchaseType();
    public int Age;
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person DeepCopy()
    {
            // Clone the root ...
        Person other = (Person) this.MemberwiseClone();
            // ... then clone the nested class.
        other.Purchase = this.Purchase.ShallowCopy();
        return other;
    }
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
    public PersonStruct(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    public struct PurchaseType
    {
        public string Description;
    }
    public PurchaseType Purchase;
    public int Age;
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct ShallowCopy()
    {
        return (PersonStruct)this;
    }
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct DeepCopy()
    {
        return (PersonStruct)this;
    }
}
// Added only for a speed comparison.
public class MyDeepCopy
{
    public static T DeepCopy<T>(T obj)
    {
        object result = null;
        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            result = (T)formatter.Deserialize(ms);
            ms.Close();
        }
        return (T)result;
    }
}

然后,从 main 调用演示:

    void MyMain(string[] args)
    {
        {
            Console.Write("Demo of shallow and deep copy, using classes and MemberwiseCopy:\n");
            var Bob = new Person(30, "Lamborghini");
            Console.Write("  Create Bob\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
            Console.Write("  Clone Bob >> BobsSon\n");
            var BobsSon = Bob.DeepCopy();
            Console.Write("  Adjust BobsSon details\n");
            BobsSon.Age = 2;
            BobsSon.Purchase.Description = "Toy car";
            Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
            Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
            Debug.Assert(Bob.Age == 30);
            Debug.Assert(Bob.Purchase.Description == "Lamborghini");
            var sw = new Stopwatch();
            sw.Start();
            int total = 0;
            for (int i = 0; i < 100000; i++)
            {
                var n = Bob.DeepCopy();
                total += n.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        {               
            Console.Write("Demo of shallow and deep copy, using structs:\n");
            var Bob = new PersonStruct(30, "Lamborghini");
            Console.Write("  Create Bob\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
            Console.Write("  Clone Bob >> BobsSon\n");
            var BobsSon = Bob.DeepCopy();
            Console.Write("  Adjust BobsSon details:\n");
            BobsSon.Age = 2;
            BobsSon.Purchase.Description = "Toy car";
            Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
            Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
            Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);                
            Debug.Assert(Bob.Age == 30);
            Debug.Assert(Bob.Purchase.Description == "Lamborghini");
            var sw = new Stopwatch();
            sw.Start();
            int total = 0;
            for (int i = 0; i < 100000; i++)
            {
                var n = Bob.DeepCopy();
                total += n.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        {
            Console.Write("Demo of deep copy, using class and serialize/deserialize:\n");
            int total = 0;
            var sw = new Stopwatch();
            sw.Start();
            var Bob = new Person(30, "Lamborghini");
            for (int i = 0; i < 100000; i++)
            {
                var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
                total += BobsSon.Age;
            }
            Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
        }
        Console.ReadKey();
    }

再次注意,如果您使用 Nested MemberwiseClone 进行深层复制,您必须为类中的每个嵌套级别手动实现一个 ShallowCopy,以及一个 DeepCopy调用所有所说的 ShallowCopy 方法来创建一个完整的克隆。这很简单:总共只有几行代码,见上面的演示代码。

请注意,在克隆对象时,“结构”和“类”之间存在很大差异:

  • 如果你有一个“结构”,它是一个值类型,所以你可以复制它,内容将被克隆。
  • 如果你有一个“类”,它是一个引用类型,所以如果你复制它,你所做的就是复制指向它的指针。要创建真正的克隆,您必须更有创意,并使用一种方法在内存中创建原始对象的另一个副本。
  • 错误地克隆对象会导致非常难以确定的错误。在生产代码中,我倾向于实施校验和来仔细检查对象是否已正确克隆,并且没有被另一个引用破坏。可以在发布模式下关闭此校验和。
  • 我发现这种方法非常有用:通常,您只想克隆对象的一部分,而不是整个对象。对于修改对象,然后将修改后的副本送入队列的任何用例,这也是必不可少的。

更新

可能使用反射来递归遍历对象图以进行深度复制。 WCF 使用这种技术来序列化一个对象,包括它的所有子对象。诀窍是使用使其可发现的属性注释所有子对象。但是,您可能会失去一些性能优势。

更新

引用独立速度测试(见下面的 cmets):

我已经使用 Neil 的序列化/反序列化运行了我自己的速度测试 扩展方法,Contango 的 Nested MemberwiseClone,Alex Burtsev 的 基于反射的扩展方法和Aut​​oMapper,100万次 每个。序列化-反序列化最慢,耗时 15.7 秒。然后 AutoMapper 来了,耗时 10.1 秒。更快的是 基于反射的方法,耗时 2.4 秒。到目前为止最快的是 嵌套的 MemberwiseClone,耗时 0.1 秒。归结为性能 与向每个类添加代码以克隆它的麻烦相比。如果性能 Alex Burtsev 的方法不是问题。 ——西蒙·图西

【讨论】:

  • 好帖子。知道为什么序列化要慢得多吗?另外,您的校验和将如何工作?为什么不只是有一个平等检查器?
  • 我可以确认这比序列化方法快得多。代价是:写更多的代码;添加字段而不将其添加到克隆方法的维护风险;需要为任何 3rd 方类(例如 Dictionary)编写帮助程序类
  • 太糟糕了,Java 和 .NET 都没有区分封装身份、可变状态或两者都封装的引用。从概念上讲,应该只有一种类型的“克隆”:一个新对象,其中每个引用都封装了与原始引用中对应的相同的东西。如果引用封装了身份,则克隆的引用必须引用 same 对象。如果它封装了可变状态但 not 标识,则克隆必须接收对具有相同状态的不同对象的引用[否则两个引用都会错误地...
  • ...封装身份和状态]。封装身份和状态的对象引用不能被克隆,除非复制其他所有包含对该对象的引用——这通常是困难或不可能的壮举。虽然对某些类型对象的引用通常用于封装身份,而对其他对象的引用通常会封装可变状态,但知道对象的类型不足以达到持有引用的目的。
  • 我已经使用 Neil 的序列化/反序列化扩展方法、Contango 的 Nested MemberwiseClone、Alex Burtsev 的基于反射的扩展方法和 AutoMapper 运行了自己的速度测试,每个测试 100 万次。序列化-反序列化最慢,耗时 15.7 秒。然后是 AutoMapper,耗时 10.1 秒。更快的是基于反射的方法,耗时 2.4 秒。到目前为止,最快的是 Nested MemberwiseClone,耗时 0.1 秒。归结为性能与向每个类添加代码以克隆它的麻烦。如果性能不是问题,请使用 Alex Burtsev 的方法。
【解决方案5】:

我相信 BinaryFormatter 方法相对较慢(这让我感到惊讶!)。如果某些对象满足 ProtoBuf 的要求,您也许可以对某些对象使用 ProtoBuf .NET。来自 ProtoBuf 入门页面 (http://code.google.com/p/protobuf-net/wiki/GettingStarted):

有关支持类型的说明:

自定义类:

  • 被标记为数据合同
  • 有一个无参数的构造函数
  • 对于 Silverlight:公开
  • 许多常见的原语等
  • 单维数组:T[]
  • 列表 / IList
  • 字典 / IDictionary
  • 任何实现 IEnumerable 并具有 Add(T) 方法的类型

The code assumes that types will be mutable around the elected members.因此,不支持自定义结构,因为它们应该是不可变的。

如果您的班级满足这些要求,您可以尝试:

public static void deepCopy<T>(ref T object2Copy, ref T objectCopy)
{
    using (var stream = new MemoryStream())
    {
        Serializer.Serialize(stream, object2Copy);
        stream.Position = 0;
        objectCopy = Serializer.Deserialize<T>(stream);
    }
}

这确实非常快......

编辑:

这是对此进行修改的工作代码(在 .NET 4.6 上测试)。它使用 System.Xml.Serialization 和 System.IO。无需将类标记为可序列化。

public void DeepCopy<T>(ref T object2Copy, ref T objectCopy)
{
    using (var stream = new MemoryStream())
    {
        var serializer = new XS.XmlSerializer(typeof(T));

        serializer.Serialize(stream, object2Copy);
        stream.Position = 0;
        objectCopy = (T)serializer.Deserialize(stream);
    }
}

【讨论】:

  • 想知道它与上面的 Nested MemberwiseClone 答案相比有多快?
  • 如果你的班级有一个必须复制的字典,这将不起作用,因为 IDictionary 不能被序列化
【解决方案6】:

你可以试试这个

    public static object DeepCopy(object obj)
    {
        if (obj == null)
            return null;
        Type type = obj.GetType();

        if (type.IsValueType || type == typeof(string))
        {
            return obj;
        }
        else if (type.IsArray)
        {
            Type elementType = Type.GetType(
                 type.FullName.Replace("[]", string.Empty));
            var array = obj as Array;
            Array copied = Array.CreateInstance(elementType, array.Length);
            for (int i = 0; i < array.Length; i++)
            {
                copied.SetValue(DeepCopy(array.GetValue(i)), i);
            }
            return Convert.ChangeType(copied, obj.GetType());
        }
        else if (type.IsClass)
        {

            object toret = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = type.GetFields(BindingFlags.Public |
                        BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                    continue;
                field.SetValue(toret, DeepCopy(fieldValue));
            }
            return toret;
        }
        else
            throw new ArgumentException("Unknown type");
    }

感谢代码项目中的 DetoX83 article

【讨论】:

  • 只有当你的 obj 有默认构造函数时才有效!
【解决方案7】:

最好的办法是:

    public interface IDeepClonable<T> where T : class
    {
        T DeepClone();
    }

    public class MyObj : IDeepClonable<MyObj>
    {
        public MyObj Clone()
        {
            var myObj = new MyObj();
            myObj._field1 = _field1;//value type
            myObj._field2 = _field2;//value type
            myObj._field3 = _field3;//value type

            if (_child != null)
            {
                myObj._child = _child.DeepClone(); //reference type .DeepClone() that does the same
            }

            int len = _array.Length;
            myObj._array = new MyObj[len]; // array / collection
            for (int i = 0; i < len; i++)
            {
                myObj._array[i] = _array[i];
            }

            return myObj;
        }

        private bool _field1;
        public bool Field1
        {
            get { return _field1; }
            set { _field1 = value; }
        }

        private int _field2;
        public int Property2
        {
            get { return _field2; }
            set { _field2 = value; }
        }

        private string _field3;
        public string Property3
        {
            get { return _field3; }
            set { _field3 = value; }
        }

        private MyObj _child;
        private MyObj Child
        {
            get { return _child; }
            set { _child = value; }
        }

        private MyObj[] _array = new MyObj[4];
    }

【讨论】:

【解决方案8】:

也许你只需要一个浅拷贝,在这种情况下使用Object.MemberWiseClone()

MemberWiseClone() 的文档中有很好的深度复制策略建议:-

http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

【讨论】:

  • 不错的尝试,但他特意要求深度克隆。
  • 您可以使用 MemberwiseClone 进行深度克隆,您只需添加嵌套即可。请参阅上面@Gravitas 的答案。
【解决方案9】:
    public static object CopyObject(object input)
    {
        if (input != null)
        {
            object result = Activator.CreateInstance(input.GetType());
            foreach (FieldInfo field in input.GetType().GetFields(Consts.AppConsts.FullBindingList))
            {
                if (field.FieldType.GetInterface("IList", false) == null)
                {
                    field.SetValue(result, field.GetValue(input));
                }
                else
                {
                    IList listObject = (IList)field.GetValue(result);
                    if (listObject != null)
                    {
                        foreach (object item in ((IList)field.GetValue(input)))
                        {
                            listObject.Add(CopyObject(item));
                        }
                    }
                }
            }
            return result;
        }
        else
        {
            return null;
        }
    }

这种方式比BinarySerialization 快几倍,并且不需要[Serializable] 属性。

【讨论】:

  • 您没有继续沿非 IList 分支进行深层复制,我认为您会遇到 ICollection/IEnumerable 的问题。
  • 使用“Nested MemberwiseClone”技术又快了一个数量级(请参阅我在@Gravitas 下的帖子)。
  • 什么是Consts.AppConsts.FullBindingList
【解决方案10】:

MSDN 文档似乎暗示 Clone 应该执行深层复制,但从未明确说明:

ICloneable 接口包含一个成员 Clone,它旨在支持超出 MemberWiseClone 提供的克隆……MemberwiseClone 方法创建一个浅拷贝……

你会发现我的帖子很有帮助。

http://pragmaticcoding.com/index.php/cloning-objects-in-c/

【讨论】:

  • ICloneable 的问题在于 Clone 方法没有明确指定它是执行浅拷贝还是深拷贝,因此调用者永远无法确定。因此,有一些 [讨论|blogs.msdn.com/brada/archive/2004/05/03/125427.aspx] 关于使 ICloneable 在 .NET Framework 中过时。
猜你喜欢
  • 2011-07-25
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
  • 2010-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多