Saya ingin melakukan sesuatu seperti:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
Dan kemudian membuat perubahan pada objek baru yang tidak tercermin pada objek asli.
Saya tidak sering membutuhkan fungsi ini, jadi ketika itu diperlukan, saya terpaksa membuat objek baru dan kemudian menyalin setiap properti secara individual, tetapi selalu membuat saya merasa bahwa ada cara penanganan yang lebih baik atau lebih elegan. situasi.
Bagaimana saya bisa mengkloning atau menyalin objek dalam sehingga objek yang dikloning dapat dimodifikasi tanpa perubahan yang tercermin pada objek asli?
clone
metode di kelas, lalu minta ia memanggil konstruktor internal dan privat yang diteruskanthis
. Jadi, menyalin itu mengerikan, tetapi menyalin dengan hati-hati (dan artikel itu layak dibaca) tidak. ; ^)Jawaban:
Sementara praktik standar adalah untuk mengimplementasikan
ICloneable
antarmuka (dijelaskan di sini , jadi saya tidak akan memuntahkan), inilah mesin fotokopi objek tiruan dalam yang saya temukan di The Code Project beberapa waktu lalu dan memasukkannya ke dalam barang-barang kami.Seperti yang disebutkan di tempat lain, itu membutuhkan objek Anda menjadi serial.
Idenya adalah bahwa ia membuat serial objek Anda dan kemudian deserializes itu menjadi objek segar. Manfaatnya adalah Anda tidak perlu khawatir tentang mengkloning segala sesuatu ketika suatu objek menjadi terlalu kompleks.
Dan dengan menggunakan metode ekstensi (juga dari sumber yang dirujuk sebelumnya):
Jika Anda lebih suka menggunakan metode ekstensi baru C # 3.0, ubah metode untuk memiliki tanda tangan berikut:
Sekarang pemanggilan metode menjadi
objectBeingCloned.Clone();
.EDIT (10 Januari 2015) Saya pikir saya akan kembali ini, untuk menyebutkan saya baru-baru ini mulai menggunakan (Newtonsoft) Json untuk melakukan ini, itu harus lebih ringan, dan menghindari overhead dari tag [Serializable]. ( NB @atconway telah menunjukkan dalam komentar bahwa anggota pribadi tidak dikloning menggunakan metode JSON)
sumber
typeof(T).IsSerializable
juga benar jika tipe telah ditandai dengan[Serializable]
atribut. Itu tidak harus mengimplementasikanISerializable
antarmuka.Saya ingin cloner untuk objek yang sangat sederhana yang kebanyakan primitif dan daftar. Jika objek Anda di luar kotak JSON serializable maka metode ini akan melakukan trik. Ini tidak memerlukan modifikasi atau implementasi antarmuka pada kelas kloning, hanya serializer JSON seperti JSON.NET.
Anda juga dapat menggunakan metode ekstensi ini
sumber
Newtonsoft.Json.JsonConvert
tetapi itu samaAlasan untuk tidak menggunakan ICloneable adalah tidak karena tidak memiliki antarmuka generik. Alasan untuk tidak menggunakannya adalah karena tidak jelas . Tidak memperjelas apakah Anda mendapatkan salinan yang dangkal atau dalam; itu terserah pelaksana.
Ya,
MemberwiseClone
buat salinan yang dangkal, tetapi yang sebaliknyaMemberwiseClone
bukanClone
; mungkin, mungkin,DeepClone
, yang tidak ada. Saat Anda menggunakan objek melalui antarmuka ICloneable, Anda tidak dapat mengetahui jenis kloning yang dilakukan oleh objek yang mendasarinya. (Dan komentar XML tidak akan membuatnya jelas, karena Anda akan mendapatkan komentar antarmuka daripada yang ada di metode Kloning objek.)Apa yang biasanya saya lakukan hanyalah membuat
Copy
metode yang melakukan persis apa yang saya inginkan.sumber
Setelah banyak membaca tentang banyak opsi yang ditautkan di sini, dan kemungkinan solusi untuk masalah ini, saya percaya semua opsi dirangkum dengan cukup baik di tautan Ian P (semua opsi lain adalah variasi dari itu) dan solusi terbaik disediakan oleh Pedro77 Link 's pada komentar pertanyaan.
Jadi saya hanya akan menyalin bagian yang relevan dari 2 referensi tersebut di sini. Dengan begitu kita dapat memiliki:
Hal terbaik untuk dilakukan untuk mengkloning objek dalam C tajam!
Pertama dan terutama, itu semua adalah pilihan kami:
The Artikel Cepat Jauh Copy oleh Trees Expression juga memiliki kinerja perbandingan kloning oleh serialisasi, Refleksi dan Pohon Ekspresi.
Mengapa saya memilih ICloneable (yaitu secara manual)
Tn. Venkat Subramaniam (tautan berlebihan di sini) menjelaskan secara mendetail mengapa .
Semua artikelnya melingkari contoh yang mencoba berlaku untuk sebagian besar kasus, menggunakan 3 objek: Orang , Otak dan Kota . Kami ingin mengkloning seseorang, yang akan memiliki otak sendiri tetapi kota yang sama. Anda dapat menggambarkan semua masalah yang ada di antara metode lain di atas yang dapat membawa atau membaca artikel.
Ini adalah versi kesimpulan saya yang sedikit dimodifikasi:
Semoga implementasi ini dapat memperjelas:
Sekarang pertimbangkan untuk memiliki kelas yang berasal dari Person.
Anda dapat mencoba menjalankan kode berikut:
Output yang dihasilkan adalah:
Perhatikan bahwa, jika kita menyimpan hitungan jumlah objek, klon seperti yang diterapkan di sini akan menyimpan jumlah yang benar dari jumlah objek.
sumber
ICloneable
untuk anggota publik. "Karena penelepon Clone tidak dapat bergantung pada metode yang melakukan operasi kloning yang dapat diprediksi, kami menyarankan agar ICloneable tidak diimplementasikan dalam API publik." msdn.microsoft.com/en-us/library/... Namun, berdasarkan penjelasan yang diberikan oleh Venkat Subramaniam dalam artikel tertaut Anda, saya pikir masuk akal untuk digunakan dalam situasi ini selama pembuat objek ICloneable memiliki kedalaman memahami sifat mana yang harus dalam vs salinan dangkal (yaitu copy dalam Otak, salinan dangkal Kota)Saya lebih suka copy constructor daripada clone. Maksudnya lebih jelas.
sumber
Metode ekstensi sederhana untuk menyalin semua properti publik. Bekerja untuk objek apa pun dan tidak memerlukan kelas
[Serializable]
. Dapat diperpanjang untuk tingkat akses lainnya.sumber
Saya baru saja membuat proyek
CloneExtensions
perpustakaan . Ini melakukan klon yang cepat dan dalam menggunakan operasi penugasan sederhana yang dihasilkan oleh kompilasi kode runtime Expression Tree.Bagaimana cara menggunakannya?
Alih-alih menulis sendiri
Clone
atauCopy
metode dengan nada penugasan antara bidang dan properti membuat program melakukannya sendiri, menggunakan Pohon Ekspresi.GetClone<T>()
metode yang ditandai sebagai metode ekstensi memungkinkan Anda memanggilnya dengan instan:Anda dapat memilih apa yang harus disalin dari
source
kenewInstance
menggunakanCloningFlags
enum:Apa yang bisa dikloning?
Anggota kelas / struct berikut dikloning secara internal:
Seberapa cepat?
Solusinya lebih cepat daripada refleksi, karena informasi anggota harus dikumpulkan hanya sekali, sebelum
GetClone<T>
digunakan untuk pertama kalinya untuk jenis yang diberikanT
.Ini juga lebih cepat daripada solusi berbasis serialisasi ketika Anda mengkloning lebih dari beberapa instance dari tipe yang sama
T
.dan banyak lagi ...
Baca lebih lanjut tentang ekspresi yang dihasilkan pada dokumentasi .
Contoh daftar debug ekspresi untuk
List<int>
:}
apa yang memiliki arti yang sama seperti mengikuti kode c #:
Bukankah itu seperti bagaimana Anda akan menulis
Clone
metode Anda sendiriList<int>
?sumber
Yah saya mengalami masalah menggunakan ICloneable di Silverlight, tapi saya menyukai ide seralisasi, saya dapat melakukan seralize XML, jadi saya melakukan ini:
sumber
Jika Anda sudah menggunakan aplikasi pihak ke-3 seperti ValueInjecter atau Automapper , Anda dapat melakukan sesuatu seperti ini:
Menggunakan metode ini Anda tidak harus mengimplementasikan
ISerializable
atauICloneable
pada objek Anda. Ini umum dengan pola MVC / MVVM, jadi alat sederhana seperti ini telah dibuat.lihat sampel kloning mendalam ValueInjecter di GitHub .
sumber
Yang terbaik adalah menerapkan metode ekstensi seperti
dan kemudian menggunakannya di mana saja dalam solusi oleh
Kami dapat memiliki tiga implementasi berikut:
Semua metode yang terhubung bekerja dengan baik dan diuji secara mendalam.
sumber
Jawaban singkatnya adalah Anda mewarisi dari antarmuka ICloneable dan kemudian mengimplementasikan fungsi .clone. Klon harus melakukan salinan sesuai dengan anggota dan melakukan salinan dalam pada anggota yang membutuhkannya, lalu mengembalikan objek yang dihasilkan. Ini adalah operasi rekursif (mengharuskan semua anggota kelas yang ingin Anda klon adalah tipe nilai atau mengimplementasikan ICloneable dan anggotanya adalah tipe nilai atau mengimplementasikan ICloneable, dan sebagainya).
Untuk penjelasan lebih rinci tentang Kloning menggunakan ICloneable, lihat artikel ini .
The panjang jawabannya adalah "tergantung". Seperti disebutkan oleh orang lain, ICloneable tidak didukung oleh obat generik, memerlukan pertimbangan khusus untuk referensi kelas melingkar, dan sebenarnya dipandang oleh beberapa orang sebagai "kesalahan" dalam .NET Framework. Metode serialisasi tergantung pada objek Anda yang dapat serial, yang mungkin tidak dan Anda mungkin tidak memiliki kendali atas. Masih ada banyak perdebatan di masyarakat yang merupakan praktik "terbaik". Pada kenyataannya, tidak ada solusi yang cocok untuk semua praktik terbaik untuk semua situasi seperti ICloneable yang awalnya ditafsirkan.
Lihat artikel Pojok Pengembang ini untuk beberapa opsi lagi (dikreditkan ke Ian).
sumber
Bersulang.
sumber
EDIT: proyek dihentikan
Jika Anda ingin kloning yang benar untuk tipe yang tidak dikenal, Anda dapat melihat fastclone .
Itu kloning berbasis ekspresi bekerja sekitar 10 kali lebih cepat daripada serialisasi biner dan mempertahankan integritas grafik objek lengkap.
Itu berarti: jika Anda merujuk beberapa kali ke objek yang sama dalam hierachy Anda, klon juga akan memiliki satu instance menjadi referensi.
Tidak perlu antarmuka, atribut, atau modifikasi lainnya pada objek yang dikloning.
sumber
Buat semuanya tetap sederhana dan gunakan AutoMapper seperti yang disebutkan lainnya, ini adalah perpustakaan kecil sederhana untuk memetakan satu objek ke objek lain ... Untuk menyalin objek ke objek lain dengan tipe yang sama, yang Anda butuhkan adalah tiga baris kode:
Objek target sekarang merupakan salinan dari objek sumber. Tidak cukup sederhana? Buat metode ekstensi untuk digunakan di mana saja dalam solusi Anda:
Metode ekstensi dapat digunakan sebagai berikut:
sumber
Saya datang dengan ini untuk mengatasi kekurangan .NET harus secara manual Daftar copy <T>.
Saya menggunakan ini:
Dan di tempat lain:
Saya mencoba untuk membuat oneliner yang melakukan ini, tetapi itu tidak mungkin, karena hasil tidak bekerja di dalam blok metode anonim.
Lebih baik lagi, gunakan Daftar generik <T> cloner:
sumber
Q. Mengapa saya memilih jawaban ini?
Dengan kata lain, gunakan jawaban lain kecuali Anda memiliki hambatan kinerja yang perlu diperbaiki, dan Anda dapat membuktikannya dengan profiler .
10x lebih cepat dari metode lain
Metode melakukan klon mendalam adalah sebagai berikut:
Dan metodenya ...
Untuk kecepatan tertinggi, Anda dapat menggunakan Nested MemberwiseClone untuk melakukan penyalinan yang dalam . Kecepatannya hampir sama dengan menyalin struct nilai, dan jauh lebih cepat daripada (a) refleksi atau (b) serialisasi (seperti yang dijelaskan dalam jawaban lain di halaman ini).
Perhatikan bahwa jika Anda menggunakan Nested MemberwiseClone untuk salinan yang lebih dalam , Anda harus mengimplementasikan ShallowCopy secara manual untuk setiap level bersarang di kelas, dan DeepCopy yang memanggil semua metode ShallowCopy yang dikatakan untuk membuat klon lengkap. Ini sederhana: total hanya beberapa baris, lihat kode demo di bawah ini.
Berikut adalah output dari kode yang menunjukkan perbedaan kinerja relatif untuk 100.000 klon:
Menggunakan Nested MemberwiseClone di kelas hampir secepat menyalin struct, dan menyalin struct sangat sangat dekat dengan kecepatan maksimum teoritis. NET mampu.
Untuk memahami bagaimana cara menyalin secara mendalam menggunakan MemberwiseCopy, berikut adalah proyek demo yang digunakan untuk menghasilkan waktu di atas:
Lalu, panggil demo dari utama:
Sekali lagi, perhatikan bahwa jika Anda menggunakan Nested MemberwiseClone untuk salinan yang dalam , Anda harus mengimplementasikan ShallowCopy secara manual untuk setiap level bersarang di kelas, dan DeepCopy yang memanggil semua metode ShallowCopy yang dikatakan untuk membuat klon lengkap. Ini sederhana: total hanya beberapa baris, lihat kode demo di atas.
Jenis nilai vs. Jenis Referensi
Perhatikan bahwa ketika datang untuk mengkloning suatu objek, ada perbedaan besar antara " struct " dan " class ":
Lihat perbedaan antara tipe nilai dan tipe referensi .
Checksum untuk membantu dalam debugging
Sangat berguna untuk memisahkan banyak utas dari banyak utas lainnya
Satu kasus penggunaan yang sangat baik untuk kode ini adalah memberi makan klon dari kelas bersarang atau struct ke dalam antrian, untuk menerapkan pola produsen / konsumen.
ConcurrentQueue
.Ini bekerja sangat baik dalam praktiknya, dan memungkinkan kami memisahkan banyak benang (produsen) dari satu atau lebih benang (konsumen).
Dan metode ini juga sangat cepat: jika kita menggunakan struct bersarang, itu 35x lebih cepat dari serialisasi / deserializing kelas bersarang, dan memungkinkan kita untuk mengambil keuntungan dari semua utas yang tersedia pada mesin.
Memperbarui
Rupanya, ExpressMapper lebih cepat, jika tidak lebih cepat, daripada pengkodean tangan seperti di atas. Saya mungkin harus melihat bagaimana mereka membandingkan dengan profiler.
sumber
Secara umum, Anda mengimplementasikan antarmuka ICloneable dan mengimplementasikan sendiri Clone. Objek C # memiliki metode MemberwiseClone bawaan yang melakukan salinan dangkal yang dapat membantu Anda untuk semua primitif.
Untuk salinan yang dalam, tidak mungkin mengetahui cara melakukannya secara otomatis.
sumber
Saya telah melihatnya diimplementasikan melalui refleksi juga. Pada dasarnya ada metode yang akan beralih melalui anggota suatu objek dan menyalinnya dengan tepat ke objek baru. Ketika mencapai jenis referensi atau koleksi saya pikir itu melakukan panggilan rekursif pada dirinya sendiri. Refleksi memang mahal, tetapi itu bekerja dengan cukup baik.
sumber
Berikut ini adalah implementasi dalam salinan:
sumber
Karena saya tidak dapat menemukan cloner yang memenuhi semua persyaratan saya di proyek yang berbeda, saya membuat cloner yang mendalam yang dapat dikonfigurasi dan disesuaikan dengan struktur kode yang berbeda daripada mengadaptasi kode saya untuk memenuhi persyaratan cloners. Itu dicapai dengan menambahkan anotasi ke kode yang akan dikloning atau Anda hanya meninggalkan kode karena memiliki perilaku default. Ini menggunakan refleksi, ketik cache dan didasarkan pada flect yang lebih cepat . Proses kloning sangat cepat untuk sejumlah besar data dan hierarki objek yang tinggi (dibandingkan dengan algoritma berbasis refleksi / serialisasi lainnya).
https://github.com/kalisohn/CloneBehave
Juga tersedia sebagai paket nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0
Sebagai contoh: Kode berikut akan deepClone Address, tetapi hanya melakukan salinan dangkal bidang _currentJob.
sumber
Generator kode
Kami telah melihat banyak ide dari serialisasi tentang implementasi manual hingga refleksi dan saya ingin mengusulkan pendekatan yang sama sekali berbeda menggunakan CGbR Code Generator . Metode menghasilkan klon adalah memori dan CPU efisien dan karenanya 300x lebih cepat sebagai DataContractSerializer standar.
Yang Anda butuhkan hanyalah definisi kelas parsial
ICloneable
dan generator mengerjakan sisanya:Catatan: Versi terbaru memiliki lebih banyak pemeriksaan nol, tetapi saya meninggalkannya untuk pemahaman yang lebih baik.
sumber
Saya suka Copyconstructors seperti itu:
Jika Anda memiliki lebih banyak hal untuk disalin, tambahkan
sumber
Metode ini memecahkan masalah bagi saya:
Gunakan seperti ini:
MyObj a = DeepCopy(b);
sumber
Di sini solusi cepat dan mudah yang berfungsi untuk saya tanpa menyampaikan Serialization / Deserialization.
EDIT : membutuhkan
Begitulah cara saya menggunakannya
sumber
Ikuti langkah ini:
ISelf<T>
denganSelf
properti hanya baca yang kembaliT
, danICloneable<out T>
, yang berasal dariISelf<T>
dan termasuk metodeT Clone()
.CloneBase
tipe yang mengimplementasikanprotected virtual generic VirtualClone
castingMemberwiseClone
ke tipe yang dilewatkan.VirtualClone
dengan memanggil metode clone dasar dan kemudian melakukan apa pun yang perlu dilakukan untuk mengkloning dengan baik aspek-aspek dari tipe turunan yang metode induk VirtualClone belum ditangani.Untuk fleksibilitas pewarisan maksimum, kelas yang mengekspos fungsi kloning publik harus
sealed
, tetapi berasal dari kelas dasar yang identik kecuali untuk kurangnya kloning. Daripada melewati variabel tipe klon yang eksplisit, ambil parameter tipeICloneable<theNonCloneableType>
. Ini akan memungkinkan suatu rutin yang mengharapkan turunan cloneableFoo
untuk bekerja dengan turunan cloneableDerivedFoo
, tetapi juga memungkinkan penciptaan turunan non-cloneable ofFoo
.sumber
Saya pikir Anda dapat mencoba ini.
sumber
Saya telah membuat versi dari jawaban yang diterima yang bekerja dengan '[Serializable]' dan '[DataContract]'. Sudah lama sejak saya menulisnya, tetapi jika saya ingat dengan benar [DataContract] membutuhkan serializer yang berbeda.
Membutuhkan System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
sumber
Ok, ada beberapa contoh jelas dengan refleksi di posting ini, TAPI refleksi biasanya lambat, sampai Anda mulai men-cache dengan benar.
jika Anda akan men-cache dengan benar, maka itu akan mengkloning dalam 1000000 objek dengan 4,6s (diukur oleh Watcher).
daripada Anda mengambil properti cache atau menambahkan baru ke kamus dan menggunakannya secara sederhana
periksa kode lengkap di posting saya di jawaban lain
https://stackoverflow.com/a/34365709/4711853
sumber
prop.GetValue(...)
masih refleksi dan tidak bisa di-cache. Dalam pohon ekspresi yang dikompilasi, jadi lebih cepatKarena hampir semua jawaban untuk pertanyaan ini tidak memuaskan atau jelas tidak berfungsi dalam situasi saya, saya telah menulis AnyClone yang sepenuhnya diimplementasikan dengan refleksi dan menyelesaikan semua kebutuhan di sini. Saya tidak bisa mendapatkan serialisasi untuk bekerja dalam skenario yang rumit dengan struktur yang kompleks, dan
IClonable
kurang ideal - bahkan seharusnya tidak perlu.Atribut abaikan standar didukung menggunakan
[IgnoreDataMember]
,[NonSerialized]
. Mendukung koleksi yang kompleks, properti tanpa seter, bidang baca saja dll.Saya harap ini membantu orang lain di luar sana yang mengalami masalah yang sama dengan saya.
sumber
Penafian: Saya penulis paket yang disebutkan.
Saya terkejut bagaimana jawaban teratas untuk pertanyaan ini pada tahun 2019 masih menggunakan serialisasi atau refleksi.
Serialisasi terbatas (memerlukan atribut, konstruktor tertentu, dll.) Dan sangat lambat
BinaryFormatter
membutuhkanSerializable
atribut,JsonConverter
memerlukan konstruktor tanpa parameter atau atribut, tidak menangani bidang baca saja atau antarmuka dengan sangat baik dan keduanya 10-30x lebih lambat dari yang diperlukan.Pohon Ekspresi
Anda bisa menggunakan Pohon Ekspresi atau Refleksi. Sebaliknya untuk menghasilkan kode kloning hanya sekali, kemudian gunakan kode yang dikompilasi itu bukan refleksi lambat atau serialisasi.
Setelah menemukan masalah sendiri dan tidak melihat solusi yang memuaskan, saya memutuskan untuk membuat paket yang melakukan hal itu dan bekerja dengan setiap jenis dan hampir secepat kode tertulis kustom .
Anda dapat menemukan proyek di GitHub: https://github.com/marcelltoth/ObjectCloner
Pemakaian
Anda dapat menginstalnya dari NuGet. Dapatkan
ObjectCloner
paket dan gunakan sebagai:atau jika Anda tidak keberatan mencemari jenis objek Anda dengan ekstensi
ObjectCloner.Extensions
juga dan menulis:Performa
Tolok ukur sederhana kloning hierarki kelas menunjukkan kinerja ~ 3x lebih cepat daripada menggunakan Refleksi, ~ 12x lebih cepat dari serialisasi Newtonsoft.Json dan ~ 36x lebih cepat dari yang sangat disarankan
BinaryFormatter
.sumber