Hoe kloon ik een generieke lijst in C#?

Ik heb een generieke lijst met objecten in C# en wil de lijst klonen. De items in de lijst kunnen worden gekloond, maar er lijkt geen optie te zijn om list.Clone()te doen.

Is er een gemakkelijke manier om dit te omzeilen?


Antwoord 1, autoriteit 100%

U kunt een extensiemethode gebruiken.

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}

Antwoord 2, autoriteit 99%

Als uw elementen waardetypes zijn, kunt u het volgende doen:

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

Als het echter referentietypes zijn en u een diepe kopie wilt (ervan uitgaande dat uw elementen ICloneablecorrect implementeren), kunt u zoiets als dit doen:

List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);
oldList.ForEach((item) =>
    {
        newList.Add((ICloneable)item.Clone());
    });

Uiteraard, vervang ICloneablein de bovenstaande generieke termen en cast met wat je elementtype ook is dat ICloneableimplementeert.

Als uw elementtype ICloneableniet ondersteunt maar wel een copy-constructor heeft, kunt u dit in plaats daarvan doen:

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

Persoonlijk zou ik vermijden ICloneablevanwege de noodzaak om een ​​diepe kopie van alle leden te garanderen. In plaats daarvan suggereer ik de kopie-constructor of een fabrieksmethode zoals YourType.CopyFrom(YourType itemToCopy)die een nieuw exemplaar van YourTyperetourneert.

Een van deze opties kan worden ingepakt door een methode (extensie of anderszins).


Antwoord 3, Autoriteit 23%

Voor een ondiepe kopie kunt u in plaats daarvan de GetRange-methode van de generieke lijstklasse gebruiken.

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

Geciteerd van: Generics Recepten


Antwoord 4, Autoriteit 21%

public static object DeepClone(object obj) 
{
    object objResult = null;
    using (var ms = new MemoryStream())
    {
        var bf = new BinaryFormatter();
        bf.Serialize(ms, obj);
        ms.Position = 0;
        objResult = bf.Deserialize(ms);
     }
     return objResult;
}

Dit is een manier om het te doen met C # en .net 2.0. Uw object vereist [Serializable()]. Het doel is om alle referenties te verliezen en nieuwe te bouwen.


Antwoord 5, Autoriteit 9%

Om een ​​lijst te klonen, bel dan. Tolist (). Dit creëert een ondiepe kopie.

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

Antwoord 6, Autoriteit 6%

Na een kleine wijziging kunt u ook klonen:

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

Antwoord 7, Autoriteit 4%

Tenzij u een daadwerkelijke kloon van elk object in uw List<T>, de beste manier om een ​​lijst wilt maken, is het maken van een nieuwe lijst met de oude lijst als de parameter van de verzameling.

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

Wijzigingen in myListzoals insert of verwijderen heeft geen invloed op cloneOfMyListen vice versa.

De werkelijke objecten die de twee lijsten bevatten, zijn echter nog steeds hetzelfde.


Antwoord 8, Autoriteit 3%

Als u alleen om waarde geeft …

en u weet het type:

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

Als u het type eerder niet kent, hebt u een helperfunctie nodig:

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

Het alleen:

List<string> myNewList = Clone(myOldList);

Antwoord 9, Autoriteit 3%

Gebruik automapper (of welke mapping lib u verkiest) om te kloon is eenvoudig en een stuk onderhoudbaar.

Definieer uw toewijzing:

Mapper.CreateMap<YourType, YourType>();

Doe de magie:

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);

Antwoord 10, Autoriteit 3%

Als u al NewtonSoft.json in uw project hebt verwezen en zijn uw objecten serialiseerbaar, kunt u altijd gebruiken:

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

Mogelijk niet de meest efficiënte manier om het te doen, maar tenzij je het 100s van 1000 momenten doet, merkt je misschien niet eens het versnellingsverschil.


Antwoord 11

Het is niet nodig om klassen te markeren als serializable en in onze tests met behulp van de Newtonsoft Jsonserizer nog sneller dan het gebruik van binaryformatter. Met verlengingsmethoden die bruikbaar zijn op elk object.

Standaard .NET JAVASCRIPTSIORIERE OPTIE:

public static T DeepCopy<T>(this T value)
{
    JavaScriptSerializer js = new JavaScriptSerializer();
    string json = js.Serialize(value);
    return js.Deserialize<T>(json);
}

Snellere optie met newtonsoft json :

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

Antwoord 12

Ik heb geluk als iemand dit ooit leest… maar om geen lijst met typeobjecten terug te geven in mijn Clone-methoden, heb ik een interface gemaakt:

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

Toen heb ik de extensie opgegeven:

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

En hier is een implementatie van de interface in mijn A/V-markeringssoftware. Ik wilde dat mijn Clone()-methode een lijst met VidMark retourneerde (terwijl de ICloneable-interface wilde dat mijn methode een lijst met objecten retourneerde):

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

En tot slot, het gebruik van de extensie binnen een klasse:

private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;
//Other methods instantiate and fill the lists
private void SetUndoVidMarks()
{
    _UndoVidMarks = _VidMarks.Clone();
}

Iemand leuk? Verbeteringen?


Antwoord 13

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

Antwoord 14

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

Antwoord 15

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

Antwoord 16

//try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();

Antwoord 17

Voor een diepe kopie is iclonable de juiste oplossing, maar hier is een vergelijkbare benadering van iclonableable met behulp van de constructor in plaats van de iclonable-interface.

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();

U hebt de volgende bibliotheek nodig waar u de kopie

maakt

using System.Linq

U kunt ook een voor lus gebruiken in plaats van SYSTEM.LINQ, maar LINQ maakt het beknopt en schoon. Evenzo zou je kunnen doen omdat andere antwoorden hebben voorgesteld en uitbreidingsmethoden, enz., Maar geen van dat nodig is.


Antwoord 18

U kunt extension-methode gebruiken:

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

U kunt alle objecten klonen met behulp van hun leden van het waardetype bijvoorbeeld, overweeg deze klasse:

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

Opmerking: als u een wijziging aanbrengt in het kopiëren (of klonen), heeft dit geen invloed op het oorspronkelijke object.


Antwoord 19

Als je een gekloonde lijst met dezelfde capaciteit nodig hebt, kun je dit proberen:

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

Antwoord 20

Het gebruik van een cast kan in dit geval handig zijn voor een ondiepe kopie:

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

toegepast op generieke lijst:

List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);

Antwoord 21

Ik heb voor mezelf een extensie gemaakt die ICollection converteert van items die IClonable niet implementeren

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

Antwoord 22

Ik gebruik automapper om een object te kopiëren. Ik heb zojuist een toewijzing ingesteld die één object aan zichzelf toewijst. U kunt deze bewerking op elke gewenste manier inpakken.

http://automapper.codeplex.com/


Antwoord 23

U kunt de lijst ook eenvoudig converteren naar een array met behulp van ToArrayen vervolgens de array klonen met Array.Clone(...).
Afhankelijk van uw behoeften, kunnen de methoden in de klasse Array aan uw behoeften voldoen.


Antwoord 24

Er is een eenvoudige manier om objecten in C# te klonen met behulp van een JSON-serializer en deserializer.

U kunt een extensieklasse maken:

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

Klonen en bezwaar maken:

obj clonedObj = originalObj.jsonCloneObject;

Antwoord 25

Als ik een diepe kopievan de collectie nodig heb, heb ik een favoriete aanpakals volgt:

public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collectionToDeepCopy)
{
    var serializedCollection = JsonConvert.SerializeObject(collectionToDeepCopy);
    return JsonConvert.DeserializeObject<IEnumerable<T>>(serializedCollection);
}

Antwoord 26

De volgende code zou met minimale wijzigingen naar een lijst moeten worden overgebracht.

In principe werkt het door bij elke volgende lus een nieuw willekeurig getal uit een groter bereik in te voegen. Als er al getallen bestaan die hetzelfde of hoger zijn, schuift u die willekeurige getallen één omhoog zodat ze worden overgedragen naar het nieuwe grotere bereik van willekeurige indexen.

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

Antwoord 27

Nog iets: je zou reflectie kunnen gebruiken. Als je dit goed in de cache opslaat, kloont het 1.000.000 objecten in 5,6 seconden (helaas 16,4 seconden met innerlijke objecten).

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

Ik heb het op een eenvoudige manier gemeten, met behulp van de Watcher-klasse.

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;

RESULTAAT:met innerlijk object PersonInstance – 16.4, PersonInstance = null – 5.6

CopyFactory is slechts mijn testklasse waar ik tientallen tests heb, inclusief het gebruik van expressie. Je zou dit in een andere vorm kunnen implementeren in een extensie of wat dan ook. Vergeet caching niet.

Ik heb het serialiseren nog niet getest, maar ik twijfel aan een verbetering met een miljoen klassen. Ik zal iets snels protobuf/newton proberen.

P.S.: voor het gemak heb ik hier alleen auto-property gebruikt. Ik zou kunnen updaten met FieldInfo, of je zou dit gemakkelijk zelf kunnen implementeren.

Ik heb onlangs de Protocol Buffersserializer getest met de DeepClone-functie uit de doos. Het wint met 4,2 seconden op een miljoen eenvoudige objecten, maar als het gaat om innerlijke objecten, wint het met het resultaat 7,4 seconden.

Serializer.DeepClone(personList);

SAMENVATTING:als je geen toegang hebt tot de lessen, dan zal dit helpen. Anders hangt het af van het aantal objecten. Ik denk dat je reflectie tot 10.000 objecten zou kunnen gebruiken (misschien een beetje minder), maar voor meer dan dit zal de Protocol Buffers-serializer beter presteren.


Antwoord 28

Voor een diepe kloon gebruik ik reflectie als volgt:

public List<T> CloneList<T>(IEnumerable<T> listToClone) {
    Type listType = listToClone.GetType();
    Type elementType = listType.GetGenericArguments()[0];
    List<T> listCopy = new List<T>();
    foreach (T item in listToClone) {
        object itemCopy = Activator.CreateInstance(elementType);
        foreach (PropertyInfo property in elementType.GetProperties()) {
            elementType.GetProperty(property.Name).SetValue(itemCopy, property.GetValue(item));
        }
        listCopy.Add((T)itemCopy);
    }
    return listCopy;
}

U kunt List of IEnumerable door elkaar gebruiken.


Antwoord 29

Ik had een probleem waarvan ik dacht dat het te maken had met mijn benadering van het combineren van lijsten, omdat het een keer werkte, maar nooit meer.

Het bleek totaal niets met elkaar te maken te hebben, en het kostte me heel erg veel tijd om het op te lossen, omdat ik op het verkeerde spoor zat.

Als je lijsten probeert te combineren en ze door een bindende bron te duwen, werkt al het bovenstaande, maar je moet ObservableCollection gebruiken in plaats van lijst.


Antwoord 30

probeer dit:

var res = ConvertModel<sourceModel, responseModle>(model);
 public static T2 ConvertModel<T1, T2>(T1 data) where T1 : class, new()
 {
     return DeserializeObject<T2>(SerializeObject(data));
 } 
 public static string SerializeObject<T>(T objectData)
 {
    string defaultJson = JsonConvert.SerializeObject(objectData);
    return defaultJson;
 }
public static T DeserializeObject<T>(string json)
{
   T obj = default(T);
   obj = JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
   return obj;
}

Other episodes