Как создать объект со свойствами другого, но не равным? [Дубликат]

Если вы хотите использовать jQuery, он имеет хороший пример в своей документации:

 $.each([ 52, 97 ], function( index, value ) {
      alert( index + ": " + value );
 });
1844
задан poke 16 December 2015 в 10:42
поделиться

30 ответов

Хотя стандартная практика заключается в реализации интерфейса 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);
}
1478
ответ дан 19 revs, 12 users 65% 25 August 2018 в 21:59
поделиться
  1. В основном вам необходимо реализовать интерфейс ICloneable, а затем реализовать копирование структуры объектов.
  2. Если это глубокая копия всех членов, вам необходимо застраховать (не относясь к выбранному решению), что все дети также клонируются.
  3. Иногда вам нужно знать какое-то ограничение во время этого процесса, например, если вы копируете объекты ORM, большинство фреймворков разрешают только один объект, прикрепленный к сеансу, и вы НЕ ДОЛЖНЫ делать клоны этого объекта или если возможно, вам нужно заботиться о сеансе прикрепления этих объектов.

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

15
ответ дан Christian Davén 25 August 2018 в 21:59
поделиться

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

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

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

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

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

yourClass newLine = oldLine.DeepCopy();

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

3
ответ дан Chtiwi Malek 25 August 2018 в 21:59
поделиться

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

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

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

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

15
ответ дан Community 25 August 2018 в 21:59
поделиться

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

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

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

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

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

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

И метод ...

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

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

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

  • 1.08 секунд для Nested MemberwiseClone на вложенных структурах
  • 4.77 секунд для вложенных MemberwiseClone на вложенных классах
  • 39.93 секунд для сериализации / десериализации

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

7
ответ дан Contango 25 August 2018 в 21:59
поделиться

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

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}
185
ответ дан craastad 25 August 2018 в 21:59
поделиться

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

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();
}
5
ответ дан Daniele D. 25 August 2018 в 21:59
поделиться

Реализация глубокой копии:

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;
}
8
ответ дан dougajmcdonald 25 August 2018 в 21:59
поделиться

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

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

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

28
ответ дан Hakam Fostok 25 August 2018 в 21:59
поделиться

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

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

7
ответ дан HappyDude 25 August 2018 в 21:59
поделиться

Я создал версию принятого ответа, которая работает как с [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;
    }

} 
3
ответ дан Jeroen Ritmeijer 25 August 2018 в 21:59
поделиться

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

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

6
ответ дан JerryGoyal 25 August 2018 в 21:59
поделиться

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

Для более подробное объяснение о клонировании с использованием ICloneable, проверьте в этой статье .

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

См. Статью Developer's Corner для нескольких больше вариантов (кредит для Ян).

20
ответ дан Johann 25 August 2018 в 21:59
поделиться

Поскольку я не мог найти клонера, который отвечает всем моим требованиям в разных проектах, я создал глубокий клонер, который можно настроить и адаптировать к различным структурам кода вместо того, чтобы адаптировать мой код для удовлетворения требований клонеров. Это достигается путем добавления аннотаций к коду, который должен быть клонирован, или вы просто оставляете код так, как будто он имеет поведение по умолчанию. Он использует отражение, тип кэша и основан на 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
7
ответ дан kalisohn 25 August 2018 в 21:59
поделиться

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

Если у вас есть что-то, что можно скопировать, добавьте их

5
ответ дан LuckyLikey 25 August 2018 в 21:59
поделиться

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

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

Вместо того, чтобы писать свои собственные методы 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 [] массивы
  • Пользовательские классы и структуры, включая общие классы и структуры.

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

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

Насколько это быстро?

Решение быстрее, чем отражение, потому что информация членов должна быть собирается только один раз, прежде чем 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>?

25
ответ дан MarcinJuraszek 25 August 2018 в 21:59
поделиться

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

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

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

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

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

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

Г-н Venkat Subramaniam (избыточная ссылка здесь) подробно объясняет, почему .

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

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

Копирование объекта с помощью указание 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

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

84
ответ дан Michael Freidgeim 25 August 2018 в 21:59
поделиться

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

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

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

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

15
ответ дан Michael Sander 25 August 2018 в 21:59
поделиться

Выполните следующие действия:

  • Определите 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.

4
ответ дан Mifeet 25 August 2018 в 21:59
поделиться

Я придумал это, чтобы преодолеть недостаток .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();
        }
    }
}
68
ответ дан Peter Mortensen 25 August 2018 в 21:59
поделиться

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

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

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

149
ответ дан Ryan Lundy 25 August 2018 в 21:59
поделиться

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

Держите вещи простыми и используйте 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();
26
ответ дан Stacked 25 August 2018 в 21:59
поделиться

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

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it
4
ответ дан Sudhanva Kotabagi 25 August 2018 в 21:59
поделиться

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

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

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

5
ответ дан Toxantron 25 August 2018 в 21:59
поделиться

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

7
ответ дан xr280xr 25 August 2018 в 21:59
поделиться

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

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

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

var copy = anyObject.DeepClone();

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

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

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

15
ответ дан Community 25 August 2018 в 21:59
поделиться

Ну, у меня были проблемы с использованием 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);
    }
}
28
ответ дан Hakam Fostok 25 August 2018 в 21:59
поделиться

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

68
ответ дан Peter Mortensen 25 August 2018 в 21:59
поделиться

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

MyObject oldObj; // The existing object to clone

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

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

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

26
ответ дан Stacked 25 August 2018 в 21:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: