Как клонировать объекты
Когда я делаю следующее: все, что делается с Person b, изменяет Person a (я думал, что это будет клонировать Person b от Person a). У меня также нет идеи, если смена Person a изменит Person b после ссылки. Из-за моего кода прямо сейчас, я могу видеть это только в одном направлении.
Person a = new Person() { head = "big", feet = "small" };
Person b = a;
b.head = "small"; //now a.head = "small" too
Теперь, если я сделаю это вместо этого. Человек a станет полностью отдельным.
Person b = new Person() { head = a.head, feet = a.feet };
Теперь это прекрасно и любопытно имеет смысл при сравнении этого поведения с другими вещами на С#. НО, это может сильно раздражать большие объекты.
Есть ли способ сократить это вообще?
Например:
Person b = a.Values;
Ответы
Ответ 1
Есть ли способ сократить это вообще?
Нет, не совсем. Вам нужно будет создать новый экземпляр, чтобы исходный текст не влиял на "копию". Для этого есть несколько вариантов:
-
Если ваш тип struct
, а не class
, он будет скопирован по значению (вместо простого копирования ссылки на экземпляр). Это даст вам семантику, которую вы описываете, но имеет много других побочных эффектов, которые, как правило, менее желательны и не рекомендуются для какого-либо изменяемого типа (что, очевидно, это или это не будет проблемой!)
-
Внедрите механизм "клонирования" для ваших типов. Это может быть ICloneable
или даже просто конструктор, который принимает экземпляр и копирует значения из него.
-
Использовать отражение, MemberwiseClone или аналогично копировать все значения, поэтому вам не нужно писать код для этого. У этого есть потенциальные проблемы, особенно если у вас есть поля, содержащие непростые типы.
Ответ 2
То, что вы ищете, - это клонирование. Вам нужно будет выполнить IClonable, а затем выполнить клонирование.
Пример:
class Person() : ICloneable
{
public string head;
public string feet;
#region ICloneable Members
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
Затем вы можете просто вызвать метод Clone для создания ShallowCopy (в этом конкретном случае также DeepCopy)
Person a = new Person() { head = "big", feet = "small" };
Person b = (Person) a.Clone();
Вы можете использовать метод MemberwiseClone класса Object для клонирования.
Ответ 3
Я использую AutoMapper для этого. Это работает так:
Mapper.CreateMap(typeof(Person), typeof(Person));
Mapper.Map(a, b);
Теперь человек а обладает всеми свойствами человека б.
Кроме того, AutoMapper работает и для разных объектов. Для получения дополнительной информации, проверьте это на http://automapper.org
Обновление: сейчас я использую этот синтаксис (упрощенно - на самом деле CreateMaps находятся в профилях AutoMapper):
Mapper.CreateMap<Person, Person>;
Mapper.Map(a, b);
Обратите внимание, что вам не нужно создавать CreateMap для сопоставления одного объекта того же типа с другим, но если вы этого не сделаете, AutoMapper создаст мелкую копию, что означает для непрофессионала, что если вы измените один объект, другой меняется также.
Ответ 4
Так как метод MemberwiseClone() не является общедоступным, я создал этот простой метод расширения, чтобы упростить клонирование объектов:
public static T Clone<T>(this T obj)
{
var inst = obj.GetType().GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
return (T)inst?.Invoke(obj, null);
}
Использование:
var clone = myObject.Clone();
Ответ 5
Чтобы клонировать объект класса, вы можете использовать метод Object.MemberwiseClone,
просто добавьте эту функцию в свой класс:
public class yourClass
{
// ...
// ...
public yourClass DeepCopy()
{
yourClass othercopy = (yourClass)this.MemberwiseClone();
return othercopy;
}
}
чтобы выполнить глубокую независимую копию, просто вызовите метод DeepCopy:
yourClass newLine = oldLine.DeepCopy();
Ответ 6
a
и b
- это просто две ссылки на один и тот же объект Person. Они оба по существу держат адрес Person
.
Существует интерфейс ICloneable, хотя относительно немногие классы поддерживают его. С этим вы бы написали:
Person b = a.Clone();
Тогда б был бы совершенно отдельным Person
.
Вы также можете реализовать конструктор копирования:
public Person(Person src)
{
// ...
}
Нет встроенного способа скопировать все поля. Вы можете сделать это с помощью рефлексии, но это приведет к снижению производительности.
Ответ 7
Безболезненно: использование библиотеки NClone
Person a = new Person() { head = "big", feet = "small" };
Person b = Clone.ObjectGraph(a);
Ответ 8
public static T Clone<T>(T obj)
{
DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());
MemoryStream memoryStream = new MemoryStream();
dcSer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
T newObject = (T)dcSer.ReadObject(memoryStream);
return newObject;
}
Ответ 9
Вы можете сделать это следующим образом:
var jss = new JavaScriptSerializer();
var b = jss.Deserialize<Person>(jss.Serialize(a));
Для глубокого клонирования вы можете взглянуть на этот ответ:
fooobar.com/questions/594/...
Ответ 10
MemberwiseClone
- это хороший способ сделать поверхностную копию, как предлагали другие. Однако он защищен, поэтому, если вы хотите использовать его без изменения класса, вы должны получить к нему доступ через отражение. Отражение однако медленное. Так что, если вы планируете клонировать много объектов, возможно, стоит кешировать результат:
public static class CloneUtil<T>
{
private static readonly Func<T, object> clone;
static CloneUtil()
{
var cloneMethod = typeof(T).GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
clone = (Func<T, object>)cloneMethod.CreateDelegate(typeof(Func<T, object>));
}
public static T ShallowClone(T obj) => (T)clone(obj);
}
public static class CloneUtil
{
public static T ShallowClone<T>(this T obj) => CloneUtil<T>.ShallowClone(obj);
}
Вы можете назвать это так:
Person b = a.ShallowClone();
Ответ 11
Это происходит потому, что "Person" - это класс, поэтому он передается по ссылке.
В заявлении "b = a" вы просто копируете ссылку на экземпляр "Person", который вы создали с ключевым словом new.
Самый простой способ иметь поведение, которое вы ищете, - использовать "тип значения".
Просто измените объявление Person из
class Person
к
struct Person