Bagaimana cara mengkloning daftar umum dalam C #?

593

Saya memiliki daftar umum objek di C #, dan ingin mengkloning daftar. Item dalam daftar dapat dikloning, tetapi sepertinya tidak ada pilihan untuk dilakukan list.Clone().

Apakah ada cara mudah untuk mengatasi ini?

Fiona
sumber
44
Anda harus mengatakan jika Anda mencari salinan yang dalam atau salinan yang dangkal
orip
10
Apa itu salinan yang dalam dan dangkal?
Kolonel Panic
3
@orip. Bukankah clone()definisi salinan dalam? Dalam C # Anda dapat mengoper pointer dengan mudah dengan =, saya pikir.
Chris
13
@ Chris menyalin salinan dangkal satu tingkat lebih dalam dari salinan pointer. Misalnya salinan daftar yang dangkal akan memiliki elemen yang sama, tetapi akan menjadi daftar yang berbeda.
orip

Jawaban:

385

Anda dapat menggunakan metode ekstensi.

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}
ajm
sumber
71
Saya pikir List.ConvertAll mungkin melakukan ini dalam waktu yang lebih cepat, karena ia dapat pra-mengalokasikan seluruh array untuk daftar, dibandingkan harus mengubah ukuran sepanjang waktu.
MichaelGG
2
@MichaelGG, bagaimana jika Anda tidak ingin Mengkonversi tetapi hanya Mengkloning / Menduplikasi item dalam daftar? Apakah ini akan berhasil? || var clonedList = ListOfStrings.ConvertAll (p => p);
IbrarMumtaz
29
@IbrarMumtaz: Itu sama dengan var clonedList = Daftar baru <string> (ListOfStrings);
Brandon Arnold
4
Solusi bagus! Ngomong-ngomong, saya lebih suka Daftar statis publik <T> CLone <T> ... Ini lebih berguna dalam kasus-kasus seperti ini, karena tidak diperlukan pemeran lebih lanjut: Daftar <MyType> dikloning = listToClone.Clone ();
Plutoz
2
ini kloning yang dalam
George Birbilis
513

Jika elemen Anda adalah tipe nilai, maka Anda bisa melakukannya:

List<YourType> newList = new List<YourType>(oldList);

Namun, jika mereka adalah tipe referensi dan Anda ingin salinan yang dalam (dengan asumsi elemen Anda diterapkan dengan benar ICloneable), Anda bisa melakukan sesuatu seperti ini:

List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);

oldList.ForEach((item) =>
    {
        newList.Add((ICloneable)item.Clone());
    });

Jelas, ganti ICloneablegenerik di atas dan gunakan dengan apa pun jenis elemen Anda yang mengimplementasikan ICloneable.

Jika tipe elemen Anda tidak mendukung ICloneabletetapi memang memiliki copy-constructor, Anda bisa melakukan ini sebagai gantinya:

List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);

oldList.ForEach((item)=>
    {
        newList.Add(new YourType(item));
    });

Secara pribadi, saya akan menghindari ICloneablekarena kebutuhan untuk menjamin salinan yang mendalam dari semua anggota. Sebagai gantinya, saya menyarankan copy-constructor atau metode pabrik seperti YourType.CopyFrom(YourType itemToCopy)itu mengembalikan contoh baru YourType.

Opsi-opsi ini dapat dibungkus dengan metode (ekstensi atau lainnya).

Jeff Yates
sumber
1
Saya pikir List <T> .ConvertAll mungkin terlihat lebih bagus daripada membuat daftar baru dan melakukan foreach + add.
MichaelGG
2
@ Dimitri: Tidak, itu tidak benar. Masalahnya adalah, ketika ICloneabledidefinisikan, definisi tidak pernah menyatakan apakah klon itu dalam atau dangkal, jadi Anda tidak dapat menentukan jenis operasi Klon yang akan dilakukan ketika suatu objek mengimplementasikannya. Ini berarti bahwa jika Anda ingin melakukan kloning yang mendalam List<T>, Anda harus melakukannya tanpa ICloneablememastikan itu adalah salinan yang dalam.
Jeff Yates
5
Mengapa tidak menggunakan metode AddRange? ( newList.AddRange(oldList.Select(i => i.Clone())atau newList.AddRange(oldList.Select(i => new YourType(i))
phoog
5
@phoog: Saya pikir itu sedikit kurang dapat dibaca / dimengerti ketika memindai kode, itu saja. Keterbacaan menang untuk saya.
Jeff Yates
1
@ JeffYates: Satu kerutan yang dianggap tidak cukup adalah bahwa hal-hal yang umumnya hanya perlu disalin jika ada beberapa jalur eksekusi yang akan memutasi mereka. Ini sangat umum untuk memiliki jenis kekal memegang referensi ke sebuah contoh dari jenis bisa berubah, tetapi tidak pernah mengekspos contoh bahwa untuk apa pun yang akan bermutasi itu. Penyalinan yang tidak perlu dari hal-hal yang tidak akan pernah berubah kadang-kadang dapat menguras kinerja utama , meningkatkan penggunaan memori dengan urutan besarnya.
supercat
84

Untuk salinan yang dangkal, Anda bisa menggunakan metode GetRange dari kelas Daftar generik.

List<int> oldList = new List<int>( );
// Populate oldList...

List<int> newList = oldList.GetRange(0, oldList.Count);

Dikutip dari: Resep Generik

Anthony Potts
sumber
43
Anda juga dapat mencapainya dengan menggunakan kontruktor Daftar <T> untuk menentukan Daftar <T> yang digunakan untuk menyalin. misalnya var shallowClonedList = Daftar baru <MyObject> (originalList);
Arkiliknam
9
Saya sering menggunakan List<int> newList = oldList.ToList(). Efek yang sama. Namun, solusi Arkiliknam adalah yang terbaik untuk keterbacaan menurut saya.
Dan Bechard
82
public static object DeepClone(object obj) 
{
  object objResult = null;
  using (MemoryStream  ms = new MemoryStream())
  {
    BinaryFormatter  bf =   new BinaryFormatter();
    bf.Serialize(ms, obj);

    ms.Position = 0;
    objResult = bf.Deserialize(ms);
  }
  return objResult;
}

Ini adalah salah satu cara untuk melakukannya dengan C # dan .NET 2.0. Objek Anda harus seperti itu [Serializable()]. Tujuannya adalah untuk kehilangan semua referensi dan membangun yang baru.

Patrick Desjardins
sumber
11
+1 - saya suka jawaban ini - cepat, kotor, tidak menyenangkan, dan sangat efektif. Saya menggunakan silverlight, dan menggunakan DataContractSerializer karena BinarySerializer tidak tersedia. Siapa yang perlu menulis halaman kode kloning objek ketika Anda bisa melakukan ini? :)
slugster
3
Saya suka ini. Meskipun senang melakukan hal-hal "benar", cepat dan kotor sering kali berguna.
Odrade
3
Cepat! tapi: Kenapa kotor?
raiserle
2
Ini klon yang dalam dan cepat dan mudah. Hati-hati dengan saran lain di halaman ini. Saya mencoba beberapa dan mereka tidak mengkloning dalam.
RandallTo
2
Hanya aspek negatifnya, jika Anda bisa menyebutnya begitu, adalah bahwa kelas Anda harus ditandai Serializable agar ini berfungsi.
Tuukka Haapaniemi
30

Untuk mengkloning daftar, panggil saja. Daftar (). Ini menciptakan salinan yang dangkal.

Microsoft (R) Roslyn C# Compiler version 2.3.2.62116
Loading context from 'CSharpInteractive.rsp'.
Type "#help" for more information.
> var x = new List<int>() { 3, 4 };
> var y = x.ToList();
> x.Add(5)
> x
List<int>(3) { 3, 4, 5 }
> y
List<int>(2) { 3, 4 }
> 
Xavier John
sumber
3
Solusi paling sederhana sejauh ini
curveorzos
29
Peringatan kecil ini adalah salinan dangkal ... Ini akan membuat dua objek daftar, tetapi objek di dalamnya akan sama. Yaitu mengubah satu properti akan mengubah objek / properti yang sama dalam daftar asli.
Mark G
22

Setelah sedikit modifikasi, Anda juga dapat mengkloning:

public static T DeepClone<T>(T obj)
{
    T objResult;
    using (MemoryStream ms = new MemoryStream())
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(ms, obj);
        ms.Position = 0;
        objResult = (T)bf.Deserialize(ms);
    }
    return objResult;
}
Ajith
sumber
Jangan lupa T harus berseri, jika tidak Anda mendapatkan System.Runtime.Serialization.SerializationException.
Bence Végert
Jawaban yang bagus. Satu petunjuk: Anda bisa menambahkan if (!obj.GetType().IsSerializable) return default(T);sebagai pernyataan pertama yang mencegah pengecualian. Dan jika Anda mengubahnya ke metode ekstensi, Anda bahkan bisa menggunakan operator Elvis seperti var b = a?.DeepClone();(diberikan var a = new List<string>() { "a", "b" }; misalnya).
Matt
15

Kecuali jika Anda membutuhkan tiruan aktual dari setiap objek di dalam diri Anda List<T>, cara terbaik untuk mengkloning daftar adalah dengan membuat daftar baru dengan daftar lama sebagai parameter koleksi.

List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);

Perubahan myListseperti menyisipkan atau menghapus tidak akan memengaruhi cloneOfMyListdan sebaliknya.

Objek aktual kedua Daftar berisi masih sama namun.

Jader Feijo
sumber
Saya setuju dengan user49126, saya melihat bahwa itu adalah salinan dangkal dan perubahan yang dibuat untuk satu daftar tercermin dalam daftar lainnya.
Seidleroni
1
@ Seidleroni, Anda salah. Perubahan yang dilakukan pada daftar yang terpengaruh pada daftar lainnya, perubahan dalam daftar itu sendiri tidak.
Wellington Zanelli
Ini adalah salinan dangkal.
Elliot Chen
Bagaimana ini salinan dangkal?
mko
2
@WellingtonZanelli Baru saja mengkonfirmasi bahwa menghapus elemen dari myList juga menghapusnya dari cloneOfMyList.
Nick Gallimore
13

Gunakan AutoMapper (atau pemetaan pemetaan apa pun yang Anda inginkan) untuk mengkloning itu sederhana dan banyak yang bisa dipertahankan.

Tentukan pemetaan Anda:

Mapper.CreateMap<YourType, YourType>();

Lakukan keajaiban:

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
Derek Liang
sumber
13

Jika Anda hanya peduli tentang jenis nilai ...

Dan Anda tahu tipenya:

List<int> newList = new List<int>(oldList);

Jika Anda tidak tahu jenisnya sebelumnya, Anda akan membutuhkan fungsi pembantu:

List<T> Clone<T>(IEnumerable<T> oldList)
{
    return newList = new List<T>(oldList);
}

Yang adil:

List<string> myNewList = Clone(myOldList);
James Curran
sumber
15
Ini tidak mengkloning elemen.
Jeff Yates
10

Jika Anda telah mereferensikan Newtonsoft.Json dalam proyek Anda dan objek Anda serial, Anda selalu dapat menggunakan:

List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))

Mungkin bukan cara yang paling efisien untuk melakukannya, tetapi kecuali Anda melakukannya 100-an dari 1000-an kali Anda bahkan mungkin tidak melihat perbedaan kecepatan.

ProfNimrod
sumber
4
Ini bukan tentang perbedaan kecepatan, ini tentang keterbacaan. Jika saya sampai pada baris kode ini, saya akan menampar kepala saya dan bertanya-tanya mengapa mereka memperkenalkan perpustakaan pihak ketiga untuk diserialisasi dan kemudian membatalkan registrasi objek yang saya tidak tahu mengapa itu terjadi. Juga, ini tidak akan berfungsi untuk daftar model dengan objek yang memiliki struktur lingkaran.
Jonathon Cwik
1
Kode ini bekerja sangat baik bagi saya untuk kloning yang dalam. Aplikasi ini memigrasi boilerplate dokumen dari Dev ke QA ke Prod. Setiap objek adalah paket dari beberapa objek templat dokumen, dan setiap dokumen pada gilirannya terdiri dari daftar objek paragraf. Kode ini izinkan saya membuat serialisasi objek .NET "source" dan segera membatalkan deserialisasi objek-objek "target" baru, yang kemudian disimpan ke database SQL di lingkungan yang berbeda. Setelah banyak penelitian, saya menemukan banyak barang, banyak yang terlalu rumit, dan memutuskan untuk mencoba ini. Pendekatan singkat dan fleksibel ini "tepat"!
Pengembang63
3
public static Object CloneType(Object objtype)
{
    Object lstfinal = new Object();

    using (MemoryStream memStream = new MemoryStream())
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
        binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
        lstfinal = binaryFormatter.Deserialize(memStream);
    }

    return lstfinal;
}
pratik
sumber
3
public class CloneableList<T> : List<T>, ICloneable where T : ICloneable
{
  public object Clone()
  {
    var clone = new List<T>();
    ForEach(item => clone.Add((T)item.Clone()));
    return clone;
  }
}
Peter
sumber
3
    public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new()
    {
        List<TEntity> retList = new List<TEntity>();
        try
        {
            Type sourceType = typeof(TEntity);
            foreach(var o1 in o1List)
            {
                TEntity o2 = new TEntity();
                foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
                {
                    var val = propInfo.GetValue(o1, null);
                    propInfo.SetValue(o2, val);
                }
                retList.Add(o2);
            }
            return retList;
        }
        catch
        {
            return retList;
        }
    }
shahrooz.bazrafshan
sumber
3

Teman saya Gregor Martinovic dan saya datang dengan solusi mudah ini menggunakan JavaScript Serializer. Tidak perlu menandai kelas sebagai Serializable dan dalam pengujian kami menggunakan Newtonsoft JsonSerializer bahkan lebih cepat daripada menggunakan BinaryFormatter. Dengan metode ekstensi dapat digunakan pada setiap objek.

Opsi .NET JavascriptSerializer standar:

public static T DeepCopy<T>(this T value)
{
    JavaScriptSerializer js = new JavaScriptSerializer();

    string json = js.Serialize(value);

    return js.Deserialize<T>(json);
}

Opsi lebih cepat menggunakan Newtonsoft JSON :

public static T DeepCopy<T>(this T value)
{
    string json = JsonConvert.SerializeObject(value);

    return JsonConvert.DeserializeObject<T>(json);
}
FH
sumber
2
Anggota pribadi tidak dikloning menggunakan metode JSON. stackoverflow.com/a/78612/885627
himanshupareek66
3
 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();
Steve
sumber
3

Saya akan beruntung jika ada yang pernah membaca ini ... tetapi untuk tidak mengembalikan daftar objek tipe dalam metode Clone saya, saya membuat antarmuka:

public interface IMyCloneable<T>
{
    T Clone();
}

Lalu saya tentukan ekstensi:

public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
    return listToClone.Select(item => (T)item.Clone()).ToList();
}

Dan ini adalah implementasi dari antarmuka di perangkat lunak penandaan A / V saya. Saya ingin agar metode Clone () saya mengembalikan daftar VidMark (sedangkan antarmuka ICloneable ingin metode saya mengembalikan daftar objek):

public class VidMark : IMyCloneable<VidMark>
{
    public long Beg { get; set; }
    public long End { get; set; }
    public string Desc { get; set; }
    public int Rank { get; set; } = 0;

    public VidMark Clone()
    {
        return (VidMark)this.MemberwiseClone();
    }
}

Dan akhirnya, penggunaan ekstensi di dalam kelas:

private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;

//Other methods instantiate and fill the lists

private void SetUndoVidMarks()
{
    _UndoVidMarks = _VidMarks.Clone();
}

Adakah yang menyukainya? Adakah perbaikan?

John Kurtz
sumber
2

Anda juga bisa dengan mudah mengonversi daftar menjadi array menggunakan ToArray, dan kemudian mengkloning array menggunakan Array.Clone(...). Tergantung pada kebutuhan Anda, metode yang termasuk dalam kelas Array dapat memenuhi kebutuhan Anda.

JHaps
sumber
Ini tidak bekerja; perubahan nilai dalam array yang dikloning MASIH mengubah nilai dalam daftar asli.
Bernoulli Lizard
Anda dapat menggunakan var clonedList = ListOfStrings.ConvertAll (p => p); seperti yang diberikan oleh @IbrarMumtaz .... Berfungsi secara efektif ... Perubahan pada satu daftar disimpan untuk dirinya sendiri dan tidak mencerminkan yang lain
zainul
2

Anda dapat menggunakan metode ekstensi:

namespace extension
{
    public class ext
    {
        public static List<double> clone(this List<double> t)
        {
            List<double> kop = new List<double>();
            int x;
            for (x = 0; x < t.Count; x++)
            {
                kop.Add(t[x]);
            }
            return kop;
        }
   };

}

Anda dapat mengkloning semua objek dengan menggunakan anggota tipe nilai mereka misalnya, pertimbangkan kelas ini:

public class matrix
{
    public List<List<double>> mat;
    public int rows,cols;
    public matrix clone()
    { 
        // create new object
        matrix copy = new matrix();
        // firstly I can directly copy rows and cols because they are value types
        copy.rows = this.rows;  
        copy.cols = this.cols;
        // but now I can no t directly copy mat because it is not value type so
        int x;
        // I assume I have clone method for List<double>
        for(x=0;x<this.mat.count;x++)
        {
            copy.mat.Add(this.mat[x].clone());
        }
        // then mat is cloned
        return copy; // and copy of original is returned 
    }
};

Catatan: jika Anda melakukan perubahan apa pun pada salinan (atau kloning) itu tidak akan mempengaruhi objek asli.

Furkan Katı
sumber
2

Jika Anda membutuhkan daftar yang dikloning dengan kapasitas yang sama, Anda dapat mencoba ini:

public static List<T> Clone<T>(this List<T> oldList)
{
    var newList = new List<T>(oldList.Capacity);
    newList.AddRange(oldList);
    return newList;
}
pengguna3245269
sumber
1

Saya telah membuat sendiri beberapa ekstensi yang mengubah ICollection item yang tidak menerapkan IClonable

static class CollectionExtensions
{
    public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
    {
        var array = new T[listToClone.Count];
        listToClone.CopyTo(array,0);
        return array.ToList();
    }
}
Kamil Budziewski
sumber
tampaknya beberapa koleksi (misalnya DataGrid's SelectedItems at Silverlight) melewatkan implementasi CopyTo yang merupakan masalah dengan pendekatan ini
George Birbilis
1

Saya menggunakan automapper untuk menyalin objek. Saya baru saja mengatur pemetaan yang memetakan satu objek ke dirinya sendiri. Anda dapat membungkus operasi ini dengan cara apa pun yang Anda suka.

http://automapper.codeplex.com/

Dan H
sumber
1

Menggunakan gips mungkin bermanfaat, dalam hal ini, untuk salinan dangkal:

IList CloneList(IList list)
{
    IList result;
    result = (IList)Activator.CreateInstance(list.GetType());
    foreach (object item in list) result.Add(item);
    return result;
}

diterapkan ke daftar umum:

List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);
Thomas Cerny
sumber
1

Untuk salinan yang dalam, ICloneable adalah solusi yang tepat, tetapi inilah pendekatan yang mirip dengan ICloneable menggunakan konstruktor alih-alih antarmuka ICloneable.

public class Student
{
  public Student(Student student)
  {
    FirstName = student.FirstName;
    LastName = student.LastName;
  }

  public string FirstName { get; set; }
  public string LastName { get; set; }
}

// wherever you have the list
List<Student> students;

// and then where you want to make a copy
List<Student> copy = students.Select(s => new Student(s)).ToList();

Anda memerlukan pustaka berikut tempat Anda membuat salinannya

using System.Linq

Anda juga bisa menggunakan for for bukan System.Linq, tetapi Linq membuatnya ringkas dan bersih. Anda juga dapat melakukan seperti yang disarankan oleh jawaban lain dan membuat metode penyuluhan, dll., Tetapi tidak ada yang diperlukan.

ztorstri
sumber
Itu disebut "copy constructor". Itu pendekatan yang rawan kesalahan, setiap kali Anda menambahkan bidang baru ke Siswa, Anda harus ingat untuk menambahkannya ke copy constructor. Gagasan utama di balik "klon" adalah untuk menghindari masalah itu.
kenno
2
Bahkan dengan ICloneable, Anda harus memiliki metode "Klon" di kelas Anda. Kecuali Anda menggunakan refleksi (yang juga bisa Anda gunakan dalam pendekatan di atas), metode Clone akan terlihat sangat mirip dengan pendekatan copy constructor di atas, dan akan mengalami masalah yang sama karena harus memperbarui bidang baru / yang diubah. Tetapi itu mengatakan "Kelas harus diperbarui ketika bidang kelas berubah". Tentu saja;)
ztorstri
0

Kode berikut harus ditransfer ke daftar dengan perubahan minimal.

Pada dasarnya ini bekerja dengan memasukkan nomor acak baru dari rentang yang lebih besar dengan setiap loop berturut-turut. Jika sudah ada angka yang sama atau lebih tinggi dari itu, pindahkan angka-angka acak ke atas sehingga mereka mentransfer ke kisaran indeks acak baru yang lebih besar.

// Example Usage
int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);

for(int i = 0; i < toSet.Length; i++)
    toSet[i] = selectFrom[indexes[i]];


private int[] getRandomUniqueIndexArray(int length, int count)
{
    if(count > length || count < 1 || length < 1)
        return new int[0];

    int[] toReturn = new int[count];
    if(count == length)
    {
        for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
        return toReturn;
    }

    Random r = new Random();
    int startPos = count - 1;
    for(int i = startPos; i >= 0; i--)
    {
        int index = r.Next(length - i);
        for(int j = startPos; j > i; j--)
            if(toReturn[j] >= index)
                toReturn[j]++;
        toReturn[i] = index;
    }

    return toReturn;
}
Adam Lewis
sumber
0

Hal lain: Anda bisa menggunakan refleksi. Jika Anda akan menembolok ini dengan benar, maka itu akan mengklon 1.000.000 objek dalam 5,6 detik (sayangnya, 16,4 detik dengan objek dalam).

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Person
{
       ...
      Job JobDescription
       ...
}

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Job
{...
}

private static readonly Type stringType = typeof (string);

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

    private static readonly MethodInfo CreateCopyReflectionMethod;

    static CopyFactory()
    {
        CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
    }

    public static T CreateCopyReflection<T>(T source) where T : new()
    {
        var copyInstance = new T();
        var sourceType = typeof(T);

        PropertyInfo[] propList;
        if (ProperyList.ContainsKey(sourceType))
            propList = ProperyList[sourceType];
        else
        {
            propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            ProperyList.Add(sourceType, propList);
        }

        foreach (var prop in propList)
        {
            var value = prop.GetValue(source, null);
            prop.SetValue(copyInstance,
                value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
        }

        return copyInstance;
    }

Saya mengukurnya dengan cara yang sederhana, dengan menggunakan kelas Watcher.

 var person = new Person
 {
     ...
 };

 for (var i = 0; i < 1000000; i++)
 {
    personList.Add(person);
 }
 var watcher = new Stopwatch();
 watcher.Start();
 var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
 watcher.Stop();
 var elapsed = watcher.Elapsed;

HASIL: Dengan objek dalam PersonInstance - 16.4, PersonInstance = null - 5.6

CopyFactory hanyalah kelas pengujian saya di mana saya memiliki selusin tes termasuk penggunaan ekspresi. Anda bisa menerapkan ini dalam bentuk lain dalam ekstensi atau apa pun. Jangan lupa tentang caching.

Saya belum menguji serialisasi, tapi saya ragu dengan peningkatan dengan sejuta kelas. Saya akan mencoba sesuatu protobuf cepat / newton.

PS: Demi membaca kesederhanaan, saya hanya menggunakan properti otomatis di sini. Saya dapat memperbarui dengan FieldInfo, atau Anda harus dengan mudah mengimplementasikannya sendiri.

Saya baru-baru ini menguji serializer Protokol Buffer dengan fungsi DeepClone di luar kotak. Ia menang dengan 4,2 detik pada sejuta objek sederhana, tetapi ketika sampai pada objek dalam, ia menang dengan hasil 7,4 detik.

Serializer.DeepClone(personList);

RINGKASAN: Jika Anda tidak memiliki akses ke kelas, maka ini akan membantu. Kalau tidak, itu tergantung pada jumlah objek. Saya pikir Anda bisa menggunakan refleksi hingga 10.000 objek (mungkin sedikit kurang), tetapi untuk lebih dari ini serializer Protokol Buffer akan tampil lebih baik.

Roma Borodov
sumber
0

Ada cara sederhana untuk mengkloning objek di C # menggunakan serializer dan deserializer JSON.

Anda dapat membuat kelas ekstensi:

using Newtonsoft.Json;

static class typeExtensions
{
    [Extension()]
    public static T jsonCloneObject<T>(T source)
    {
    string json = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(json);
    }
}

Untuk mengkloning dan objek:

obj clonedObj = originalObj.jsonCloneObject;
Albert arnau
sumber