c# клонирование - Объекты глубокого клонирования



объектов глубокое (25)

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

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

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

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

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


Answers

Я думаю, вы можете попробовать это.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it

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

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


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

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


Мне нужен клонер для очень простых объектов в основном примитивов и списков. Если ваш объект несовместим с сериализуемым 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);
    }
}

Ну, у меня были проблемы с использованием ICloneable в Silverlight, но мне понравилась идея серализации, я могу серализовать XML, поэтому я сделал это:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //[email protected]
    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);
    }
}

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

Простой метод расширения для копирования всех общедоступных свойств. Работает для любых объектов и не требует, чтобы класс был [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 ) } );
        }
    };
}

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

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

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


Я только что создал CloneExtensions библиотеки CloneExtensions . Он выполняет быстрый, глубокий клон, используя простые операции присваивания, сгенерированные компиляцией кода времени выполнения выражения.

Как это использовать?

Вместо того, чтобы писать собственные методы Clone или Copy с тоном присвоений между полями и свойствами, сделайте программу для себя, используя дерево выражений. GetClone<T>() помеченный как метод расширения, позволяет просто вызвать его в вашем экземпляре:

var newInstance = source.GetClone();

Вы можете выбрать, что должно быть скопировано из source в newInstance используя перечисление CloningFlags :

var newInstance 
    = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

Что можно клонировать?

  • Примитивные (int, uint, byte, double, char и т. Д.), Известные неизменные типы (DateTime, TimeSpan, String) и делегаты (включая Action, Func и т. Д.),
  • Nullable
  • T [] массивы
  • Пользовательские классы и структуры, включая общие классы и структуры.

Следующие члены класса / структуры клонируются внутри страны:

  • Значения общедоступных, а не только полей
  • Значения публичных свойств с помощью как получателей, так и набора
  • Элементы коллекции для типов, реализующих ICollection

Как быстро это происходит?

Решение быстрее, чем отражение, потому что информация членов должна собираться только один раз, прежде чем GetClone<T> будет использоваться впервые для данного типа T

Это также быстрее, чем решение на основе сериализации, когда вы клонируете более чем пару экземпляров того же типа T

и больше...

Подробнее о сгенерированных выражениях в documentation .

Пример описания отладки выражения для 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> ?


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

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

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

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

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

yourClass newLine = oldLine.DeepCopy();

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


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

если вы будете кэшировать его правильно, то он будет клонировать 1000000 объектов на 4,6 с (измеряется 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);
}

полная проверка кода в моем сообщении в другом ответе

https://.com/a/34365709/4711853


Здесь решение быстро и просто, что сработало для меня без передачи на сериализацию / десериализацию.

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

Короткий ответ: вы наследуете интерфейс ICloneable, а затем реализуете функцию .clone. Clone должен делать копию по порядку и выполнять глубокую копию на любом члене, который ее требует, а затем возвращать результирующий объект. Это рекурсивная операция (требуется, чтобы все члены класса, к которому вы хотите клонировать, являются либо типами значений, либо реализуют ICloneable, а их члены являются либо типами значений, либо реализуют ICloneable и т. Д.).

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

Длительный ответ - «это зависит». Как упоминалось другими, ICloneable не поддерживается дженериками, требует особых соображений для круговых ссылок на классы и фактически рассматривается некоторыми как «ошибка» в .NET Framework. Метод сериализации зависит от ваших сериализуемых объектов, которых они могут не быть, и вы не можете контролировать. В обществе по-прежнему много дискуссий, которые являются «лучшей» практикой. На самом деле, ни одно из решений не является одним из самых подходящих для всех ситуаций, таких как ICloneable, изначально было истолковано.

См. Статью «Уголок разработчика» для нескольких дополнительных опций (кредит для Ян).


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

MyObject oldObj; // The existing object to clone

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

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

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


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


В общем, вы реализуете интерфейс ICloneable и реализуете Clone самостоятельно. Объекты C # имеют встроенный метод MemberwiseClone, который выполняет мелкую копию, которая может помочь вам выполнить все примитивы.

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


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

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

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

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


  1. В основном вам необходимо реализовать интерфейс ICloneable, а затем реализовать копирование структуры объектов.
  2. Если это глубокая копия всех членов, вам необходимо застраховать (не относясь к выбранному решению), что все дети также клонируются.
  3. Иногда вам нужно знать о некоторых ограничениях во время этого процесса, например, если вы копируете объекты ORM, большинство фреймворков разрешают только один объект, прикрепленный к сеансу, и вы НЕ ДОЛЖНЫ делать клоны этого объекта, или если это возможно, вам нужно ухаживать о сеансе прикрепления этих объектов.

Приветствия.


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

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


После многого прочтения многих опций, связанных здесь, и возможных решений этой проблемы, я считаю, что все варианты очень хорошо описаны на ссылке 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]

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


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

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


Хотя стандартная практика заключается в том, чтобы реализовать интерфейс 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 T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

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

var copy = anyObject.DeepClone();

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

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

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


Если ваше дерево объектов является 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;
}

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

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


Глубокое копирование объектов в JavaScript (я думаю, самый лучший и самый простой)

1. Использование JSON.parse (JSON.stringify (объект));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2. Использование созданного метода

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Использование Lo-Dash's _.cloneDeep ссылки lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Использование метода Object.assign ()

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

НО НЕПРАВИЛЬНО КОГДА

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5.Using Underscore.js _.clone link Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

НО НЕПРАВИЛЬНО КОГДА

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

Ссылка medium.com

JSBEN.CH Performance Benchmarking Игровая площадка 1 ~ 3 http://jsben.ch/KVQLd





c# .net clone