Pytanie Głębokie obiekty do klonowania


Chcę zrobić coś takiego:

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

A następnie wprowadź zmiany do nowego obiektu, które nie są odzwierciedlone w oryginalnym obiekcie.

Nie często potrzebuję tej funkcjonalności, więc gdy jest to konieczne, uciekam się do tworzenia nowego obiektu, a następnie kopiowania każdej właściwości indywidualnie, ale zawsze pozostawia mnie wrażenie, że istnieje lepszy lub bardziej elegancki sposób obsługi. sytuacja.

Jak mogę klonować lub głęboko kopiować obiekt, aby sklonowany obiekt mógł być modyfikowany bez żadnych zmian odzwierciedlanych w oryginalnym obiekcie?


1831
2017-09-17 00:06


pochodzenie


Może być przydatny: "Dlaczego kopiowanie obiektu jest straszne?" agiledeveloper.com/articles/cloning072002.htm - Pedro77
stackoverflow.com/questions/8025890/... Inne rozwiązanie... - Felix K.
Powinieneś rzucić okiem na AutoMapper - Daniel Little
Twoje rozwiązanie jest o wiele bardziej skomplikowane, zagubiłem się czytając ... hehehe. Używam interfejsu DeepClone. publiczny interfejs IDeepCloneable <T> {T DeepClone (); } - Pedro77
@ Pedro77: Obawa, z którą mam do czynienia IDeepCloneable jest to, że nie wszystkie kolekcje odniesień do rzeczy, które mogą być głęboko sklonowane powinny być; prawidłowe zachowanie podczas klonowania List<T> zależy nie tylko od T, ale także w celu list. Jeśli żadna z pozycji na listach nigdy nie zostanie narażona na nic, co zmusiłoby ich do mutacji, to nawet jeśli przedmioty z list mogły zostać sklonowane, byłoby lepiej skopiować referencje bezpośrednio. - supercat


Odpowiedzi:


Podczas gdy standardową praktyką jest wdrożenie ICloneable interfejs (opisany tutaj, więc nie będę regurgitate), oto ładna kopiarka z głębokim klonem, którą znalazłem Projekt Code jakiś czas temu i włączyłem go do naszych rzeczy.

Jak wspomniano w innym miejscu, wymaga to możliwości serializacji obiektów.

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 obiekt, a następnie przekształca go w nowy obiekt. Korzyścią jest to, że nie musisz się martwić o klonowanie wszystkiego, gdy obiekt staje się zbyt złożony.

I za pomocą metod rozszerzających (również z oryginalnego źródła):

Jeśli wolisz korzystać z nowego metody rozszerzenia C # 3.0, zmień metodę, aby mieć następujący podpis:

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

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

EDYTOWAĆ (10 stycznia 2015 r.) Myślałem, że to powtórzę, wspomnę o tym, że ostatnio zacząłem używać (Newtonsoft) Json, aby to zrobić, powinno być lżejszy i unika narzutów znaczników [Serializable]. (NB @atconway zwrócił uwagę 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);
}

1467
2018-04-03 13:31



stackoverflow.com/questions/78536/cloning-objects-in-c/... ma link do powyższego kodu [i odwołuje się do dwóch innych takich implementacji, z których jedna jest bardziej odpowiednia w moim kontekście] - Ruben Bartelink
Serializacja / deserializacja wiąże się ze znacznym obciążeniem, które nie jest konieczne. Zobacz interfejs ICloneable i metody klonowania .MemberWise () w języku C #. - 3Dave
@David, przyznane, ale jeśli obiekty są lekkie, a wydajność osiągnięta podczas korzystania z nich nie jest zbyt wysoka dla twoich wymagań, to jest to przydatna wskazówka. Nie wykorzystałem go intensywnie z dużą ilością danych w pętli, przyznaję, ale nigdy nie widziałem problemu z wydajnością. - johnc
@Amir: faktycznie, nie: typeof(T).IsSerializable jest również prawdziwe, jeśli typ został oznaczony symbolem [Serializable] atrybut. To nie musi implementować ISerializable berło. - Daniel Gehriger
Pomyślałem, że wspomnę o tym, że chociaż ta metoda jest przydatna, a ja sam jej używałem przez długi czas, to nie jest ona w ogóle kompatybilna z Medium Trust - więc uważaj, jeśli piszesz kod, który wymaga kompatybilności. BinaryFormatter uzyskuje dostęp do pól prywatnych i dlatego nie może działać w domyślnym zestawie uprawnień dla środowisk o częściowym zaufaniu. Możesz spróbować innego serializera, ale upewnij się, że Twój rozmówca wie, że klon może nie być idealny, jeśli obiekt przychodzący opiera się na prywatnych polach. - Alex Norcliffe


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

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

183
2017-09-17 01:12



Solutiojn jest jeszcze szybszy niż rozwiązanie BinaryFormatter, Porównanie wydajności serializacji .NET - esskar
Dzięki za to. Byłem w stanie zrobić to samo z serializatorem BSON dostarczanym ze sterownikiem MongoDB dla C #. - Mark Ewer
To jest dla mnie najlepszy sposób, jednak używam Newtonsoft.Json.JsonConvert ale to jest to samo - Pierre
Podejście JSON jest OK w przypadku małych obiektów płaskich, ale pierwotne pytanie dotyczyło dowolnego obiektu, w tym również głębokiego. Serializator NFX.Slim działa szybciej o rząd wielkości na dowolnym typie .NET, o ile nie ma delegatów i niezarządzanych wskaźników, tutaj jest to źródło, które działa bardzo podobnie do BinaryFormnatter tylko co najmniej 5 razy szybciej: github.com/aumcode/nfx/blob/master/Source/NFX/Serialization/...    Zestaw testów: github.com/aumcode/serbench  udowadnia, że ​​jedyny seryjniejszy, który jest szybszy, to Protobuf, któremu brakuje dynamiki i referencji - itadapter DKh
Aby to działało, obiekt do klonowania musi być serializowalny, jak już wspomniano - oznacza to na przykład, że może nie mieć okrągłych zależności - radomeit


Powód, by nie używać ICloneable jest nie ponieważ nie ma ogólnego interfejsu. Powodem, by tego nie używać, jest to, że jest niejasne. Nie wyjaśnia, czy otrzymujesz płytką, czy głęboką kopię; to zależy od realizatora.

Tak, MemberwiseClone tworzy płytką kopię, ale przeciwieństwo MemberwiseClone nie jest Clone; to może być DeepClone, która nie istnieje. Kiedy używasz obiektu poprzez jego interfejs ICloneable, nie możesz wiedzieć, jaki rodzaj klonowania wykonuje dany obiekt. (A komentarze XML nie wyjaśnią, ponieważ otrzymasz komentarze interfejsu, a nie te w metodzie Klonowania obiektu.)

Zazwyczaj robię tylko Copy metoda, która robi dokładnie to, co chcę.


147
2017-09-26 20:18



Nie wiem, dlaczego program ICloneable jest uważany za mało precyzyjny. Biorąc pod uwagę taki typ jak słownik (Of T, U), spodziewałbym się, że ICloneable.Clone powinien zrobić, co jest potrzebne do głębokiego i płytkiego kopiowania, aby nowy słownik był niezależnym słownikiem, który zawiera te same litery T i U (zawartość struct, i / lub odniesienia do obiektów) jako oryginału. Gdzie jest niejednoznaczność? Dla pewności, ogólny ICloneable (Of T), który odziedziczył ISelf (Of T), który zawierał metodę "Self", byłby o wiele lepszy, ale nie widzę dwuznaczności na głębokim lub płytkim klonowaniu. - supercat
Twój przykład ilustruje problem. Załóżmy, że masz słownik <string, Customer>. Jeśli klonowany słownik ma podobnie Obiekty klienta jako oryginał, lub kopie tych obiektów klienta? Istnieją rozsądne przypadki użycia dla każdego z nich. Ale ICloneable nie wyjaśnia, który dostaniesz. Dlatego nie jest to przydatne. - Ryan Lundy
@ Kyralessa Artykuł Microsoft MSDN faktycznie stwierdza ten problem, nie wiedząc, czy żądasz głębokiej lub płytkiej kopii. - crush
Odpowiedź z duplikatu stackoverflow.com/questions/129389/... opisuje rozszerzenie kopiowania, oparte na rekurencyjnym MembershipClone - Michael Freidgeim


Mam nadzieję, że po wielu lekturach dotyczących wielu opcji tutaj połączonych i możliwych rozwiązań tego problemu wszystkie opcje są dość dobrze podsumowane Ian P.link (wszystkie inne opcje są odmianami), a najlepszym rozwiązaniem jest Pedro77link na pytanie komentarze.

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

Najlepszą rzeczą do zrobienia w przypadku klonowania obiektów w c ostro!

Przede wszystkim są to wszystkie nasze opcje:

The artykuł Szybkie, głębokie kopiowanie według drzew wyrażeń   ma również porównanie wydajności klonowania według drzew serializacyjnych, refleksyjnych i ekspresyjnych.

Dlaczego wybieram ICloneable (tzn. ręcznie)

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

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

Oto moja nieco zmodyfikowana wersja jego wniosku:

Kopiowanie obiektu przez określenie New następnie nazwa klasy często prowadzi do kodu, który nie jest rozszerzalny. Korzystanie z klonu, zastosowanie wzoru prototypu, jest lepszym sposobem na osiągnięcie tego. Jednak użycie klona, ​​jak to jest w C # (i Java) może być dość problematyczne, jak również. Lepiej jest dostarczyć chroniony (niepubliczny) konstruktor kopii 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 bezpiecznie tworząc obiekty za pomocą chronionego konstruktora kopiowania.

Mam nadzieję, że ta implementacja może wyjaśnić:

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 produkt wyjściowy 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

Zwróć uwagę, że jeśli zachowamy liczbę obiektów, to klon zaimplementowany tutaj zachowa prawidłową liczbę obiektów.


84
2017-09-17 00:13



MS zaleca, aby nie używać ICloneable dla członków publicznych. "Ponieważ wywołujący Clone nie mogą polegać na metodzie przeprowadzania przewidywalnej operacji klonowania, zalecamy, aby ICloneable nie było implementowane w publicznych interfejsach API." msdn.microsoft.com/en-us/library/... Jednak w oparciu o wyjaśnienia podane przez Venkata Subramaniama w twoim połączonym artykule, myślę, że to ma sens, aby użyć w tej sytuacji tak długo, jak twórcy obiektów ICloneable mają głębokie zrozumienie, które właściwości powinny być głębokie w stosunku do płytkich kopii (tzn. głębokie kopie Brain, płytkie kopie City) - BateTech
Po pierwsze, jestem daleko od eksperta w tym temacie (publiczne API). ja myśleć raz ta uwaga MS ma wiele sensu. I nie sądzę, że można bezpiecznie założyć użytkowników tego API będzie miało tak głębokie zrozumienie. Tak więc ma sens jedynie wdrożenie go na publiczny interfejs APIjeśli to naprawdę nie ma znaczenia dla tego, kto z niego skorzysta. ja odgadnąć posiadanie pewnego rodzaju UML bardzo wyraźnie czyni rozróżnienie na każdej właściwości może pomóc. Ale chciałbym usłyszeć od kogoś z większym doświadczeniem. : P - cregox
Możesz użyć Generator klonów CGbR i uzyskać podobny wynik bez ręcznego pisania kodu. - Toxantron
Wdrożenie języka pośredniego jest użyteczne - Michael Freidgeim


Wolę konstruktora kopii od klonu. Intencja jest wyraźniejsza.


69
2018-03-16 11:38



.Net nie ma konstruktorów kopiowania. - Pop Catalin
Pewnie, że to robi: new MyObject (objToCloneFrom) Wystarczy zadeklarować ctor, który bierze obiekt do klonowania jako parametr. - Nick
To nie to samo. Musisz dodać ją do każdej klasy ręcznie, a nawet nie wiesz, czy garantujesz głęboką kopię. - Dave Van den Eynde
+1 dla kopii ctor. Musisz ręcznie napisać funkcję clone () dla każdego typu obiektu, i powodzenia w tym przypadku, gdy hierarchia klasowa osiągnie kilka poziomów. - Andrew Grant
Z konstruktorami kopii tracisz jednak hierarchię. agiledeveloper.com/articles/cloning072002.htm - Will


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żna go rozszerzyć o 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 ) } );
        }
    };
}

36
2017-12-02 17:39



To, niestety, jest wadliwe. Jest to równoważne wywołaniu objectOne.MyProperty = objectTwo.MyProperty (tzn. Skopiuje tylko odwołanie). Nie będzie klonować wartości właściwości. - Alex Norcliffe
Alexowi Norcliffe: autorowi pytania zadawano pytanie o "kopiowanie każdej własności" zamiast klonowania. w większości przypadków dokładna duplikacja właściwości nie jest potrzebna. - Konstantin Salavatov
Myślę o użyciu tej metody, ale z rekursją. więc jeśli wartość właściwości jest odniesieniem, utwórz nowy obiekt i ponownie wywołaj CopyTo. widzę tylko jeden problem, że wszystkie używane klasy muszą mieć konstruktor bez parametrów. Ktoś już tego próbował? Zastanawiam się również, czy to faktycznie będzie działać z właściwościami zawierającymi .NET klasy takie jak DataRow i DataTable? - Koryu


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
    //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
2017-10-15 17:55





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

MyObject oldObj; // The existing object to clone

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

Dzięki tej metodzie nie musisz implementować ISerializable ani ICloneable na swoich obiektach. Jest to częste w przypadku wzorca MVC / MVVM, więc stworzono proste narzędzia takie jak ta.

widzieć rozwiązanie do głębokiego klonowania Valueinjecter w CodePlex.


26
2017-12-24 22:56





Właśnie stworzyłem CloneExtensions biblioteka projekt. Wykonuje szybki, głęboki klon za pomocą prostych operacji przypisania generowanych przez kompilację kodu wykonawczego Expression Tree.

Jak tego użyć?

Zamiast pisać własne Clone lub Copy metody z tonem przypisań między polami i właściwościami sprawiają, że program robi to dla siebie, używając drzewa wyrazów. GetClone<T>() Metoda oznaczona jako metoda rozszerzenia pozwala po prostu wywołać ją na instancji:

var newInstance = source.GetClone();

Możesz wybrać, z czego chcesz skopiować source do newInstance za pomocą CloningFlags enum:

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

Co można sklonować?

  • Prymitywny (int, uint, byte, double, char itd.), Znany niezmienny typy (DateTime, TimeSpan, String) i delegatów (w tym Akcja, Funk, itp.)
  • Nullable
  • Tablice T []
  • Niestandardowe klasy i struktury, w tym ogólne klasy i struktury.

Następujące elementy klasy / klasy są klonowane wewnętrznie:

  • Wartości pól publicznych, a nie tylko do odczytu
  • Wartości właściwości publicznych z zarówno get, jak i set accessors
  • Elementy kolekcji dla typów implementujących ICollection

Jak szybko to działa?

Rozwiązanie jest szybsze od refleksji, ponieważ informacje o członkach muszą być zebrane tylko raz, wcześniej GetClone<T> jest używany po raz pierwszy dla danego typu T.

Jest także szybszy od rozwiązania opartego na serializacji, gdy klonujesz więcej niż kilka instancji tego samego typu T.

i więcej...

Przeczytaj więcej na temat generowanych wyrażeń dokumentacja.

Przykładowa lista debugowania wyrażeń 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 to nie jest tak, jak piszesz własne? Clone metoda dla List<int>?


25
2017-09-17 00:14



Jakie są szanse na wejście na NuGet? Wydaje się, że to najlepsze rozwiązanie. Jak to się porównuje NClone? - crush
Myślę, że ta odpowiedź powinna zostać przegłosowana więcej razy. Ręczne wdrażanie programu ICloneable jest uciążliwe i podatne na błędy, użycie refleksji lub serializacji jest powolne, jeśli wydajność jest ważna i trzeba skopiować tysiące obiektów w krótkim okresie czasu. - nightcoder
Wcale nie, mylisz się co do refleksji, powinieneś po prostu odpowiednio to zapamiętać. Sprawdź moją odpowiedź poniżej stackoverflow.com/a/34368738/4711853 - Roma Borodov


Krótka odpowiedź to dziedziczenie z interfejsu ICloneable, a następnie implementacja funkcji .clone. Klon powinien wykonać kopię członkowską i wykonać głęboką kopię na dowolnym elemencie, który tego wymaga, a następnie zwrócić wynikowy obiekt. Jest to operacja rekursywna (wymaga, aby wszyscy członkowie klasy, którą chcesz sklonować, byli albo typami wartości albo implementowali ICloneable, a ich członkowie byli albo typami wartości albo implementowali ICloneable itd.).

Aby uzyskać bardziej szczegółowe wyjaśnienie dotyczące klonowania za pomocą ICloneable, sprawdź Ten artykuł.

The długie odpowiedź brzmi "to zależy". Jak wspomnieli inni, funkcja ICloneable nie jest obsługiwana przez typy ogólne, wymaga specjalnych względów dla odwołań do klas kołowych i jest faktycznie postrzegana przez niektórych jako "błąd" w .NET Framework. Metoda serializacji polega na tym, że twoje obiekty mogą być przekształcane do postaci szeregowej, do której mogą nie należeć i na które nie masz wpływu. W społeczności wciąż trwa dyskusja, która jest "najlepszą" praktyką. W rzeczywistości, żadne z rozwiązań nie są uniwersalnymi rozwiązaniami dla wszystkich sytuacji, takich jak ICloneable, pierwotnie interpretowano.

Zobacz to Developer's Corner article dla kilku dodatkowych opcji (kredyt dla Iana).


20
2018-02-16 11:30



ICloneable nie ma ogólnego interfejsu, więc nie zaleca się używania tego interfejsu. - Karg
Twoje rozwiązanie działa, dopóki nie wymaga obsługi odwołań cyklicznych, a potem wszystko zaczyna się komplikować. Lepiej jest wdrożyć głębokie klonowanie przy użyciu głębokiej serializacji. - Pop Catalin
Niestety, nie wszystkie obiekty można poddawać serializacji, więc nie zawsze można użyć tej metody. Łącze Iana jest jak dotąd najbardziej wyczerpującą odpowiedzią. - Zach Burlingame
+1 za wzmiankę o artykule Brada Abramsa. - Merlyn Morgan-Graham


Jeśli chcesz prawdziwego klonowania do nieznanych typów, możesz rzucić okiem fastclone.

To klonowanie oparte na ekspresji działa około 10 razy szybciej niż serializacja binarna i zachowuje całkowitą integralność grafu obiektu.

Oznacza to, że jeśli wielokrotnie odwołasz się do tego samego obiektu w twojej hierarchii, klon będzie miał także jedną instancję, do której się odwołuje.

Nie ma potrzeby tworzenia interfejsów, atrybutów ani żadnych innych modyfikacji klonowanych obiektów.


15
2017-08-03 22:24



Ten wydaje się całkiem przydatny - LuckyLikey
Łatwiej jest rozpocząć pracę z migawką kodu niż dla całego systemu, szczególnie zamkniętego. Jest całkiem zrozumiałe, że żadna biblioteka nie może rozwiązać wszystkich problemów za jednym razem. Powinno się dokonać pewnych relaksacji. - TarmoPikaro
Próbowałem Twojego rozwiązania i wygląda na to, że działa dobrze, dzięki! Myślę, że ta odpowiedź powinna zostać przegłosowana więcej razy. Ręczne wdrażanie programu ICloneable jest uciążliwe i podatne na błędy, użycie refleksji lub serializacji jest powolne, jeśli wydajność jest ważna i trzeba skopiować tysiące obiektów w krótkim okresie czasu. - nightcoder