Chcę zrobić coś takiego:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
Następnie wprowadź zmiany 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, zacząłem tworzyć nowy obiekt, a następnie kopiować każdą właściwość osobno, ale zawsze daje mi to poczucie, że istnieje lepszy lub bardziej elegancki sposób obsługi sytuacja.
Jak mogę sklonować lub głęboko skopiować obiekt, aby sklonowany obiekt mógł być modyfikowany bez odzwierciedlania jakichkolwiek zmian w oryginalnym obiekcie?
clone
metodę w klasie, a następnie wywołuje wewnętrznego, prywatnego konstruktora, który zostaje przekazanythis
. Więc kopiowanie jest okropne [sic], ale kopiowanie ostrożne (a artykuł zdecydowanie warto przeczytać) nie jest. ; ^)Odpowiedzi:
Podczas gdy standardową praktyką jest implementacja
ICloneable
interfejsu (opisanego tutaj , więc nie będę regurgitować), oto ładna kopiarka obiektów z głębokim klonowaniem, którą znalazłem w The Code Project i włączyłem ją do naszych materiałów.Jak wspomniano w innym miejscu, Twoje obiekty muszą być serializowane.
Chodzi o to, że serializuje Twój obiekt, a następnie przekształca go w nowy obiekt. Zaletą jest to, że nie trzeba się martwić klonowaniem wszystkiego, gdy obiekt staje się zbyt złożony.
I przy użyciu metod rozszerzenia (również z pierwotnie przywoływanego źródła):
Jeśli wolisz korzystać z nowych metod rozszerzenia C # 3.0, zmień metodę, tak aby miała następujący podpis:
Teraz wywołanie metody staje się po prostu
objectBeingCloned.Clone();
.EDYCJA (10 stycznia 2015 r.) Pomyślałem, że wrócę do tego, aby wspomnieć, że ostatnio zacząłem używać Jsona (Newtonsoft), aby to zrobić, powinien być lżejszy i pozwala uniknąć narzutów tagów [Serializable]. ( NB @atconway zwrócił uwagę w komentarzach, że członkowie prywatni nie są klonowani przy użyciu metody JSON)
źródło
typeof(T).IsSerializable
jest również prawdziwe, jeśli typ został oznaczony[Serializable]
atrybutem. Nie musi implementowaćISerializable
interfejsu.Chciałem klonera dla bardzo prostych obiektów, głównie prymitywów i list. Jeśli Twój obiekt jest po wyjęciu z pudełka JSON serializowalny, ta metoda załatwi sprawę. Nie wymaga to modyfikacji ani implementacji interfejsów w sklonowanej klasie, tylko serializator JSON, taki jak JSON.NET.
Możesz także użyć tej metody rozszerzenia
źródło
Newtonsoft.Json.JsonConvert
ale jest tak samoPowodem nieużywania ICloneable nie jest to, że nie ma on ogólnego interfejsu. Powodem, dla którego nie należy go używać, jest niejasny . Nie jest jasne, czy otrzymujesz płytką czy głęboką kopię; to zależy od implementatora.
Tak,
MemberwiseClone
tworzy płytką kopię, ale przeciwieństwoMemberwiseClone
nie jestClone
; być możeDeepClone
nie istniałoby. Kiedy używasz obiektu przez jego interfejs ICloneable, nie możesz wiedzieć, jakiego rodzaju klonowanie wykonuje dany obiekt. (A komentarze XML nie wyjaśnią tego, ponieważ otrzymasz komentarze interfejsu zamiast tych dotyczących metody klonowania obiektu).Zwykle robię po prostu
Copy
metodę, która robi dokładnie to, czego chcę.źródło
Po wielu lekturach na temat wielu połączonych tutaj opcji i możliwych rozwiązań tego problemu, uważam, że wszystkie opcje są dość dobrze podsumowane pod linkiem Iana P. (wszystkie inne opcje są ich odmianami), a najlepsze rozwiązanie zapewnia Pedro77 link, na komentarzach zapytania.
Więc po prostu skopiuję odpowiednie części tych 2 referencji tutaj. W ten sposób możemy mieć:
Najlepszą rzeczą do klonowania obiektów w Cis!
Przede wszystkim są to wszystkie nasze opcje:
W artykule Szybka, głęboka kopia według drzew wyrażeń zawiera również porównanie wydajności klonowania przez drzewa serializacji, odbicia i wyrażenia.
Dlaczego wybieram ICloneable (tj. 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 próbuje znaleźć zastosowanie 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 albo zobrazować wszystkie problemy, które dowolna z powyższych metod może przynieść, albo przeczytać artykuł.
Oto moja nieznacznie zmodyfikowana wersja jego wniosku:
Mamy nadzieję, że ta implementacja wyjaśni wszystko:
Teraz rozważ, aby klasa pochodziła od Osoby.
Możesz spróbować uruchomić następujący kod:
Wytworzona produkcja będzie:
Zauważ, że jeśli utrzymamy liczbę obiektów, zastosowany tutaj klon zachowa prawidłową liczbę obiektów.
źródło
ICloneable
dla członków publicznych. „Ponieważ osoby wywołujące klon nie mogą polegać na metodzie wykonującej przewidywalną operację klonowania, zalecamy, aby ICloneable nie był implementowany w publicznych interfejsach API”. msdn.microsoft.com/en-us/library/… Jednak na podstawie wyjaśnień podanych przez Venkat Subramaniam w twoim powiązanym artykule, myślę, że warto stosować w tej sytuacji, o ile twórcy obiektów ICloneable mają głębokie zrozumienie, które właściwości powinny być głębokie a płytkie (tj. głęboka kopia Mózg, płytka kopia Miasto)Wolę konstruktora kopii niż klon. Cel jest wyraźniejszy.
źródło
Prosta metoda rozszerzenia do skopiowania wszystkich właściwości publicznych. Działa dla dowolnych obiektów i nie wymaga klasy
[Serializable]
. Można rozszerzyć o inny poziom dostępu.źródło
Właśnie stworzyłem projekt
CloneExtensions
biblioteki . Wykonuje szybki, głęboki klon przy użyciu prostych operacji przypisania generowanych przez kompilację kodu środowiska wykonawczego drzewa wyrażeń.Jak tego użyć?
Zamiast pisać własne
Clone
lubCopy
metody z tonem przypisań między polami i właściwościami, zmuś program do zrobienia tego sam, korzystając z drzewa wyrażeń.GetClone<T>()
Metoda oznaczona jako metoda rozszerzenia pozwala po prostu wywołać ją w instancji:Można wybrać to, co powinno być kopiowane z
source
donewInstance
korzystaniaCloningFlags
ENUM:Co można sklonować?
Następujący członkowie klasy / struktury są klonowani wewnętrznie:
Jak szybko to jest
Rozwiązanie jest szybsze niż refleksja, ponieważ informacje o członkach należy zebrać tylko raz, zanim
GetClone<T>
zostaną użyte po raz pierwszy dla danego typuT
.Jest także szybszy niż rozwiązanie oparte na serializacji, gdy klonujesz więcej niż kilka instancji tego samego typu
T
.i więcej...
Przeczytaj więcej o wygenerowanych wyrażeniach w dokumentacji .
Przykładowa lista debugowania wyrażeń dla
List<int>
:}
co ma takie samo znaczenie jak następujący kod c #:
Czy to nie tak, jak napisałbyś własną
Clone
metodęList<int>
?źródło
Cóż, miałem problemy z używaniem ICloneable w Silverlight, ale podobał mi się pomysł serializacji, mogę seralizować XML, więc zrobiłem to:
źródło
Jeśli już używasz aplikacji innej firmy, takiej jak ValueInjecter lub Automapper , możesz zrobić coś takiego:
Używając tej metody nie musisz implementować
ISerializable
aniICloneable
na swoich obiektach. Jest to powszechne w przypadku wzorca MVC / MVVM, dlatego stworzono takie proste narzędzia, jak ten.zobacz próbkę głębokiego klonowania ValueInjecter na GitHub .
źródło
Najlepiej jest zaimplementować metodę rozszerzenia taką jak
a następnie użyj go w dowolnym miejscu rozwiązania przez
Możemy mieć następujące trzy implementacje:
Wszystkie powiązane metody działają dobrze i zostały głęboko przetestowane.
źródło
Krótka odpowiedź brzmi: dziedziczysz po interfejsie ICloneable, a następnie implementujesz funkcję .clone. Klon powinien wykonać kopię członka 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 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.).
Bardziej szczegółowe wyjaśnienie dotyczące klonowania przy użyciu ICloneable znajduje się w tym artykule .
Długie odpowiedź brzmi „to zależy”. Jak wspomnieli inni, ICloneable nie jest obsługiwany przez generyczne, wymaga specjalnych rozważań dla odwołań do klas okrągłych i przez niektórych jest postrzegany jako „błąd” w .NET Framework. Metoda serializacji zależy od możliwości serializacji obiektów, którymi mogą nie być i możesz nie mieć nad nimi kontroli. Społeczność wciąż toczy wiele dyskusji na temat „najlepszej” praktyki. W rzeczywistości żadne z tych rozwiązań nie jest uniwersalne dla wszystkich najlepszych praktyk we wszystkich sytuacjach, w których pierwotnie interpretowano ICloneable.
Zobacz ten artykuł dla deweloperów, aby uzyskać więcej opcji (podziękowania dla Iana).
źródło
Twoje zdrowie.
źródło
EDYCJA: projekt został przerwany
Jeśli chcesz prawdziwego klonowania do nieznanych typów, możesz rzucić okiem na fastclone .
To klonowanie oparte na wyrażeniach działa około 10 razy szybciej niż serializacja binarna i zachowuje pełną spójność grafu obiektowego.
Oznacza to, że: jeśli wielokrotnie odwołasz się do tego samego obiektu w hierarchii, do klonu będzie również przypisana jedna instancja.
Nie ma potrzeby tworzenia interfejsów, atrybutów ani innych modyfikacji klonowanych obiektów.
źródło
Uprość i używaj AutoMapper, jak wspomniano inni, to prosta biblioteka do mapowania jednego obiektu na inny ... Aby skopiować obiekt na inny tego samego typu, wystarczy trzy linie kodu:
Obiekt docelowy jest teraz kopią obiektu źródłowego. Nie dość proste? Utwórz metodę rozszerzenia do użycia wszędzie w swoim rozwiązaniu:
Metodę rozszerzenia można zastosować w następujący sposób:
źródło
Wymyśliłem to, aby przezwyciężyć niedociągnięcie platformy .NET polegające na ręcznym głębokim kopiowaniu listy <T>.
Używam tego:
I w innym miejscu:
Próbowałem wymyślić oneliner, który to robi, ale nie jest to możliwe, ponieważ wydajność nie działa w anonimowych blokach metod.
Jeszcze lepiej, użyj ogólnego klonera List <T>:
źródło
P: Dlaczego miałbym wybrać tę odpowiedź?
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 .
10 razy szybszy niż inne metody
Następująca metoda wykonywania głębokiego klonowania to:
I metoda ...
Aby uzyskać najwyższą prędkość, możesz użyć Nested MemberwiseClone, aby wykonać głęboką kopię . Ma prawie taką samą szybkość jak kopiowanie struktury wartości i jest znacznie szybszy niż (a) odbicie lub (b) serializacja (jak opisano w innych odpowiedziach na tej stronie).
Zauważ, że jeśli używasz Nested MemberwiseClone do głębokiej kopii , musisz ręcznie wdrożyć ShallowCopy dla każdego poziomu zagnieżdżonego w klasie oraz DeepCopy, który wywołuje wszystkie wspomniane metody ShallowCopy w celu utworzenia pełnego klonu. To proste: w sumie tylko kilka wierszy, zobacz poniższy kod demonstracyjny.
Oto wynik kodu pokazującego względną różnicę wydajności dla 100 000 klonów:
Używanie Nested MemberwiseClone na klasie prawie tak szybko, jak kopiowanie struktury, a kopiowanie struktury jest dość zbliżone do teoretycznej maksymalnej prędkości, jaką potrafi .NET.
Aby zrozumieć, jak wykonać głęboką kopię przy użyciu MemberwiseCopy, oto projekt demonstracyjny, który został użyty do wygenerowania powyższych czasów:
Następnie wywołaj demo z main:
Ponownie zauważ, że jeśli używasz Nested MemberwiseClone do głębokiej kopii , musisz ręcznie wdrożyć ShallowCopy dla każdego poziomu zagnieżdżonego w klasie oraz DeepCopy, który wywołuje wszystkie wymienione metody ShallowCopy w celu utworzenia pełnego klonu. To proste: w sumie tylko kilka wierszy, patrz powyższy kod demonstracyjny.
Typy wartości a typy referencji
Zauważ, że jeśli chodzi o klonowanie obiektu, istnieje duża różnica między „ strukturą ” a „ klasą ”:
Zobacz różnice między typami wartości a typami referencji .
Sumy kontrolne ułatwiające debugowanie
Naprawdę przydatne do oddzielania wielu wątków od wielu innych wątków
Doskonałym przykładem użycia tego kodu jest podawanie klonów zagnieżdżonej klasy lub struktury do kolejki w celu zaimplementowania wzorca producent / konsument.
ConcurrentQueue
.Działa to wyjątkowo dobrze w praktyce i pozwala nam oddzielić wiele wątków (producentów) od jednego lub więcej wątków (konsumentów).
Ta metoda jest również niesamowicie szybka: jeśli użyjemy zagnieżdżonych struktur, jest 35 razy szybsza niż serializacja / deserializacja zagnieżdżonych klas i pozwala nam skorzystać ze wszystkich wątków dostępnych na maszynie.
Aktualizacja
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.
źródło
Zasadniczo implementujesz interfejs ICloneable i sam klonujesz. Obiekty C # mają wbudowaną metodę MemberwiseClone, która wykonuje płytką kopię, która może pomóc ci w przypadku wszystkich prymitywów.
W przypadku głębokiej kopii nie ma możliwości, aby wiedzieć, jak to zrobić automatycznie.
źródło
Widziałem to także poprzez refleksję. Zasadniczo istniała metoda, która iterowałaby przez elementy obiektu i odpowiednio kopiowała je do nowego obiektu. Kiedy osiągnął typy referencyjne lub kolekcje, myślę, że wykonał rekurencyjne wywołanie. Odbicie jest drogie, ale działało całkiem dobrze.
źródło
Oto implementacja głębokiej kopii:
źródło
Ponieważ nie mogłem znaleźć klonera, który spełniałby wszystkie moje wymagania w różnych projektach, stworzyłem głębokiego klonera, który można skonfigurować i dostosować do różnych struktur kodu zamiast dostosowywać mój kod, aby spełniał wymagania klonerów. Osiąga się to poprzez dodanie adnotacji do kodu, który należy sklonować, lub po prostu zostawiasz kod tak, jak ma to być zachowanie domyślne. Wykorzystuje odbicia, typy pamięci podręcznych i jest oparty na szybszym odbiciu . Proces klonowania jest bardzo szybki dla ogromnej ilości danych i wysokiej hierarchii 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 będzie głębokoClone Adres, ale wykona tylko płytką kopię pola _currentJob.
źródło
Generator kodów
Widzieliśmy wiele pomysłów od serializacji poprzez ręczne wdrożenie do refleksji i chcę zaproponować zupełnie inne podejście przy użyciu CGbR Code Generator . Metoda generowania klonów jest wydajna pod względem pamięci i procesora, a zatem 300 razy szybsza niż standardowy DataContractSerializer.
Wystarczy częściowa definicja klasy,
ICloneable
a generator zajmie się resztą:Uwaga: najnowsza wersja ma bardziej zerowe kontrole, ale pominąłem je dla lepszego zrozumienia.
źródło
Lubię takie Copyconstructors:
Jeśli masz więcej rzeczy do skopiowania, dodaj je
źródło
Ta metoda rozwiązała dla mnie problem:
Użyj tego w ten sposób:
MyObj a = DeepCopy(b);
źródło
Tutaj rozwiązanie jest szybkie i łatwe, które działało dla mnie bez konieczności przechodzenia do serializacji / deserializacji.
EDYCJA : wymaga
Tak to wykorzystałem
źródło
Wykonaj następujące kroki:
ISelf<T>
tylko do odczytu,Self
która zwracaT
, iICloneable<out T>
która pochodzi zISelf<T>
metody i obejmuje jąT Clone()
.CloneBase
typ, który implementujeprotected virtual generic VirtualClone
rzutowanieMemberwiseClone
do typu przekazywanego.VirtualClone
przez wywołanie metody podstawowego klonowania, a następnie wykonanie wszelkich czynności, które należy zrobić, aby poprawnie sklonować te aspekty typu pochodnego, których nadrzędna metoda VirtualClone jeszcze nie obsłużyła.Aby uzyskać maksymalną wszechstronność dziedziczenia, klasy udostępniające publiczną funkcję klonowania powinny być
sealed
, ale wywodzą się z klasy bazowej, która poza tym jest identyczna, z wyjątkiem braku klonowania. Zamiast przekazywać zmienne jawnego typu klonowalnego, weź parametr typuICloneable<theNonCloneableType>
. Pozwoli to rutynie, która oczekuje, że klonowalna pochodnaFoo
będzie działać z klonowalną pochodnąDerivedFoo
, ale także pozwoli na tworzenie nie klonowalnych pochodnychFoo
.źródło
Myślę, że możesz spróbować.
źródło
Utworzył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łem innego serializatora.
Wymaga System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
źródło
Ok, jest jakiś oczywisty przykład z odbiciem w tym poście, ALE odbicie jest zwykle wolne, dopóki nie zaczniesz go odpowiednio buforować.
jeśli odpowiednio go zbuforujesz, spowoduje to głębokie sklonowanie obiektu 1000000 o 4,6s (mierzone przez Watchera).
niż bierzesz właściwości z pamięci podręcznej lub dodajesz nowe do słownika i używasz ich po prostu
pełny kod sprawdź w moim poście w innej odpowiedzi
https://stackoverflow.com/a/34365709/4711853
źródło
prop.GetValue(...)
nadal są odbiciem i nie mogą być buforowane. Jednak w drzewie wyrażeń jest ono skompilowane, tak szybkoPonieważ prawie wszystkie odpowiedzi na to pytanie są niezadowalające lub po prostu nie działają w mojej sytuacji, napisałem AnyClone, który został w pełni wdrożony z refleksją i rozwiązał wszystkie potrzeby tutaj. Nie byłem w stanie uzyskać serializacji do pracy w skomplikowanym scenariuszu o złożonej strukturze i
IClonable
jest mniej niż idealny - w rzeczywistości nie powinno to nawet być konieczne.Standardowe ignorować atrybuty są obsługiwane za pomocą
[IgnoreDataMember]
,[NonSerialized]
. Obsługuje złożone kolekcje, właściwości bez ustawiaczy, pola tylko do odczytu itp.Mam nadzieję, że pomoże to komuś innemu, kto miał takie same problemy jak ja.
źródło
Oświadczenie: Jestem autorem wspomnianego pakietu.
Byłem zaskoczony, jak najlepsze odpowiedzi na to pytanie w 2019 roku nadal używają serializacji lub refleksji.
Serializacja jest ograniczona (wymaga atrybutów, określonych konstruktorów itp.) I jest bardzo wolna
BinaryFormatter
wymagaSerializable
atrybutu,JsonConverter
wymaga konstruktora lub atrybutów bez parametrów, ani nie obsługuje bardzo dobrze pól ani interfejsów tylko do odczytu, a oba są 10-30x wolniejsze niż to konieczne.Drzewa ekspresji
Zamiast tego możesz użyć drzew wyrażeń lub Reflection.Emit, aby wygenerować kod klonujący tylko raz, a następnie użyć skompilowanego kodu zamiast powolnego odzwierciedlania lub serializacji.
Po tym, jak sam spotkałem się z problemem i nie znalazłem zadowalającego rozwiązania, postanowiłem stworzyć pakiet, który właśnie to działa i działa z każdym typem i jest prawie tak szybki, jak niestandardowy kod napisany .
Możesz znaleźć projekt na GitHub: https://github.com/marcelltoth/ObjectCloner
Stosowanie
Możesz zainstalować go z NuGet. Pobierz
ObjectCloner
pakiet i użyj go jako:lub jeśli nie masz nic przeciwko zanieczyszczeniu typu obiektu rozszerzeniami, pobierz
ObjectCloner.Extensions
również i napisz:Wydajność
Prosty test klonowania hierarchii klas wykazał wydajność ~ 3-krotnie szybszą niż użycie Reflection, ~ 12-krotnie szybszą niż Newtonsoft. Serializacja Jsona i ~ 36-krotnie szybsza niż wysoce sugerowana
BinaryFormatter
.źródło