如何在.NET中执行对象的深层复制(特别是C#)?

use*_*931 560 .net c# serialization clone

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

Kil*_*fer 610

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

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

   return (T) formatter.Deserialize(ms);
 }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 您的班级必须标记为[Serializable]为了使其工作.
  • 您的源文件必须包含以下代码:

    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;
    
    Run Code Online (Sandbox Code Playgroud)

  • 递归的MemberwiseClone也会做深度复制,它比BinaryFormatter快3倍,不需要默认构造函数或任何属性.请参阅我的回答:http://stackoverflow.com/a/11308879/235715 (14认同)
  • 事件订阅包含在序列化图中,因为BinaryFormatter通过反射使用字段,事件只是委托类型的字段加上添加/删除/调用方法.您可以在事件中使用[field:NonSerialized]来避免这种情况. (7认同)
  • 我知道这篇文章很旧,但在搜索深度克隆时它仍然是热门内容。请注意,根据 Microsoft (https://aka.ms/binaryformatter) 的说法,这不再是推荐的解决方案,因为它不安全。 (6认同)
  • 如果对象有事件会发生什么,是否由于序列化而丢失了所有内容? (5认同)
  • @ Sean87:在类声明之上,添加``Serializable]`.所以`[Serializable]公共类Foo {}`会将`Foo`标记为可序列化. (4认同)

Ale*_*sev 306

写了一个深度对象复制扩展方法,基于递归"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;
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 当克隆相互引用的对象时,我收到“StackOverflowException”...有人知道解决方法吗? (4认同)
  • 谢谢亚历克斯,是的,我需要打电话给副本而且有效! (3认同)
  • 关于`IsPrimitive`:您为字符串返回true 的原因是什么。此外,您是否有任何理由在语句中使用单个 `&amp;` 而不是 `&amp;&amp;`:`return (type.IsValueType &amp; type.IsPrimitive);`? (3认同)
  • 整件事中没有一个评论。哇。 (3认同)
  • @MattSmith你是绝对正确的GetHashCode,检查它,是的StackOverflow异常 - https://gist.github.com/Burtsev-Alexey/11227277 (2认同)
  • @MattSmith它适用于代表,但我专心禁用它(通过设置null),请参阅https://github.com/Burtsev-Alexey/net-object-deep-copy/issues/7,订阅者被克隆,最后,如果你有两个对象A和B连接(通过事件订阅)你会得到对象A'和B'连接,这是正确的,但这不是大多数人想要的克隆对象. (2认同)
  • @MattSmith字符串被视为基元,因为1:它们是不可变的,2:在字符串上调用受保护的MemberwiseClone将导致内存损坏,字符串数据将变为随机字符,很快.NET运行时将崩溃并出现内部错误,称其为a但是在.NET中:-) (2认同)
  • @AlexBurtsev,您的代码使用命名空间 System.ArrayExtensions,我在哪里可以获得它? (2认同)
  • @ Alex141 - 刚遇到同样的困惑.所有相关代码都在引用文件中,下面是一个ArrayExtensions命名空间. (2认同)
  • 这是一个非常聪明和强大的实现,但是在决定是否适合您的数据模型之前,您必须考虑一些事项。“ Memberwiseclone()”之所以如此之快,是因为它不调用构造函数。因此,如果您的构造函数正在进行繁重的工作(例如事件订阅),那么您就不走运了。它依赖于复制对象的私有字段,从而绕过属性和方法中的业务逻辑。例如,即使所有实例都已更改,我也看到将“ hashCode”字段复制到“ HashSet”集合中。 (2认同)
  • 支持 @yu_ominae 循环引用并且工作正常。你的问题出在别的地方。如果您可以共享您尝试克隆的类的源代码,请在项目网站上打开一个问题。当克隆不应该克隆的内容时(例如 .NET 内部基础设施),我偶尔会遇到 SO 异常,这些异常可能会被意外引用,尤其是指针及其反射。作为一个例子,尝试克隆包含附加到会话的集合的 NHibernate 实体,将导致大混乱,因为实现 IList&lt;T&gt; 的 NH 集合内部包含 DbConnection 字段。 (2认同)
  • 我知道这段代码很旧并且没有持续管理,但它有严重的问题。如果深度复制一个以“Type”对象作为成员的对象,“Type”对象本身也会被复制。新的看起来和旧的一样,但是两个 Type 之间的类型比较会失败,因为 Type.Equals(Type) 使用引用比较。还有一点,如果对象“A”具有用户定义的“struct”成员“B”,并且“B”再次引用对象“C”作为成员,则对象“C”将不会被深度复制与“A”。请谨慎使用此代码。 (2认同)

Nei*_*eil 164

以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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

MyClass copy = obj.DeepClone();
Run Code Online (Sandbox Code Playgroud)

  • 为此添加"public static T DeepClone <T>(this T a)其中T:ISerializable" (35认同)
  • @Amir - 类没有必要实现ISerializable,使用SerializableAttribute进行标记就足够了.该属性使用反射来执行序列化,而界面允许您编写自定义序列化程序 (14认同)
  • 我同意你的陈述,但我喜欢Amir的建议b/c它提供编译时检查.有没有办法调和这两个? (9认同)
  • 传递单元测试var stringbuilder = new StringBuilder("TestData"); var copy = stringbuilder.DeepClone(); Assert.IsFalse(等于(stringbuilder的,复制)); 非常感谢. (2认同)
  • @Neil此方法比NestedMemberwiseClone方法慢10倍,请参阅本页的帖子. (2认同)

Con*_*ngo 52

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

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

以下是显示相对性能差异的代码输出(深度嵌套的MemberwiseCopy为4.77秒,序列化为39.93秒).使用嵌套的MemberwiseCopy几乎与复制结构一样快,并且复制结构非常接近.NET能够达到的理论最大速度.

    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
Run Code Online (Sandbox Code Playgroud)

要了解如何使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,从main调用demo:

    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();
    }
Run Code Online (Sandbox Code Playgroud)

同样,请注意,如果您使用嵌套的MemberwiseClone进行深层复制,则必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述的ShallowCopy方法来创建完整的克隆.这很简单:总共只有几行,请参阅上面的演示代码.

请注意,在克隆对象时,"struct"和"class"之间存在很大差异:

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

更新

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

更新

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

我使用Neil的序列化/反序列化扩展方法,Contango的嵌套MemberwiseClone,Alex Burtsev的基于反射的扩展方法和Aut​​oMapper,每个100万次运行我自己的速度测试.Serialize-deserialize最慢,耗时15.7秒.然后是AutoMapper,耗时10.1秒.基于反射的方法要快2.4秒,速度要快得多.到目前为止,最快的是嵌套的MemberwiseClone,耗时0.1秒.归结为性能与为每个类添加代码以克隆它的麻烦.如果表现不是问题,请选择Alex Burtsev的方法. - Simon Tewsi

  • 我可以确认这比序列化方法快得多.成本是:编写更多代码; 添加字段而不将其添加到克隆方法的维护风险; 需要为任何第三方类(如Dictionary <>)编写辅助类 (3认同)
  • Java和.NET都没有区分包含身份,可变状态,两者或两者都没有的引用.从概念上讲,应该只有一种类型的"克隆":一个新对象,其中每个引用封装与原始对应引用中相同的东西.如果引用封装了identity,则clone的引用必须引用*same*对象.如果它封装了可变状态但是*不是*identity,那么克隆必须接收对具有相同状态的不同对象的引用[否则两个引用都会错误地... (3认同)
  • ......封装身份以及国家].无法克隆封装身份和状态的对象引用,除非通过复制其他所有*来保存对该对象的引用* - 这通常很难或不可能.虽然对某些类型的对象的引用通常用于封装标识,但对其他对象的引用通常会封装可变状态,因此知道对象的类型不足以满足引用的目的. (3认同)
  • 我已经使用 Neil 的序列化/反序列化扩展方法、Contango 的 Nested MemberwiseClone、Alex Burtsev 的基于反射的扩展方法和 AutoMapper 运行了我自己的速度测试,各进行了 100 万次。Serialize-deserialize 最慢,需要 15.7 秒。然后是 AutoMapper,耗时 10.1 秒。基于反射的方法要快得多,需要 2.4 秒。目前最快的是 Nested MemberwiseClone,耗时 0.1 秒。归结为性能与向每个类添加代码以克隆它的麻烦。如果性能不是问题,请使用 Alex Burtsev 的方法。 (2认同)

小智 17

我相信BinaryFormatter方法相对较慢(这让我感到惊讶!).如果符合ProtoBuf的要求,您可以将ProtoBuf .NET用于某些对象.从ProtoBuf入门页面(http://code.google.com/p/protobuf-net/wiki/GettingStarted):

支持的类型说明:

自定义类:

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

该代码假定类型将在当选成员周围变化.因此,不支持自定义结构,因为它们应该是不可变的.

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:

这是修改此代码的工作代码(在.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你的类有一个必须复制的字典,这将不起作用,因为 IDictionary 无法序列化 (2认同)

Sur*_*amy 7

你可以试试这个

    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");
    }
Run Code Online (Sandbox Code Playgroud)

感谢DetoX83 关于代码项目的文章.

  • 仅当您的 obj 具有默认构造函数时才有效! (3认同)