C# & # 39; de genel bir listeyi nasıl klonlayabilirim?

C# nesnelerinin genel bir listesi var ve listeyi klonlamak istiyorum. Listedeki öğeler klonlanabilir, ancak list.Clone() yapmak için bir seçenek yok gibi görünüyor .

bunun etrafında kolay bir yol var mı?

472
tarihinde sordu Peter Mortensen 2008-10-21 20:47:05
kaynak

23 ответов

bir uzatma yöntemi kullanabilirsiniz.

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}
316
cevap ajm 2013-09-23 23:11:35
kaynak

öğeleriniz değer türleri ise,

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

ancak, referans türleri ve derin bir kopya istiyorsanız (öğelerinizin ICloneable doğru bir şekilde uygulandığını varsayarsak), şöyle bir şey yapabilirsiniz:

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

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

açıkçası, yukarıdaki jenerik ICloneable yerine ve ICloneable uygulayan öğe türü ne olursa olsun ile döküm .

eğer öğe türü ICloneable yi desteklemiyor ancak bir kopya oluşturucu var, bunun yerine bunu yapabilirsiniz:

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

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

şahsen, tüm üyelerin derin bir kopyasını garanti etme ihtiyacı nedeniyle ICloneable dan kaçınırım. Bunun yerine, kopya oluşturucu veya YourType.CopyFrom(YourType itemToCopy) yeni bir örneğini döndüren YourType gibi bir fabrika yöntemini öneririm .

bu seçeneklerden herhangi biri bir yöntemle (uzantı veya başka bir şekilde) sarılabilir.

422
cevap Jeff Yates 2008-10-21 21:06:22
kaynak
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;
}

bu, C# ve.net 2.0 ile yapmanın bir yoludur. Nesnenin [Serializable()] olması gerekir . Amaç tüm referansları kaybetmek ve yenilerini inşa etmektir.

72
cevap Patrick Desjardins 2015-07-09 16:21:47
kaynak
Sığ bir kopya için

, bunun yerine genel liste sınıfının GetRange yöntemini kullanabilirsiniz.

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

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

alıntı: jenerik Tarifleri

61
cevap Anthony Potts 2018-04-07 17:15:37
kaynak

hafif bir değişiklikten sonra da klonlayabilirsiniz:

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;
}
19
cevap Ajith 2012-12-03 10:25:43
kaynak

, List<T> içindeki her nesnenin gerçek bir klonuna ihtiyacınız yoksa, bir listeyi klonlamanın en iyi yolu, koleksiyon parametresi olarak eski liste ile yeni bir liste oluşturmaktır.

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

Ekle veya Kaldır gibi myList değişiklikleri cloneOfMyList ve tam tersi etkilemez.

iki liste içeren gerçek nesneler yine de aynıdır.

14
cevap Jader Feijo 2015-07-10 17:09:54
kaynak

klonlamak için AutoMapper (veya tercih ettiğiniz herhangi bir eşleme lib) kullanın basit ve çok sürdürülebilir.

Haritanızı tanımlayın:

Mapper.CreateMap<YourType, YourType>();

büyüyü yap:

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
13
cevap Derek Liang 2013-02-14 03:20:22
kaynak

bir liste sadece çağrı klonlamak için .ToList ()

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 }
> 
13
cevap Xavier John 2017-09-25 03:35:03
kaynak

sadece değer türlerini önemsiyorsanız...

ve türünü biliyorum:

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

daha önce türünü bilmiyorsanız, bir yardımcı işleve ihtiyacınız olacak:

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

sadece:

List<string> myNewList = Clone(myOldList);
12
cevap James Curran 2013-04-15 17:08:48
kaynak

zaten newtonsoft başvurduysanız.Projenizdeki json ve nesneleriniz her zaman kullanabileceğiniz seri hale getirilebilir:

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

muhtemelen bunu yapmanın en etkili yolu değil, ancak 100'lük 100'lük 100'lük bir sürede yapmazsanız hız farkını bile fark edemezsiniz.

7
cevap ProfNimrod 2013-11-01 18:43:56
kaynak
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;
}
3
cevap pratik 2011-04-25 20:22:23
kaynak
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;
  }
}
3
cevap Peter 2011-10-07 11:04:00
kaynak
    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;
        }
    }
3
cevap shahrooz.bazrafshan 2016-04-10 10:40:51
kaynak

ayrıca listeyi ToArray kullanarak bir diziye dönüştürebilir ve ardından Array.Clone(...) kullanarak diziyi klonlayabilirsiniz . İhtiyaçlarınıza bağlı olarak, Array sınıfında yer alan yöntemler ihtiyaçlarınızı karşılayabilir.

2
cevap JHaps 2015-01-15 18:34:44
kaynak

uzatma yöntemini kullanabilirsiniz:

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

}

sen-ebilmek klonlamak tüm nesneleri kullanarak kendi değer türü, örneğin, bu sınıf düşünün:

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

not: kopya (veya klon) üzerinde herhangi bir değişiklik yaparsanız orijinal nesneyi etkilemez.

2
cevap user2463322 2016-09-26 12:08:21
kaynak

aynı kapasiteye sahip klonlanmış bir listeye ihtiyacınız varsa, bunu deneyebilirsiniz:

public static List<T> Clone<T>(this List<T> oldList)
{
    var newList = new List<T>(oldList.Capacity);
    newList.AddRange(oldList);
    return newList;
}
2
cevap user3245269 2017-05-16 15:02:38
kaynak

arkadaşım Gregor Martinovic ve bir JavaScript Serileştirici kullanarak bu kolay çözümü buldum. Sınıfları seri hale getirilebilir olarak işaretlemeye ve testlerimizde Newtonsoft Jsonserializer'ı BinaryFormatter kullanmaktan daha hızlı kullanmaya gerek yoktur. Her nesne üzerinde kullanılabilir uzatma yöntemleri ile.

standart. net JavascriptSerializer seçeneği:

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

    string json = js.Serialize(value);

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

kullanarak daha hızlı seçenek Newtonsoft JSON :

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

    return JsonConvert.DeserializeObject<T>(json);
}
2
cevap F.H. 2017-05-16 15:22:51
kaynak

Ben Icl uygulamak değil öğelerin İCollection dönüştürür kendi bazı uzantısı için yaptık

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();
    }
}
1
cevap wudzik 2013-07-03 16:41:06
kaynak

bir nesneyi kopyalamak için automapper kullanıyorum. Sadece bir nesneyi kendisine eşleyen bir eşleme ayarladım. Bu işlemi istediğiniz gibi sarabilirsiniz.

http://automapper.codeplex.com/

1
cevap Dan H 2014-10-13 18:28:08
kaynak

aşağıdaki kod, minimal değişikliklerle bir listeye aktarılmalıdır.

temelde her ardışık döngü ile daha büyük bir aralıktan yeni bir rasgele sayı ekleyerek çalışır. Zaten aynı veya daha yüksek olan sayılar varsa, bu rasgele sayıları bir tane yukarı kaydırın, böylece yeni daha büyük rasgele indeks aralığına aktarırlar.

// 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;
}
0
cevap Adam Lewis 2017-05-16 14:56:03
kaynak

başka bir şey: yansıma kullanabilirsiniz. Bunu düzgün bir şekilde önbelleğe alırsanız, 5.6 saniyede 1,000,000 nesneleri klonlayacaktır (ne yazık ki, iç nesnelerle 16.4 saniye).

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

Watcher sınıfını kullanarak basit bir şekilde ölçtüm.

 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;

sonuç: iç nesne ile Personınstance - 16.4, Personınstance = null - 5.6

CopyFactory, sahip olduğum test sınıfım ifadenin kullanımı da dahil olmak üzere düzinelerce test. Bunu bir uzantıda veya her neyse başka bir biçimde uygulayabilirsiniz. Önbelleğe almayı unutma.

henüz seri hale getirmeyi test etmedim, ancak bir milyon sınıfla bir iyileşmeden şüpheliyim. Hızlı bir şey deneyeceğim protobuf / newton.

not: sadeliği okumak uğruna, sadece burada otomatik mülk kullandım. Fieldınfo ile güncelleyebilirim veya bunu kendi başınıza kolayca uygulamanız gerekir.

son zamanlarda Protokol Tamponlarını serializer'ı kutunun dışında DeepClone fonksiyonu ile test ettim. Bir milyon basit nesnede 4.2 saniye ile kazanır, ancak iç nesneler söz konusu olduğunda, sonuç 7.4 saniye ile kazanır.

Serializer.DeepClone(personList);

özeti: sınıflara erişiminiz yoksa, bu yardımcı olacaktır. Aksi takdirde nesnelerin sayısına bağlıdır. Bence yansımayı 10,000'e kadar kullanabilirsin. nesneler (belki biraz daha az), ancak bundan daha fazlası için protokol arabellekleri serializer daha iyi performans gösterecektir.

0
cevap Roma Borodov 2017-05-16 15:02:11
kaynak

# JSON seri hale getirici ve deserializer kullanarak C nesneleri klonlamak için basit bir yoludur.

bir uzantı sınıfı oluşturabilirsiniz:

using Newtonsoft.Json;

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

klon ve nesne için:

obj clonedObj = originalObj.jsonCloneObject;
0
cevap Albert arnau 2017-05-16 15:23:34
kaynak
 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();
0
cevap Steve 2018-02-18 07:58:56
kaynak

Diğer sorular c# generics list clone