присваивание - поверхностное и глубокое копирование c#




Объекты глубокого клонирования (20)

Генератор кода

Мы видели много идей от сериализации по сравнению с ручной реализацией до отражения, и я хочу предложить совершенно другой подход с использованием генератора кода CGbR . Метод сгенерированного клона - это эффективная память и процессор, а в 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;
    }
}

Примечание. В последней версии есть больше null-проверок, но я оставил их для лучшего понимания.

Я хочу сделать что-то вроде:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

А затем внесите изменения в новый объект, который не отражен в исходном объекте.

Мне часто не нужна эта функциональность, поэтому, когда это было необходимо, я прибегал к созданию нового объекта, а затем копировал каждое свойство по отдельности, но это всегда оставляет мне ощущение, что есть лучший или более элегантный способ обработки ситуация.

Как я могу клонировать или глубоко копировать объект, чтобы клонированный объект мог быть изменен без каких-либо изменений, отражаемых в исходном объекте?


В. Почему я должен выбрать этот ответ?

  • Выберите этот ответ, если вы хотите, чтобы максимальная скорость, на которую способен .NET.
  • Игнорируйте этот ответ, если хотите действительно простой способ клонирования.

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

10 раз быстрее, чем другие методы

Следующий способ выполнения глубокого клона:

  • В 10 раз быстрее, чем что-либо, что предполагает сериализацию / десериализацию;
  • Довольно чертовски близко к теоретической максимальной скорости, на которую способен .NET.

И метод ...

Для максимальной скорости вы можете использовать Nested MemberwiseClone, чтобы сделать глубокую копию . Его почти такая же скорость, как и копирование структуры значений, и намного быстрее, чем (а) отражение или (б) сериализация (как описано в других ответах на этой странице).

Обратите внимание: если вы используете Nested MemberwiseClone для глубокой копии , вам необходимо вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все указанные методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демо-код ниже.

Вот результат кода, показывающий относительную разницу в производительности для 100 000 клонов:

  • 1.08 секунд для вложенных MemberwiseClone для вложенных структур
  • 4.77 секунд для вложенного члена по умолчанию для вложенных классов
  • 39,93 секунды для сериализации / десериализации

Использование Nested MemberwiseClone в классе почти так же быстро, как копирование структуры, а копирование структуры довольно близко к теоретической максимальной скорости, на которую способен .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

Чтобы понять, как сделать глубокую копию с помощью UserwiseCopy, вот демонстрационный проект, который использовался для генерации времени выше:

// 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();
}

Обратите внимание, что если вы используете Nested MemberwiseClone для глубокой копии , вам необходимо вручную реализовать ShallowCopy для каждого вложенного уровня в классе и DeepCopy, который вызывает все упомянутые методы ShallowCopy для создания полного клона. Это просто: всего несколько строк, см. Демо-код выше.

Типы значений и ссылки Типы

Обратите внимание, что когда дело доходит до клонирования объекта, существует большая разница между « структурой » и « классом »:

  • Если у вас есть « структура », это тип значения, поэтому вы можете просто скопировать его, и содержимое будет клонировано (но оно будет делать только мелкий клон, если вы не используете методы в этом сообщении).
  • Если у вас есть « класс », это ссылочный тип , поэтому, если вы его скопируете, все, что вы делаете, это копирование указателя на него. Чтобы создать настоящий клон, вы должны быть более креативными и использовать различия между типами значений и типами ссылок, которые создают другую копию исходного объекта в памяти.

См. Различия между типами значений и типами ссылок .

Контрольные суммы для помощи в отладке

  • Неправильное клонирование объектов может привести к очень сложным ошибкам. В производственном коде я стараюсь реализовать контрольную сумму, чтобы дважды проверить, что объект был клонирован правильно и не был поврежден другой ссылкой на него. Эта контрольная сумма может быть отключена в режиме деблокирования.
  • Я считаю этот метод весьма полезным: часто вы хотите клонировать части объекта, а не все.

Действительно полезен для развязки многих потоков из многих других потоков

Одним из превосходных вариантов использования этого кода является подача клонов вложенного класса или структуры в очередь, для реализации шаблона производителя / потребителя.

  • У нас может быть один (или более) поток, изменяющий класс, который у них есть, а затем нажатие полной копии этого класса в ConcurrentQueue.
  • Затем мы имеем одну (или более) нити, вытягивающие копии этих классов и имеющие дело с ними.

Это на практике очень хорошо работает и позволяет отделить множество потоков (производителей) от одной или нескольких потоков (потребителей).

И этот метод также близок: если мы используем вложенные структуры, это на 35 раз быстрее, чем сериализация / десериализация вложенных классов и позволяет нам использовать все потоки, доступные на машине.

Обновить

По-видимому, ExpressMapper так же быстро, если не быстрее, чем ручное кодирование, например, выше. Возможно, мне придется посмотреть, как они сравниваются с профилировщиком.


Если вы уже используете стороннее приложение, такое как ValueInjecter или Automapper , вы можете сделать что-то вроде этого:

MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

Используя этот метод, вам не нужно реализовывать ISerializable или ICloneable на ваших объектах. Это характерно для шаблона MVC / MVVM, поэтому были созданы простые инструменты, подобные этому.

см. решение глубокого клонирования значения инжектора на CodePlex .


Если вы хотите, чтобы истинное клонирование было неизвестным, вы можете взглянуть на fastclone .

Это клонирование на основе выражений, работающее примерно в 10 раз быстрее, чем двоичная сериализация и сохранение целостности целостности объекта.

Это означает: если вы ссылаетесь несколько раз на один и тот же объект в своем иерархии, клон также будет иметь один экземпляр, на который ссылаются.

Нет необходимости в интерфейсах, атрибутах или любой другой модификации клонируемых объектов.


Лучше всего реализовать метод расширения, например

public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

а затем использовать его в любом месте решения путем

var copy = anyObject.DeepClone();

Мы можем иметь следующие три реализации:

  1. Сериализация (самый короткий код)
  2. По рефлексии - на 5 раз быстрее
  3. По деревьям выражений - в 20 раз быстрее

Все связанные методы хорошо работают и прошли глубокую проверку.


Мне нужен клонер для очень простых объектов в основном примитивов и списков. Если ваш объект несовместим с сериализуемым JSON, этот метод будет делать трюк. Это не требует модификации или реализации интерфейсов в клонированном классе, просто JSON-сериализаторе, таком как JSON.NET.

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

Кроме того, вы можете использовать этот метод расширения

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}

После многого прочтения многих опций, связанных здесь, и возможных решений этой проблемы, я считаю, что все варианты очень хорошо описаны на ссылке Ian P (все остальные варианты - это варианты), и лучшее решение обеспечивается agiledeveloper.com/articles/cloning072002.htm на комментарий к вопросу.

Поэтому я просто скопирую соответствующие части этих двух ссылок здесь. Таким образом, мы можем:

Лучшее, что можно сделать для клонирования объектов в c sharp!

Прежде всего, это все наши варианты:

В статье «Быстрая глубокая копия по деревьям выражений » также сравнивается производительность клонирования по деревьям сериализации, отражения и выражений.

Почему я выбираю ICloneable (т.е. вручную)

agiledeveloper.com/articles/cloning072002.htm .

Все его статьи вокруг примера, который пытается быть применимым для большинства случаев, использует 3 объекта: Person , Brain и City . Мы хотим клонировать человека, который будет иметь свой собственный мозг, но тот же город. Вы можете либо просмотреть все проблемы, какие из приведенных выше методов могут принести или прочитать статью.

Это моя слегка измененная версия его вывода:

Копирование объекта путем указания 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);
    }
    …
}

Теперь рассмотрим вопрос о том, какой класс принадлежит Лицу.

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 [email protected]
This is person with [email protected]
SkilledPerson: This is person with [email protected]
SkilledPerson: This is person with [email protected]

Обратите внимание, что если мы храним счетчик количества объектов, клон, реализованный здесь, будет содержать правильный подсчет количества объектов.


Причина не использовать ICloneable не потому, что у нее нет общего интерфейса. Причина не в том, чтобы использовать его, потому что это расплывчато . Он не дает понять, получаете ли вы мелкую или глубокую копию; это зависит от исполнителя.

Да, MemberwiseClone делает мелкую копию, но противоположность MemberwiseClone не является Clone ; это, возможно, DeepClone , которого не существует. Когда вы используете объект через свой интерфейс ICloneable, вы не можете знать, какой тип клонирования выполняет базовый объект. (И комментарии XML не станут ясными, потому что вы получите комментарии к интерфейсу, а не те, которые находятся в методе Clone объекта.)

То, что я обычно делаю, это просто сделать метод Copy который делает именно то, что я хочу.


Хотя стандартная практика заключается в том, чтобы реализовать интерфейс ICloneable (описанный here , поэтому я не начну срыгивать), вот хороший клон для глубокого клонирования объектов, который я нашел в Code Project некоторое время назад и включил его в наш материал.

Как упоминалось в других разделах, это требует, чтобы ваши объекты были сериализуемыми.

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 для этого, он должен быть легче и избегать накладных расходов на теги [Serializable]. ( 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);
}

Я предпочитаю копировать конструктор клон. Целью яснее.


Вот глубокая реализация:

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;
}

быть информированным о том, что это решение довольно просто, но это не так хорошо, как другие решения.

И убедитесь, что если класс вырастет, все равно будут клонированы только те поля, которые также будут сериализованы.


Мне нравится 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));
        }
    }

Если у вас есть больше вещей, чтобы скопировать их добавить


Следуй этим шагам:

  • Определите свойство « 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.


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

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

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

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

то для выполнения глубокой независимой копии просто вызовите метод DeepCopy:

yourClass newLine = oldLine.DeepCopy();

надеюсь это поможет.


Этот метод решил проблему для меня:

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 самостоятельно. Объекты C # имеют встроенный метод MemberwiseClone, который выполняет мелкую копию, которая может помочь вам выполнить все примитивы.

Для глубокой копии нет способа узнать, как автоматически это сделать.


Держите вещи простыми и используйте 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();

Я видел, как это реализовано и через отражение. В основном существует метод, который будет проходить через элементы объекта и соответствующим образом копировать их в новый объект. Когда он достиг ссылочных типов или коллекций, я думаю, что он сделал рекурсивный вызов сам по себе. Отражение дорого, но все получилось очень хорошо.


Я придумал это, чтобы преодолеть недостаток .NET , чтобы вручную скопировать List <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, который делает это, но это невозможно, из-за того, что выход не работает внутри блоков анонимного метода.

Еще лучше использовать общий список List <T>:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}






clone