Как клонировать объекты - PullRequest
49 голосов
/ 19 марта 2011

Когда я делаю следующее ... все, что делается для Персоны b, модифицирует Персона a (я думал, что это приведет к клонированию Персоны b от Персоны a). Я также понятия не имею, если смена Person a изменит Person b после связывания. Из-за моего кода сейчас я вижу это только в 1 направлении.

Person a = new Person() { head = "big", feet = "small" };
Person b = a; 

b.head = "small"; //now a.head = "small" too   

Теперь, если я сделаю это вместо этого ... Персона а станет совершенно отдельной.

Person b = new Person() { head = a.head, feet = a.feet };

Теперь это прекрасно и имеет смысл, если сравнивать это поведение с другими вещами в C #. НО, это может стать очень раздражающим с большими объектами.

Есть ли способ, чтобы сократить это вообще?

Например:

Person b = a.Values;

Ответы [ 11 ]

62 голосов
/ 19 марта 2011

То, что вы ищете, это клонирование.Вам нужно будет реализовать 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 для выполнения клонирования.

38 голосов
/ 19 марта 2011

Есть ли способ это вообще сделать?

Нет, не совсем. Вам нужно будет создать новый экземпляр, чтобы избежать влияния оригинала на «копию». Для этого есть несколько вариантов:

  1. Если ваш тип struct, а не class, он будет скопирован по значению (вместо простого копирования ссылки на экземпляр). Это даст вам семантику, которую вы описываете, но имеет много других побочных эффектов, которые, как правило, меньше, чем желательно, и не рекомендуется для любого изменяемого типа (что, очевидно, является, или это не будет проблемой!)

  2. Реализовать механизм "клонирования" для ваших типов. Это может быть ICloneable или даже просто конструктор, который принимает экземпляр и копирует из него значения.

  3. Используйте отражение, MemberwiseClone или подобное для копирования всех значений, поэтому вам не нужно писать код для этого. Это имеет потенциальные проблемы, особенно если у вас есть поля, содержащие непростые типы.

14 голосов
/ 16 июля 2014

Я использую AutoMapper для этого. Это работает так:

Mapper.CreateMap(typeof(Person), typeof(Person));
Mapper.Map(a, b);

Теперь человек a обладает всеми свойствами человека b.

Помимо этого, AutoMapper работает и для разных объектов. Для получения дополнительной информации, проверьте его на http://automapper.org

Обновление: сейчас я использую этот синтаксис (упрощенно - на самом деле CreateMaps находятся в профилях AutoMapper):

Mapper.CreateMap<Person, Person>;
Mapper.Map(a, b);

Обратите внимание, что вам не нужно создавать CreateMap для сопоставления одного объекта того же типа с другим, но если вы этого не сделаете, AutoMapper создаст мелкую копию, что означает для непрофессионала, что если вы измените один объект , другие изменения также.

13 голосов
/ 23 сентября 2016

Поскольку метод 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();
7 голосов
/ 25 апреля 2014

Чтобы клонировать ваш объект класса, вы можете использовать метод Object.MemberwiseClone,

, просто добавьте эту функцию в ваш класс:

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

, затем, чтобы выполнить глубокую независимую копию, просто вызовитеметод DeepCopy:

yourClass newLine = oldLine.DeepCopy();
6 голосов
/ 19 марта 2011

a и b - это просто две ссылки на один и тот же объект Person.Оба они по существу содержат адрес Person.

. Существует интерфейс ICloneable , хотя его поддерживают относительно немного классов.При этом вы должны написать:

Person b = a.Clone();

Тогда b будет совершенно отдельным Person.

Вы также можете реализовать конструктор копирования:

public Person(Person src)
{
  // ... 
}

Нет встроенного способа скопировать все поля.Вы можете сделать это с помощью рефлексии, но это приведет к снижению производительности.

2 голосов
/ 21 февраля 2017

Безболезненно: используя NClone библиотеку

Person a = new Person() { head = "big", feet = "small" };
Person b = Clone.ObjectGraph(a); 
1 голос
/ 20 февраля 2015
  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;
  }
0 голосов
/ 24 октября 2018

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();
0 голосов
/ 08 января 2014

Вы можете сделать это так:

var jss = new JavaScriptSerializer();
var b = jss.Deserialize<Person>(jss.Serialize(a));

Для глубокого клонирования вы можете взглянуть на этот ответ: https://stackoverflow.com/a/78612/550975

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...