Если вы хотите использовать jQuery, он имеет хороший пример в своей документации:
$.each([ 52, 97 ], function( index, value ) {
alert( index + ": " + value );
});
Хотя стандартная практика заключается в реализации интерфейса ICloneable
(описанный здесь здесь , поэтому я не буду срыгиваться), вот хороший клон для клон-клонов, который я нашел на Код проекта некоторое время назад и включил его в наш материал.
Как уже упоминалось в другом месте, это требует, чтобы ваши объекты были сериализуемыми.
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don't serialize a null object, simply return the default for that object
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);
}
}
}
Идея состоит в том, что он сериализует ваш объект и затем десериализует его в новый объект. Преимущество в том, что вам не нужно беспокоиться о клонировании всего, когда объект становится слишком сложным.
И с использованием методов расширения (также из исходного источника):
Если вы предпочитаете использовать новые методы расширения для C # 3.0, измените метод на наличие следующей сигнатуры:
public static T Clone<T>(this T source)
{
//...
}
Теперь вызов метода просто становится objectBeingCloned.Clone();
.
EDIT (10 января 2015 г.) Думал, что передумал, отметив, что я недавно начал использовать (Newtonsoft) Json для этого, он должен быть легче, и избегает накладные расходы [Сериализуемые] теги. (NB @atconway указал в комментариях, что частные члены не клонируются с использованием метода JSON)
/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
Приветствия.
Чтобы клонировать объект класса, вы можете использовать метод Object.MemberwiseClone,
просто добавьте эту функцию в свой класс:
public class yourClass
{
// ...
// ...
public yourClass DeepCopy()
{
yourClass othercopy = (yourClass)this.MemberwiseClone();
return othercopy;
}
}
, чтобы выполнить глубокую независимую копию, просто вызовите метод DeepCopy:
yourClass newLine = oldLine.DeepCopy();
надеюсь, что это поможет.
Хорошо, есть некоторые очевидные примеры с отражением в этом сообщении, НО отражение обычно медленное, пока вы не начнете его кэшировать должным образом.
, если вы будете кэшировать его правильно, чем он будет глубоко clone 1000000 object by 4,6s (измеряется Watcher).
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
, чем вы берете кешированные свойства или добавляете новый словарь и используете их просто
foreach (var prop in propList)
{
var value = prop.GetValue(source, null);
prop.SetValue(copyInstance, value, null);
}
проверка полного кода в моем сообщении в другом ответе
Другими словами, идет с другим ответом, если у вас нет узкого места производительности, которое требует исправления, и вы можете доказать это с помощью профайлера .
Следующий метод выполнения глубокого клона:
Для максимальной скорости вы можете использовать Вложенный MemberwiseClone сделает глубокую копию. Его почти такая же скорость, как и копирование структуры значений, и намного быстрее, чем (а) отражение или (б) сериализация (как описано в других ответах на этой странице).
Обратите внимание, что если вы используете Nested MemberwiseClone для глубокой копии вам нужно вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все указанные методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демо-код ниже.
Вот результат кода, показывающий относительную разницу в производительности для 100 000 клонов:
Использование вложенного члена в классе почти так же быстро, как копирование структуры, а копирование структуры довольно близко к теоретической максимальной скорости, на которую способен .NET.
Demo 1 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 2 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 3 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;
}
}
Затем вызовите демо из основного:
void MyMain(string[] args)
{
{
Console.Write("Demo 1 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\n", sw.Elapsed, total);
}
{
Console.Write("Demo 2 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\n", sw.Elapsed, total);
}
{
Console.Write("Demo 3 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();
}
Снова обратите внимание, что если вы используете Вложенный объект MemberwiseClone для глубокой копии вам нужно вручную реализовать ShallowCopy для каждого вложенного уровня в классе, а DeepCopy, который вызывает все указанные методы ShallowCopy для создания полного clo северо-восток Это просто: всего несколько строк, см. Демонстрационный код выше.
Обратите внимание, что когда дело доходит до клонирования объекта, существует большая разница между «структурой» и «классом»:
См. различия между типами значений и типами ссылок .
. Один превосходный вариант использования этого кода - это подача клонов вложенного класса или структуры в очередь для реализации шаблона производителя / потребителя.
ConcurrentQueue
. Это на практике очень хорошо работает и позволяет отделить множество потоков (производителей) от одного или нескольких потоки (потребители).
И этот метод также близок быстро: если мы используем вложенные структуры, это на 35 раз быстрее, чем сериализация / десериализация вложенных классов, и позволяет нам использовать преимущества всех потоков, доступных на машина.
По-видимому, ExpressMapper работает так же быстро, если не быстрее, чем ручное кодирование, например, выше. Возможно, мне придется посмотреть, как они сравниваются с профилировщиком.
Мне нужен клонер для очень простых объектов в основном примитивов и списков. Если ваш объект несовместим с сериализуемым JSON, этот метод будет делать трюк. Это не требует модификации или реализации интерфейсов в клонированном классе, просто JSON-сериализаторе, таком как JSON.NET.
public static T Clone<T>(T source)
{
var serialized = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(serialized);
}
Здесь решение быстрое и легкое, что работало для меня без передачи на сериализацию / десериализацию.
public class MyClass
{
public virtual MyClass DeepClone()
{
var returnObj = (MyClass)MemberwiseClone();
var type = returnObj.GetType();
var fieldInfoArray = type.GetRuntimeFields().ToArray();
foreach (var fieldInfo in fieldInfoArray)
{
object sourceFieldValue = fieldInfo.GetValue(this);
if (!(sourceFieldValue is MyClass))
{
continue;
}
var sourceObj = (MyClass)sourceFieldValue;
var clonedObj = sourceObj.DeepClone();
fieldInfo.SetValue(returnObj, clonedObj);
}
return returnObj;
}
}
EDIT: требуется
using System.Linq;
using System.Reflection;
Вот как я его использовал
public MyClass Clone(MyClass theObjectIneededToClone)
{
MyClass clonedObj = theObjectIneededToClone.DeepClone();
}
Реализация глубокой копии:
public static object CloneObject(object opSource)
{
//grab the type and create a new instance of that type
Type opSourceType = opSource.GetType();
object opTarget = CreateInstanceOfType(opSourceType);
//grab the properties
PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//iterate over the properties and if it has a 'set' method assign it from the source TO the target
foreach (PropertyInfo item in opPropertyInfo)
{
if (item.CanWrite)
{
//value types can simply be 'set'
if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
{
item.SetValue(opTarget, item.GetValue(opSource, null), null);
}
//object/complex types need to recursively call this method until the end of the tree is reached
else
{
object opPropertyValue = item.GetValue(opSource, null);
if (opPropertyValue == null)
{
item.SetValue(opTarget, null, null);
}
else
{
item.SetValue(opTarget, CloneObject(opPropertyValue), null);
}
}
}
}
//return the new item
return opTarget;
}
Если ваше Дерево объектов является Serializeable, вы также можете использовать что-то вроде этого
static public MyClass Clone(MyClass myClass)
{
MyClass clone;
XmlSerializer ser = new XmlSerializer(typeof(MyClass), _xmlAttributeOverrides);
using (var ms = new MemoryStream())
{
ser.Serialize(ms, myClass);
ms.Position = 0;
clone = (MyClass)ser.Deserialize(ms);
}
return clone;
}
, чтобы сообщить, что это Решение довольно просто, но оно не так эффективно, как другие решения.
И убедитесь, что если класс вырастет, все равно будут клонированы только те поля, которые также будут сериализованы.
В общем, вы реализуете интерфейс ICloneable и реализуете Clone самостоятельно. Объекты C # имеют встроенный метод MemberwiseClone, который выполняет неглубокую копию, которая может помочь вам выполнить все примитивы.
Для глубокой копии нет способа узнать, как автоматически это сделать.
Я создал версию принятого ответа, которая работает как с [Serializable], так и с [DataContract]. Прошло некоторое время с тех пор, как я его написал, но если я правильно помню, [DataContract] потребовался другой сериализатор.
Требуется System, System.IO, System.Runtime.Serialization, System.Runtime. Serialization.Formatters.Binary, System.Xml ;
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]'
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if (typeof(T).IsSerializable == true)
{
return CloneUsingSerializable<T>(source);
}
if (IsDataContract(typeof(T)) == true)
{
return CloneUsingDataContracts<T>(source);
}
throw new ArgumentException("The type must be Serializable or use DataContracts.", "source");
}
/// <summary>
/// Perform a deep Copy of an object that is marked with '[Serializable]'
/// </summary>
/// <remarks>
/// Found on http://stackoverflow.com/questions/78536/cloning-objects-in-c-sharp
/// Uses code found on CodeProject, which allows free use in third party apps
/// - http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// </remarks>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneUsingSerializable<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don't serialize a null object, simply return the default for that object
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);
}
}
/// <summary>
/// Perform a deep Copy of an object that is marked with '[DataContract]'
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneUsingDataContracts<T>(T source)
{
if (IsDataContract(typeof(T)) == false)
{
throw new ArgumentException("The type must be a data contract.", "source");
}
// ** Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
using(Stream stream = new MemoryStream())
{
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
{
dcs.WriteObject(writer, source);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
{
return (T)dcs.ReadObject(reader);
}
}
}
}
/// <summary>
/// Helper function to check if a class is a [DataContract]
/// </summary>
/// <param name="type">The type of the object to check.</param>
/// <returns>Boolean flag indicating if the class is a DataContract (true) or not (false) </returns>
public static bool IsDataContract(Type type)
{
object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
return attributes.Length == 1;
}
}
Этот метод решил для меня проблему:
private static MyObj DeepCopy(MyObj source)
{
var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);
}
Используйте его следующим образом: MyObj a = DeepCopy(b);
Короткий ответ: вы наследуете интерфейс ICloneable, а затем реализуете функцию .clone. Clone должен делать копию по порядку и выполнять глубокую копию на любом члене, который ее требует, а затем возвращать результирующий объект. Это рекурсивная операция (требуется, чтобы все члены класса, к которому вы хотите клонировать, являются либо типами значений, либо реализуют ICloneable и что их члены являются либо типами значений, либо реализуют ICloneable и т. Д.).
Для более подробное объяснение о клонировании с использованием ICloneable, проверьте в этой статье .
Ответ long «это зависит». Как упоминалось другими, ICloneable не поддерживается дженериками, требует специальных соображений для ссылок на круговые классы и на самом деле рассматривается некоторыми как «ошибка» в .NET Framework. Метод сериализации зависит от ваших сериализуемых объектов, которых они могут не быть, и вы не можете контролировать. В обществе по-прежнему много дискуссий, которые являются «лучшей» практикой. На самом деле, ни одно из решений не является одним из самых подходящих для всех ситуаций, таких как ICloneable, изначально было истолковано.
См. Статью Developer's Corner для нескольких больше вариантов (кредит для Ян).
Поскольку я не мог найти клонера, который отвечает всем моим требованиям в разных проектах, я создал глубокий клонер, который можно настроить и адаптировать к различным структурам кода вместо того, чтобы адаптировать мой код для удовлетворения требований клонеров. Это достигается путем добавления аннотаций к коду, который должен быть клонирован, или вы просто оставляете код так, как будто он имеет поведение по умолчанию. Он использует отражение, тип кэша и основан на quickflect . Процесс клонирования очень быстрый для огромного количества данных и высокой иерархии объектов (по сравнению с другими алгоритмами, основанными на отражении / сериализации).
https://github.com/kalisohn/CloneBehave
Также доступен в виде пакета nuget: https: //www.nuget. org / packages / Clone.Behave / 1.0.0
Например: Следующий код содержит адрес deepClone, но выполняет только мелкую копию поля _currentJob.
public class Person
{
[DeepClone(DeepCloneBehavior.Shallow)]
private Job _currentJob;
public string Name { get; set; }
public Job CurrentJob
{
get{ return _currentJob; }
set{ _currentJob = value; }
}
public Person Manager { get; set; }
}
public class Address
{
public Person PersonLivingHere { get; set; }
}
Address adr = new Address();
adr.PersonLivingHere = new Person("John");
adr.PersonLivingHere.BestFriend = new Person("James");
adr.PersonLivingHere.CurrentJob = new Job("Programmer");
Address adrClone = adr.Clone();
//RESULT
adr.PersonLivingHere == adrClone.PersonLivingHere //false
adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false
adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true
adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true
Мне нравится Copyconstructors:
public AnyObject(AnyObject anyObject)
{
foreach (var property in typeof(AnyObject).GetProperties())
{
property.SetValue(this, property.GetValue(anyObject));
}
foreach (var field in typeof(AnyObject).GetFields())
{
field.SetValue(this, field.GetValue(anyObject));
}
}
Если у вас есть что-то, что можно скопировать, добавьте их
Я только что создал проект CloneExtensions
library . Он выполняет быстрый, глубокий клон, используя простые операции присваивания, сгенерированные компиляцией кода времени исполнения выражения.
Как его использовать?
Вместо того, чтобы писать свои собственные методы Clone
или Copy
с тоном назначений между полями и свойствами, сделайте программу для себя, используя дерево выражений. GetClone<T>()
метод, помеченный как метод расширения, позволяет просто вызвать его в вашем экземпляре:
var newInstance = source.GetClone();
Вы можете выбрать, что должно быть скопировано с source
на newInstance
, используя CloningFlags
перечисление:
var newInstance
= source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);
Что можно клонировать?
Следующие члены класса / структуры клонируются внутри страны:
Насколько это быстро?
Решение быстрее, чем отражение, потому что информация членов должна быть собирается только один раз, прежде чем GetClone<T>
будет использоваться впервые для заданного типа T
.
Он также быстрее, чем на основе сериализации
и более ...
Подробнее о сгенерированных выражениях в документации читайте больше.
Пример отладки образца выражения для List<int>
:
.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
System.Collections.Generic.List`1[System.Int32] $source,
CloneExtensions.CloningFlags $flags,
System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
.Block(System.Collections.Generic.List`1[System.Int32] $target) {
.If ($source == null) {
.Return #Label1 { null }
} .Else {
.Default(System.Void)
};
.If (
.Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
) {
$target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
).Invoke((System.Object)$source)
} .Else {
$target = .New System.Collections.Generic.List`1[System.Int32]()
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
) {
.Default(System.Void)
} .Else {
.Default(System.Void)
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
) {
.Block() {
$target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
$source.Capacity,
$flags,
$initializers)
}
} .Else {
.Default(System.Void)
};
.If (
((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
) {
.Block(
System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
System.Collections.Generic.ICollection`1[System.Int32] $var2) {
$var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
$var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
.Loop {
.If (.Call $var1.MoveNext() != False) {
.Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
$var1.Current,
$flags,
$initializers))
} .Else {
.Break #Label2 { }
}
}
.LabelTarget #Label2:
}
} .Else {
.Default(System.Void)
};
.Label
$target
.LabelTarget #Label1:
}
}
Что имеет такое же значение, как следующий код c #:
(source, flags, initializers) =>
{
if(source == null)
return null;
if(initializers.ContainsKey(typeof(List<int>))
target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
else
target = new List<int>();
if((flags & CloningFlags.Properties) == CloningFlags.Properties)
{
target.Capacity = target.Capacity.GetClone(flags, initializers);
}
if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
{
var targetCollection = (ICollection<int>)target;
foreach(var item in (ICollection<int>)source)
{
targetCollection.Add(item.Clone(flags, initializers));
}
}
return target;
}
Разве не совсем так, как вы напишете свой собственный метод Clone
для List<int>
?
После многого чтения многих опций, связанных здесь, и возможных решений для этой проблемы, я полагаю, что все варианты очень хорошо описаны на ссылке Ian P (все остальные варианты являются вариантами этих ), и наилучшее решение предоставляется ссылкой Педро77 в комментариях к вопросу.
Итак, я просто скопирую соответствующие части этих двух ссылок здесь. Таким образом, мы можем иметь:
Прежде всего, это все наши варианты:
статья Быстрая глубокая копия с помощью деревьев выражений также сравнивает производительность клонирования с помощью сериализации, отражения и выражений.
Г-н Venkat Subramaniam (избыточная ссылка здесь) подробно объясняет, почему .
Все его статьи кругом вокруг примера, который пытается применяться в большинстве случаев, используя 3 объекта: Person , Мозг и Город . Мы хотим клонировать человека, который будет иметь свой собственный мозг, но тот же город.
Это моя слегка измененная версия его вывода:
Копирование объекта с помощью указание
blockquote>New
, за которым следует имя класса, часто приводит к тому, что код не расширяется. Использование клона, применение шаблона прототипа, является лучшим способом достижения этого. Однако использование клона, как это предусмотрено в C # (и Java), может быть довольно проблематичным. Лучше предоставить защищенный (непубличный) конструктор копирования и вызвать его из метода клонирования. Это дает нам возможность делегировать задачу создания объекта на экземпляр самого класса, тем самым обеспечивая расширяемость, а также безопасное создание объектов с использованием защищенного конструктора копии.Надеюсь, это реализация может сделать все ясно:
public class Person : ICloneable { private final Brain brain; // brain is final since I do not want // any transplant on it once created! private int age; public Person(Brain aBrain, int theAge) { brain = aBrain; age = theAge; } protected Person(Person another) { Brain refBrain = null; try { refBrain = (Brain) another.brain.clone(); // You can set the brain in the constructor } catch(CloneNotSupportedException e) {} brain = refBrain; age = another.age; } public String toString() { return "This is person with " + brain; // Not meant to sound rude as it reads! } public Object clone() { return new Person(this); } … }
Теперь рассмотрим вопрос о том, какой класс следует из Person.
public class SkilledPerson extends Person { private String theSkills; public SkilledPerson(Brain aBrain, int theAge, String skills) { super(aBrain, theAge); theSkills = skills; } protected SkilledPerson(SkilledPerson another) { super(another); theSkills = another.theSkills; } public Object clone() { return new SkilledPerson(this); } public String toString() { return "SkilledPerson: " + super.toString(); } }
Вы можете попробовать запустить следующий код:
public class User { public static void play(Person p) { Person another = (Person) p.clone(); System.out.println(p); System.out.println(another); } public static void main(String[] args) { Person sam = new Person(new Brain(), 1); play(sam); SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer"); play(bob); } }
Выведенный результат будет:
This is person with Brain@1fcc69 This is person with Brain@253498 SkilledPerson: This is person with SmarterBrain@1fef6f SkilledPerson: This is person with SmarterBrain@209f4e
Обратите внимание, что если мы храним счетчик количества объектов, клон, реализованный здесь, будет содержать правильный подсчет количества объектов.
Если вы хотите, чтобы истинное клонирование было неизвестным, вы можете взглянуть на fastclone .
Это клонирование на основе выражений, работающее примерно в 10 раз быстрее, чем двоичная сериализация и сохранение полного графа объектов целостность.
Это означает: если вы ссылаетесь несколько раз на один и тот же объект в иерархии, клон также будет иметь один экземпляр, на который ссылаются.
Нет необходимости в интерфейсах, атрибуты или любую другую модификацию клонируемых объектов.
Выполните следующие действия:
ISelf<T>
с единственным параметром Self
, доступным только для чтения, который возвращает T
и ICloneable<out T>
, который происходит из ISelf<T>
, и включает метод T Clone()
. CloneBase
, который реализует кастинг protected virtual generic VirtualClone
MemberwiseClone
для переданного типа. VirtualClone
, вызывая метод базового клона, а затем делать все, что нужно сделать, чтобы правильно клонировать те аспекты производного типа, которые родительский метод VirtualClone еще не обработал. Для максимальной универсальности наследования классы, демонстрирующие функциональность публичного клонирования, должны быть sealed
, но должны быть получены из базового класса, который в противном случае идентичен, за исключением отсутствия клонирования. Вместо того, чтобы передавать переменные явного клонируемого типа, возьмите параметр типа ICloneable<theNonCloneableType>
. Это позволит подпрограмме, которая ожидает, что клонированная производная Foo
будет работать с клонируемой производной от DerivedFoo
, но также позволит создавать некланируемые производные Foo
.
Я придумал это, чтобы преодолеть недостаток .NET , который должен вручную выполнить глубокое копирование List & lt; T>.
Я использую это:
static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
foreach (SpotPlacement sp in spotPlacements)
{
yield return (SpotPlacement)sp.Clone();
}
}
И в другом месте:
public object Clone()
{
OrderItem newOrderItem = new OrderItem();
...
newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
...
return newOrderItem;
}
Я попытался придумать oneliner, который делает это, но это невозможно, из-за того, что выход не работает внутри блоков анонимного метода.
Еще лучше использовать универсальный список & lt; T> cloner:
class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}
Невозможно использовать ICloneable не потому, что у него нет общего интерфейса. Причина не в том, чтобы использовать его, потому что это неопределенный . Он не дает понять, получаете ли вы мелкую или глубокую копию; это зависит от исполнителя.
Да, MemberwiseClone
делает мелкую копию, но противоположность MemberwiseClone
не является Clone
; это было бы, пожалуй, DeepClone
, которого не существует. Когда вы используете объект через свой интерфейс ICloneable, вы не можете знать, какой тип клонирования выполняет базовый объект. (И комментарии XML не станут ясными, потому что вы получите комментарии к интерфейсу, а не те, которые относятся к методу Clone объекта.)
То, что я обычно делаю, это просто сделать метод Copy
, который делает именно то, что я хочу.
Простой метод расширения для копирования всех общедоступных свойств. Работает для любых объектов и не требует, чтобы класс был [Serializable]
. Может быть расширен для другого уровня доступа.
public static void CopyTo( this object S, object T )
{
foreach( var pS in S.GetType().GetProperties() )
{
foreach( var pT in T.GetType().GetProperties() )
{
if( pT.Name != pS.Name ) continue;
( pT.GetSetMethod() ).Invoke( T, new object[]
{ pS.GetGetMethod().Invoke( S, null ) } );
}
};
}
Держите вещи простыми и используйте AutoMapper , как упоминалось выше, это простая небольшая библиотека для сопоставления одного объекта с другим ... Чтобы скопировать объект в другой с тем же типом, все, что вам нужно, это три строки кода:
MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);
Целевой объект теперь является копией исходного объекта. Не достаточно просто? Создайте метод расширения, который будет использоваться повсюду в вашем решении:
public static T Copy<T>(this T source)
{
T copy = default(T);
Mapper.CreateMap<T, T>();
copy = Mapper.Map<T, T>(source);
return copy;
}
Используя метод расширения, три строки становятся одной строкой:
MyType copy = source.Copy();
Я думаю, вы можете попробовать это.
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it
Мы видели много идей от сериализации по сравнению с ручной реализацией до отражения, и я хочу предложить совершенно другой подход с использованием CGbR Code Generator . Генерирующий клон-метод - это память и центральный процессор, а в 300 раз быстрее, чем стандартный DataContractSerializer.
Все, что вам нужно, это частичное определение класса с ICloneable
, а генератор делает остальные:
public partial class Root : ICloneable
{
public Root(int number)
{
_number = number;
}
private int _number;
public Partial[] Partials { get; set; }
public IList<ulong> Numbers { get; set; }
public object Clone()
{
return Clone(true);
}
private Root()
{
}
}
public partial class Root
{
public Root Clone(bool deep)
{
var copy = new Root();
// All value types can be simply copied
copy._number = _number;
if (deep)
{
// In a deep clone the references are cloned
var tempPartials = new Partial[Partials.Length];
for (var i = 0; i < Partials.Length; i++)
{
var value = Partials[i];
value = value.Clone(true);
tempPartials[i] = value;
}
copy.Partials = tempPartials;
var tempNumbers = new List<ulong>(Numbers.Count);
for (var i = 0; i < Numbers.Count; i++)
{
var value = Numbers[i];
tempNumbers.Add(value);
}
copy.Numbers = tempNumbers;
}
else
{
// In a shallow clone only references are copied
copy.Partials = Partials;
copy.Numbers = Numbers;
}
return copy;
}
}
Примечание. У последней версии есть более нулевые проверки, но я оставил их для лучшего понимания.
Я видел, как это реализовано и через отражение. В основном существует метод, который будет проходить через элементы объекта и соответствующим образом копировать их в новый объект. Когда он достиг ссылочных типов или коллекций, я думаю, что он сделал рекурсивный вызов сам по себе. Отражение стоит дорого, но оно работает очень хорошо.
Лучше всего реализовать метод расширения, такой как
public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }
, а затем использовать его в любом месте решения с помощью
var copy = anyObject.DeepClone();
. Мы можем иметь следующие три реализации:
Все связанные методы хорошо работают и были глубоко протестированы.
Ну, у меня были проблемы с использованием ICloneable в Silverlight, но мне понравилась идея серализации, я могу серализовать XML, поэтому я сделал это:
static public class SerializeHelper
{
//Michael White, Holly Springs Consulting, 2009
//michael@hollyspringsconsulting.com
public static T DeserializeXML<T>(string xmlData) where T:new()
{
if (string.IsNullOrEmpty(xmlData))
return default(T);
TextReader tr = new StringReader(xmlData);
T DocItms = new T();
XmlSerializer xms = new XmlSerializer(DocItms.GetType());
DocItms = (T)xms.Deserialize(tr);
return DocItms == null ? default(T) : DocItms;
}
public static string SeralizeObjectToXML<T>(T xmlObject)
{
StringBuilder sbTR = new StringBuilder();
XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
XmlWriterSettings xwsTR = new XmlWriterSettings();
XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
xmsTR.Serialize(xmwTR,xmlObject);
return sbTR.ToString();
}
public static T CloneObject<T>(T objClone) where T:new()
{
string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);
return SerializeHelper.DeserializeXML<T>(GetString);
}
}
Я предпочитаю конструктор копирования клону. Цель понятна.
Если вы уже используете стороннее приложение, например ValueInjecter или Automapper , вы можете сделать что-то вроде этого:
MyObject oldObj; // The existing object to clone
MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax
Использование этот метод вам не нужно реализовывать ISerializable или ICloneable на ваших объектах. Это характерно для шаблона MVC / MVVM, поэтому были созданы такие простые инструменты, как это.
см. решение для глубокого клонирования значения инжектора на CodePlex .