Głębokie klonowanie obiektów

Chcę zrobić coś takiego:

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

A następnie dokonać zmian w nowym obiekcie, które nie są odzwierciedlone w oryginalnym obiekcie.

Często nie potrzebuję tej funkcjonalności, więc kiedy było to konieczne, uciekam się do tworzenia nowego obiektu, a następnie kopiowania każdej właściwości indywidualnie, ale zawsze pozostawia mi to poczucie, że istnieje lepszy lub bardziej elegancki sposób radzenia sobie z sytuacją.

Jak mogę sklonować lub głęboko skopiować obiekt, aby sklonowany obiekt mógł być modyfikowane bez żadnych zmian odzwierciedlonych w oryginalnym obiekcie?

 1883
Author: poke, 2008-09-17

30 answers

Podczas gdy standardową praktyką jest wdrażanie ICloneable interfejs (opisany tutaj, więc nie będę się regurgitate), oto ładny kopiarka deep Clone object, którą znalazłem na projekt Code jakiś czas temu i włączyłem go do naszych rzeczy.

Jak wspomniano w innym miejscu, wymaga, aby twoje obiekty były serializowane.

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

Chodzi o to, że serializuje Twój obiekt, a następnie deserializuje go w świeży obiekt. Zaletą jest to, że nie musisz się martwić siebie o klonowaniu wszystkiego, gdy obiekt staje się zbyt złożony.

I przy użyciu metod rozszerzeń (również z pierwotnie odwołanego źródła):

Jeśli wolisz używać nowych metod rozszerzeń W C# 3.0, zmień metodę, aby miała następujący podpis:

public static T Clone<T>(this T source)
{
   //...
}

Teraz wywołanie metody staje się po prostu objectBeingCloned.Clone();.

EDIT (10 stycznia 2015) pomyślałem, że wrócę do tego, aby wspomnieć, że niedawno zacząłem używać (Newtonsoft) Json do to, to powinno być lżejsze i pozwala uniknąć napowietrznych tagów [Serializowalnych]. (NB @atconway wskazał w komentarzach, że członkowie prywatni nie są klonowani przy użyciu metody 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);
}
 1495
Author: johnc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:36

Chciałem klonera dla bardzo prostych obiektów, głównie prymitywów i list. Jeśli twój obiekt jest poza polem JSON serializable, to ta metoda załatwi sprawę. Nie wymaga to modyfikacji ani implementacji interfejsów na klonowanej klasie, tylko serializer JSON jak JSON.NET.

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

Możesz również użyć tej metody rozszerzenia

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}
 192
Author: craastad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-14 06:16:54

Powodem, aby nie używać ICloneable jest Nie , ponieważ nie ma ogólnego interfejsu. powodem, aby go nie używać, jest to, że jest niejasne . To nie wyjaśnia, czy otrzymujesz płytką czy głęboką kopię; To zależy od wykonawcy.

Tak, MemberwiseClone tworzy płytką kopię, ale przeciwieństwem MemberwiseClone nie jest Clone; Być może byłoby DeepClone, które nie istnieje. Kiedy używasz obiektu za pośrednictwem interfejsu ICloneable, nie możesz wiedzieć, jakiego rodzaju klonowanie wykonywanego obiektu. (A komentarze XML nie wyjaśnią tego jasno, ponieważ otrzymasz komentarze interfejsu, a nie te dotyczące metody klonowania obiektu.)

Zazwyczaj robię po prostu metodę Copy, która robi dokładnie to, co chcę.

 152
Author: Ryan Lundy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-07 18:37:12

Po wielu przeczytaniach na temat wielu opcji, które tu są połączone, i możliwych rozwiązań tego problemu, uważam, że wszystkie opcje są podsumowane dość dobrze naIan P'S link (wszystkie inne opcje są ich odmianami) i najlepsze rozwiązanie jest dostarczane przez Ian P . Pedro77 'S link w komentarzach do pytania.

Więc po prostu skopiuję odpowiednie części tych 2 odniesień tutaj. W ten sposób możemy mieć:

Najlepsza rzecz do klonowania obiektów w c sharp!

Przede wszystkim, to wszystkie nasze opcje:

Artykuł Fast Deep Copy by expression Trees zawiera również porównanie wydajności klonowania przez serializację, Reflection I expression Trees.

Dlaczego wybieram ICloneable (tj. ręcznie)

Pan Venkat Subramaniam (zbędny link tutaj) wyjaśnia bardzo szczegółowo dlaczego .

Cały jego artykuł krąży wokół przykładu, który stara się być stosowany w większości przypadków, używając 3 obiektów: osoba, mózg i Miasto . Chcemy sklonować osobę, która będzie miała własny mózg, ale to samo miasto. Możesz sobie wyobrazić wszystkie problemy, które każda z powyższych metod może przynieść lub przeczytać artykuł.

To moja lekko zmodyfikowana wersja jego wniosek:

Kopiowanie obiektu przez podanie New, po którym następuje nazwa klasy często prowadzi do kodu, który nie jest rozszerzalny. Lepszym sposobem na osiągnięcie tego jest użycie klonu, czyli zastosowania wzorca prototypu. Jednak używanie klonowania w C# (i Javie) może być również dość problematyczne. Lepiej jest dostarczyć chroniony (Niepubliczny) Konstruktor kopiujący i wywołać go z metody klonowania. Daje nam to możliwość delegowania zadania tworzenia obiektu do instancji samej klasy, zapewniając w ten sposób rozszerzalność, a także bezpieczne tworzenie obiektów za pomocą konstruktora protected copy.

Miejmy nadzieję, że ta implementacja wyjaśni sprawę:

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

Teraz rozważ posiadanie klasy wywodzącej się od osoby.

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

Możesz spróbować uruchomić następujący kod:

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

Wytworzony wynik będzie:

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

Zauważ, że jeśli zachowamy liczbę obiektów, klon zaimplementowany tutaj będzie zachowaj poprawną liczbę obiektów.

 90
Author: cregox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 20:30:46

Wolę Konstruktor kopiujący niż klon. Intencje są jaśniejsze.

 70
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-03 17:21:32

Prosta metoda rozszerzenia do kopiowania wszystkich właściwości publicznych. Działa dla dowolnych obiektów i nie wymaga , Aby klasa była [Serializable]. Może być rozszerzony na inny poziom dostępu.

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 ) } );
        }
    };
}
 35
Author: Konstantin Salavatov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-07 12:59:49

Cóż miałem problemy z używaniem ICloneable w Silverlight, ale spodobał mi się pomysł seralizacji, mogę seralizować XML, więc zrobiłem to:

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);
    }
}
 28
Author: Michael White,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-21 14:11:33

Jeśli używasz już aplikacji innej firmy, takiej jak ValueInjecter lub Automapper , możesz zrobić coś takiego:

MyObject oldObj; // The existing object to clone

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

Używając tej metody nie musisz implementować ISerializable lub Iclonable na swoich obiektach. Jest to wspólne z wzorcem MVC / MVVM, więc proste narzędzia takie jak ten zostały stworzone.

Zobacz rozwiązanie do głębokiego klonowania valueinjecter na CodePlex .

 26
Author: Michael Cox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-19 22:57:37

Właśnie stworzyłem CloneExtensions biblioteka projekt. Wykonuje szybkie, Głębokie klonowanie za pomocą prostych operacji przypisania generowanych przez kompilację kodu wykonawczego drzewa wyrażeń.

Jak go używać?

Zamiast pisać własne metody Clone lub Copy z tonem przypisań między polami i właściwościami zrób to sam, używając drzewa wyrażeń. GetClone<T>() metoda oznaczona jako extension metoda pozwala po prostu wywołać ją na Twoim instancja:

var newInstance = source.GetClone();

Możesz wybrać, co należy skopiować z source do newInstance używając CloningFlags enum:

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

Co można sklonować?

  • Primitive (int, uint, byte, double, char, itp.), znane pod nazwą typy (DateTime, TimeSpan, String) i delegaty (w tym Akcja, Func, etc)
  • Nullable
  • T[] tablice
  • własne klasy i struktury, w tym ogólne klasy i struktury.

Następnymi członkami klasy / struktury są sklonowany wewnętrznie:

  • wartości pól public, nie readonly
  • wartości właściwości publicznych z accesorami get I set
  • elementy kolekcji dla typów implementujących ICollection

Jak szybko?

Rozwiązanie jest szybsze niż odbicie, ponieważ informacje o członach muszą być zebrane tylko raz, zanim {[11] } zostanie użyty po raz pierwszy dla danego typu T.

Jest również szybszy niż rozwiązanie oparte na serializacji jeśli klonujesz więcej niż kilka instancji tego samego typu T.

I nie tylko...

Przeczytaj więcej o wygenerowanych wyrażeniach na dokumentacji .

Przykładowa lista debugowania wyrażenia dla 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:
}

}

Co ma takie samo znaczenie jak następujący kod 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;
}

Czy nie jest tak, jak napisałbyś własną metodę Clone dla List<int>?

 25
Author: MarcinJuraszek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-08 15:26:38

Krótka odpowiedź brzmi: dziedziczysz z interfejsu ICloneable, a następnie zaimplementujesz .funkcja klonowania. Klon powinien wykonać kopię memberwise i wykonać głęboką kopię na dowolnym elemencie, który tego wymaga, a następnie zwrócić wynikowy obiekt. Jest to operacja rekurencyjna (wymaga, aby wszystkie elementy klasy, które chcesz sklonować, były albo typami wartości, albo implementowały ICloneable, a ich elementy były albo typami wartości, albo implementowały ICloneable, itd.).

Dla bardziej szczegółowego wyjaśnienia na temat klonowania przy użyciu ICloneable, sprawdź Ten artykuł .

ODPOWIEDŹ długa brzmi "to zależy". Jak wspominali inni, ICloneable nie jest wspierany przez generyki, wymaga specjalnych rozważań dla odwołań do klas okrągłych i jest postrzegany przez niektórych jako"błąd" w.NET Framework. Metoda serializacji zależy od tego, czy Twoje obiekty są serializowalne, które mogą nie być i możesz nie mieć nad nimi kontroli. Jest jeszcze wiele dyskusji w społeczności na temat co jest "najlepszą" praktyką. W rzeczywistości żadne z rozwiązań nie jest uniwersalną najlepszą praktyką dla wszystkich sytuacji, jak pierwotnie zinterpretowano ICloneable.

Zobacz ten Developer ' s Corner Artykuł Aby uzyskać więcej opcji (kredyt dla Iana).

 20
Author: Zach Burlingame,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-09 22:46:07

Jeśli chcesz prawdziwego klonowania do nieznanych typów możesz spojrzeć na fastclone .

To klonowanie oparte na wyrażeniach działa około 10 razy szybciej niż serializacja binarna i zachowuje pełną integralność grafu obiektowego.

Oznacza to, że jeśli odwołasz się wielokrotnie do tego samego obiektu w hierachach, klon będzie miał również jedną instancję.

Nie ma potrzeby tworzenia interfejsów, atrybutów ani innych modyfikacji obiektów będących sklonowany.

 15
Author: Michael Sander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-12 20:07:36

Najlepiej zaimplementować metodę rozszerzenia Jak

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

A następnie użyj go w dowolnym miejscu roztworu przez

var copy = anyObject.DeepClone();

Możemy mieć następujące trzy implementacje:

  1. przez serializacja (najkrótszy kod)
  2. przez Reflection - 5x szybciej
  3. przez drzewa wyrazu - 20x szybciej

Wszystkie powiązane metody działają dobrze i były głęboko sprawdzone.

 15
Author: frakon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:19
  1. zasadniczo musisz zaimplementować interfejs ICloneable, a następnie zrealizować kopiowanie struktury obiektu.
  2. jeśli jest to głęboka Kopia wszystkich członków, musisz upewnić się (nie dotyczy wybranego rozwiązania), że wszystkie dzieci są również klonowalne.
  3. czasami musisz być świadomy pewnych ograniczeń podczas tego procesu, na przykład, jeśli kopiujesz obiekty ORM większość frameworków zezwala tylko na jeden obiekt dołączony do sesji i nie możesz tworzyć klonów tego obiektu, lub jeśli jest to możliwe, że musisz dbać o dołączanie sesji tych obiektów.
Zdrówko.
 15
Author: dimarzionist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-03 07:25:13

Wymyśliłem to, aby przezwyciężyć . Net niedociągnięcia związane z ręcznym kopiowaniem listy.

Używam tego:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

I w innym miejscu:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

Próbowałem wymyślić jeden z nich, który to robi, ale nie jest to możliwe, ze względu na brak pracy w blokach anonimowych metod.

Jeszcze lepiej, Użyj listy generycznej 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();
        }
    }
}
 10
Author: Daniel Mošmondor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-03 17:23:13

Zachowaj prostotę i używaj AutoMapper jak inni wspominali, jest to prosta mała biblioteka do mapowania jednego obiektu na drugi... Aby skopiować obiekt do innego o tym samym typie, wystarczy trzy linie kodu:

MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);

Obiekt docelowy jest teraz kopią obiektu źródłowego. Nie dość proste? Utwórz metodę rozszerzenia, która będzie używana wszędzie w Twoim rozwiązaniu:

public static T Copy<T>(this T source)
{
    T copy = default(T);
    Mapper.CreateMap<T, T>();
    copy = Mapper.Map<T, T>(source);
    return copy;
}

Używając metody rozszerzenia, trzy linie stają się jedną linią:

MyType copy = source.Copy();
 9
Author: Stacked,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-28 11:23:05

Ogólnie rzecz biorąc, zaimplementujesz interfejs ICloneable i zaimplementujesz Sam Klon. Obiekty C# mają wbudowaną metodę MemberwiseClone, która wykonuje płytką kopię, która może pomóc wszystkim prymitywnym.

Dla głębokiej kopii, nie ma możliwości, aby wiedział, jak to zrobić automatycznie.

 7
Author: HappyDude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 00:09:02

Widziałem to również poprzez refleksję. Zasadniczo istniała metoda, która powtarzała elementy składowe obiektu i odpowiednio kopiowała je do nowego obiektu. Kiedy osiągnął typy referencyjne lub Kolekcje, myślę, że zrobił rekurencyjne wywołanie na siebie. Odbicie jest drogie, ale działało całkiem nieźle.

 7
Author: xr280xr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-19 13:01:19

Oto implementacja głębokiej kopii:

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;
}
 7
Author: dougajmcdonald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-06 07:38:32

P. Dlaczego miałbym wybrać tę odpowiedź?

  • Wybierz tę odpowiedź, jeśli chcesz najszybszą prędkość. NET jest w stanie.
  • zignoruj tę odpowiedź, jeśli chcesz naprawdę, naprawdę łatwą metodę klonowania.

Innymi słowy, idź z inną odpowiedzią, chyba że masz wąskie gardło wydajności, które wymaga naprawy, i możesz to udowodnić za pomocą profilera.

10X szybciej niż inne metody

Następująca metoda wykonania klonu głębokiego jest:

  • 10x szybciej niż cokolwiek, co wiąże się z serializacją / deserializacją;
  • Całkiem blisko teoretycznej maksymalnej prędkości. NET.

I metody ...

Dla maksymalnej prędkości, możesz użyć zagnieżdżonego MemberwiseClone, aby wykonać głęboką kopię . Jego prędkość jest prawie taka sama jak kopiowanie struktury wartości i jest znacznie szybsza niż (a) odbicie lub (b) serializacja (jak opisano w innych odpowiedziach na tej stronie).

Zauważ, że jeśli używasz zagnieżdżonego MemberwiseClone do głębokiej kopii , musisz ręcznie zaimplementować ShallowCopy dla każdego zagnieżdżonego poziomu w klasie oraz DeepCopy, który wywołuje wszystkie wymienione metody ShallowCopy, aby utworzyć kompletny klon. To proste: w sumie tylko kilka linijek, zobacz poniższy kod demonstracyjny.

Oto wynik kodu pokazujący względną różnicę wydajności dla 100 000 klonów:

  • 1.08 sekundy dla zagnieżdżonego MemberwiseClone na zagnieżdżonym structs
  • 4.77 sekund dla zagnieżdżonego MemberwiseClone na zagnieżdżonych klasach
  • 39,93 sekundy na serializację / Deserializację

Używanie zagnieżdżonego MemberwiseClone na klasie prawie tak szybko jak kopiowanie struktury, a kopiowanie struktury jest dość bliskie teoretycznej maksymalnej prędkości. 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

Aby zrozumieć, jak zrobić głęboką kopię za pomocą Membisecopy, oto projekt demo, który został użyty do wygenerowania powyższych czasów:

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

Wtedy, wywołanie demo z main:

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

Ponownie zauważ, że jeśli używasz zagnieżdżonego MemberwiseClone do głębokiej kopii , musisz ręcznie zaimplementować ShallowCopy dla każdego zagnieżdżonego poziomu w klasie oraz DeepCopy, który wywołuje wszystkie wymienione metody ShallowCopy, aby utworzyć kompletny klon. Jest to proste: w sumie tylko kilka linijek, zobacz powyższy kod demo.

Typy wartości a typy odwołań

Zauważ, że jeśli chodzi o klonowanie obiektu, jest duża różnica między klasą" struct "a klasą" ":

  • Jeśli masz " struct ", jest to typ wartości , więc możesz go po prostu skopiować ,a zawartość zostanie sklonowana (ale będzie to tylko płytki klon, chyba że użyjesz technik w tym poście).
  • Jeśli masz klasę " ", jest to typ odniesienia , więc jeśli ją skopiujesz, jedyne co robisz, to kopiujesz wskaźnik do niej. Aby stworzyć prawdziwego klona, trzeba być bardziej kreatywnym i użyj różnic między typami wartości i typami referencji , które tworzą kolejną kopię oryginalnego obiektu w pamięci.

Zobacz różnice między typami wartości i typami referencji .

Sumy kontrolne wspomagające debugowanie

    Niepoprawne klonowanie obiektów może prowadzić do bardzo trudnych do zidentyfikowania błędów. W kodzie produkcyjnym implementuję sumę kontrolną, aby dokładnie sprawdzić, czy obiekt został poprawnie sklonowany i czy nie został uszkodzony przez inny nawiązanie do niego. Tę sumę kontrolną można wyłączyć w trybie Release.
  • uważam, że ta metoda jest bardzo przydatna: często chcesz tylko klonować części obiektu, a nie całą rzecz.

Naprawdę przydatne do oddzielania wielu wątków od wielu innych wątków

Doskonałym przykładem użycia tego kodu jest wprowadzenie klonów zagnieżdżonej klasy lub struktury do kolejki, aby zaimplementować wzorzec producent / konsument.

  • możemy mieć jeden (lub więcej) wątek modyfikujący klasę, która posiadają, a następnie wciskają kompletną kopię tej klasy do ConcurrentQueue.
  • mamy wtedy jeden (lub więcej) wątków wyciągających kopie tych klas i zajmujących się nimi.

Działa to bardzo dobrze w praktyce i pozwala nam oddzielić wiele wątków (producentów) od jednego lub więcej wątków (konsumentów).

I ta metoda jest również ślepo szybka: jeśli używamy zagnieżdżonych struktur, jest 35x szybsza niż serializowanie / deserializacja zagnieżdżonych klas i pozwala nam na wykorzystanie wszystkich gwintów dostępnych na maszynie.

Update

Najwyraźniej ExpressMapper jest tak szybki, jeśli nie szybszy, niż ręczne kodowanie, takie jak powyżej. Może będę musiał zobaczyć, jak porównują się z profilerem.

 7
Author: Contango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-18 18:12:33

Ponieważ nie mogłem znaleźć klonera spełniającego wszystkie moje wymagania w różnych projektach, stworzyłem głęboki kloner, który można skonfigurować i dostosować do różnych struktur kodu, zamiast dostosowywać mój kod do wymagań klonerów. Jego osiągnięcie polega na dodaniu adnotacji do kodu, który zostanie sklonowany lub po prostu zostawisz Kod, ponieważ ma mieć domyślne zachowanie. Wykorzystuje odbicia, Cache typu i jest oparty na fasterflect . Proces klonowania jest bardzo szybki dla ogromnej ilości Dane i wysoka hierarchia obiektów (w porównaniu do innych algorytmów opartych na odbiciu / serializacji).

Https://github.com/kalisohn/CloneBehave

Dostępny również jako pakiet nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0

Na przykład: poniższy kod spowoduje adres deepClone, ale wykona tylko płytką kopię pola _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
Author: kalisohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-27 12:53:06

Ta metoda rozwiązała mi problem:

private static MyObj DeepCopy(MyObj source)
        {

            var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

            return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);

        }

Użyj go tak: MyObj a = DeepCopy(b);

 6
Author: JerryGoyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-12 13:43:36

Lubię takie kopiarki:

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

Jeśli masz więcej rzeczy do skopiowania dodaj je

 5
Author: LuckyLikey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 15:39:15

Generator Kodu

Widzieliśmy wiele pomysłów od serializacji przez ręczną implementację po refleksję i chcę zaproponować zupełnie inne podejście przy użyciu generatora kodu CGbR . Metoda generowania klonów jest wydajna w pamięci i procesorze, a tym samym 300x szybsza jako standardowy DataContractSerializer.

Wszystko czego potrzebujesz to częściowa definicja klasy z ICloneable, A generator zrobi resztę:

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

Uwaga: najnowsza wersja ma więcej null czeki, ale pominąłem je dla lepszego zrozumienia.

 5
Author: Toxantron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-09 21:24:27

Tutaj rozwiązanie szybkie i łatwe, które działało dla mnie bez przekazywania na serializacji/deserializacji.

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

Edytuj : wymaga

    using System.Linq;
    using System.Reflection;

Tak go używałem

public MyClass Clone(MyClass theObjectIneededToClone)
{
    MyClass clonedObj = theObjectIneededToClone.DeepClone();
}
 5
Author: Daniele D.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 14:15:45

Wykonaj następujące kroki:

  • Zdefiniuj {[0] } z właściwością tylko do odczytu Self, która zwraca T i ICloneable<out T>, która wywodzi się z ISelf<T> i zawiera metodę T Clone().
  • następnie zdefiniuj typ CloneBase, który implementuje typ protected virtual generic VirtualClone do typu przekazywanego.
  • każdy typ Pochodny powinien zaimplementować VirtualClone, wywołując podstawową metodę klonowania, a następnie robiąc wszystko, co należy zrobić, aby poprawnie klonować te aspekty typu pochodnego, które macierzysta metoda VirtualClone jeszcze nie poradził sobie.

Dla maksymalnej uniwersalności dziedziczenia, klasy ujawniające publiczną funkcjonalność klonowania powinny być sealed, ale wywodzą się z klasy bazowej, która jest identyczna z wyjątkiem braku klonowania. Zamiast przekazywać zmienne typu jawnego clonable, należy przyjąć parametr typu ICloneable<theNonCloneableType>. Pozwoli to na rutynę, która oczekuje, że pochodna Foo będzie działać z pochodną DerivedFoo, ale także pozwoli na tworzenie nie-klonowalnych pochodne Foo.

 4
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-04 14:29:45

Myślę, że możesz spróbować tego.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it
 4
Author: Sudhanva Kotabagi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-19 16:47:21

Stworzyłem wersję zaakceptowanej odpowiedzi, która działa zarówno z "[Serializable]", jak i " [DataContract]". Minęło trochę czasu odkąd go napisałem, ale jeśli dobrze pamiętam [DataContract] potrzebował innego serializera.

Wymaga Systemu, System.IO, System.Runtime.Serializacja, System.Runtime.Serializacja.Formatery.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
Author: Jeroen Ritmeijer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-11 16:06:55

Aby sklonować obiekt klasy, możesz go użyć.Metoda MemberwiseClone,

Po prostu dodaj tę funkcję do swojej klasy:

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

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

Następnie, aby wykonać kopię deepcopy, wystarczy wywołać metodę DeepCopy:

yourClass newLine = oldLine.DeepCopy();
Mam nadzieję, że to pomoże.
 3
Author: Chtiwi Malek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-25 09:39:22

Ok, jest jakiś oczywisty przykład z refleksją w tym poście, ale odbicie jest zwykle powolne, dopóki nie zaczniesz prawidłowo buforować.

Jeśli odpowiednio go buforujesz, to głęboko sklonuje 1000000 obiektów o 4,6 s (mierzony przez obserwatora).

static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();

Niż bierzesz właściwości buforowane lub dodajesz nowe do słownika i używasz ich po prostu

foreach (var prop in propList)
{
        var value = prop.GetValue(source, null);   
        prop.SetValue(copyInstance, value, null);
}

Pełny kod Sprawdź w moim poście w innej odpowiedzi

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

 3
Author: Roma Borodov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:36

Jeśli Twoje drzewo obiektów jest Serializowalne, możesz również użyć czegoś takiego

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

Informujemy, że to rozwiązanie jest dość proste, ale nie jest tak wydajne, jak inne rozwiązania mogą być.

I upewnij się, że jeśli Klasa rośnie, nadal będą tylko te pola sklonowane, które również zostaną serializowane.

 3
Author: LuckyLikey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-21 14:14:04