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);
}
}
笔记:
您的 class 必须标记为 [Serializable] 才能生效 .
您的源文件必须包含以下代码:
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
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;
}
}
}
}
6
以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();
0
你可以使用 Nested MemberwiseClone to do a deep copy . 它与复制值结构的速度几乎相同,并且比(a)反射或(b)序列化(如本页其他答案中所述)快一个数量级 .
请注意 if 您使用 Nested MemberwiseClone for a deep copy ,您必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述ShallowCopy方法来创建完整克隆 . 这很简单:总共只有几行,请参阅下面的演示代码 .
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调用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();
}
同样,请注意 if 您使用 Nested MemberwiseClone for a deep copy ,您必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述ShallowCopy方法来创建完整的克隆 . 这很简单:总共只有几行,请参阅上面的演示代码 .
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);
}
}
5
你可以试试这个
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");
}
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];
}
public class Fruit
{
public string Name {get; set;}
public int SeedCount {get; set;}
}
void SomeMethod()
{
List<Fruit> originalFruits = new List<Fruit>();
originalFruits.Add(new Fruit {Name="Apple", SeedCount=10});
originalFruits.Add(new Fruit {Name="Banana", SeedCount=0});
//Deep Copy
List<Fruit> deepCopiedFruits = from f in originalFruits
select new Fruit {Name=f.Name, SeedCount=f.SeedCount};
}
11 回答
我已经看到了一些不同的方法,但我使用了一个通用的实用方法:
笔记:
您的 class 必须标记为
[Serializable]
才能生效 .您的源文件必须包含以下代码:
我wrote a deep object copy extension method,基于递归 "MemberwiseClone" . 它比BinaryFormatter快( three times faster ),适用于任何对象 . 您不需要默认构造函数或可序列化属性 .
源代码:
以Kilhoffer的解决方案为基础......
使用C#3.0,您可以创建一个扩展方法,如下所示:
它扩展了使用DeepClone方法标记为[Serializable]的任何类
你可以使用 Nested MemberwiseClone to do a deep copy . 它与复制值结构的速度几乎相同,并且比(a)反射或(b)序列化(如本页其他答案中所述)快一个数量级 .
请注意 if 您使用 Nested MemberwiseClone for a deep copy ,您必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述ShallowCopy方法来创建完整克隆 . 这很简单:总共只有几行,请参阅下面的演示代码 .
以下是显示相对性能差异的代码输出(深度嵌套的MemberwiseCopy为4.77秒,序列化为39.93秒) . 使用嵌套的MemberwiseCopy几乎与复制结构一样快,复制结构非常接近.NET能够达到的理论最大速度 .
要了解如何使用MemberwiseCopy执行深层复制,以下是演示项目:
然后,从main调用demo:
同样,请注意 if 您使用 Nested MemberwiseClone for a deep copy ,您必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述ShallowCopy方法来创建完整的克隆 . 这很简单:总共只有几行,请参阅上面的演示代码 .
请注意,在克隆对象时,“struct”和“class”之间存在很大差异:
如果您有一个"struct",它是一个值类型,因此您可以复制它,并且将克隆内容 .
如果你有"class",它是一个引用类型,所以如果你复制它,你所做的只是将指针复制到它 . 要创建真正的克隆,您必须更具创造性,并使用在内存中创建原始对象的另一个副本的方法 .
错误地克隆对象可能导致非常难以确定的错误 . 在 生产环境 代码中,我倾向于实现校验和以仔细检查对象是否已正确克隆,并且没有被另一个对它的引用破坏 . 可以在释放模式下关闭此校验和 .
我发现这种方法非常有用:通常,你只想克隆对象的一部分,而不是整个事物 . 对于修改对象,然后将修改后的副本提供到队列中的任何用例,它也是必不可少的 .
Update
可能使用反射来递归遍历对象图以进行深层复制 . WCF使用此技术序列化对象,包括其所有子对象 . 诀窍是使用使其可被发现的属性来注释所有子对象 . 但是,您可能会失去一些性能优势 .
Update
引用独立速度测试(见下面的评论):
我相信BinaryFormatter方法相对较慢(这让我感到惊讶!) . 如果符合ProtoBuf的要求,您可以将ProtoBuf .NET用于某些对象 . 从ProtoBuf入门页面(http://code.google.com/p/protobuf-net/wiki/GettingStarted):
Notes on types supported:
自定义类:
被标记为数据 Contract
有一个无参数构造函数
对于Silverlight:是公开的
许多常见的原语等
单维数组:T []
List <T> / IList <T>
Dictionary <TKey,TValue> / IDictionary <TKey,TValue>
任何实现IEnumerable <T>并具有Add(T)方法的类型
该代码假定类型将在当选成员周围变化 . 因此,不支持自定义结构,因为它们应该是不可变的 .
如果你的 class 遇到这些您可以尝试的要求:
哪个确实非常快......
Edit:
这是修改此代码的工作代码(在.NET 4.6上测试) . 它使用System.Xml.Serialization和System.IO . 无需将类标记为可序列化 .
你可以试试这个
感谢DetoX83 article代码项目 .
也许你只需要一个浅拷贝,在这种情况下使用
Object.MemberWiseClone()
.有关深层复制策略的文档中有很好的建议
MemberWiseClone()
:http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx
最好的方法是:
MSDN文档似乎暗示克隆应该执行深层复制,但它从未明确说明:
ICloneable接口包含一个成员Clone,它旨在支持除MemberWiseClone提供的克隆之外的克隆... MemberwiseClone方法创建一个浅拷贝...
你可以找到我的帖子 .
http://pragmaticcoding.com/index.php/cloning-objects-in-c/
这种方式比
BinarySerialization
快几倍,并且这不需要[Serializable]
属性 .我有一个更简单的想法 . 使用LINQ进行新选择 .