Wie klone ich eine generische Liste in C #?

592

Ich habe eine generische Liste von Objekten in C # und möchte die Liste klonen. Die Elemente in der Liste sind klonbar, aber es scheint keine Option zu geben list.Clone().

Gibt es einen einfachen Weg, dies zu umgehen?

Fiona
quelle
44
Sie sollten sagen, wenn Sie nach einer tiefen Kopie oder einer flachen Kopie
suchen
10
Was sind tiefe und flache Kopien?
Colonel Panic
3
@orip Ist nicht clone()per Definition eine tiefe Kopie? In C # kann man mit = leicht Zeiger weitergeben, dachte ich.
Chris
13
@ Chris eine flache Kopie kopiert eine Ebene tiefer als die Zeigerkopie. Beispielsweise enthält eine flache Kopie einer Liste dieselben Elemente, ist jedoch eine andere Liste.
Orip

Antworten:

385

Sie können eine Erweiterungsmethode verwenden.

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
quelle
71
Ich denke, List.ConvertAll könnte dies in kürzerer Zeit tun, da es das gesamte Array für die Liste vorab zuweisen kann, anstatt ständig die Größe ändern zu müssen.
MichaelGG
2
@MichaelGG, was ist, wenn Sie die Elemente in der Liste nicht konvertieren, sondern nur klonen / duplizieren möchten? Würde das funktionieren? || var clonedList = ListOfStrings.ConvertAll (p => p);
IbrarMumtaz
29
@IbrarMumtaz: Das ist dasselbe wie var clonedList = new List <string> (ListOfStrings);
Brandon Arnold
4
Schöne Lösung! Übrigens bevorzuge ich öffentliche statische List <T> CLone <T> ... In solchen Fällen ist es nützlicher, da keine weitere Umwandlung erforderlich ist: List <MyType> cloned = listToClone.Clone ();
Plutoz
2
Dies ist tiefes Klonen
George Birbilis
512

Wenn Ihre Elemente Werttypen sind, können Sie einfach Folgendes tun:

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

Wenn es sich jedoch um Referenztypen handelt und Sie eine tiefe Kopie wünschen (vorausgesetzt, Ihre Elemente werden ordnungsgemäß implementiert ICloneable), können Sie Folgendes tun:

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

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

Ersetzen Sie natürlich ICloneabledie oben genannten Generika und setzen Sie sie mit dem Elementtyp um, der implementiert wird ICloneable.

Wenn Ihr Elementtyp nicht unterstützt ICloneable, aber über einen Kopierkonstruktor verfügt, können Sie stattdessen Folgendes tun:

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

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

Persönlich würde ich es vermeiden, ICloneableweil eine tiefe Kopie aller Mitglieder garantiert werden muss. Stattdessen würde ich vorschlagen, dass der Kopierkonstruktor oder eine solche Factory-Methode YourType.CopyFrom(YourType itemToCopy)eine neue Instanz von zurückgibt YourType.

Jede dieser Optionen kann durch eine Methode (Erweiterung oder auf andere Weise) umbrochen werden.

Jeff Yates
quelle
1
Ich denke, List <T> .ConvertAll sieht vielleicht besser aus, als eine neue Liste zu erstellen und foreach + add durchzuführen.
MichaelGG
2
@ Dimitri: Nein, das stimmt nicht. Das Problem ist, dass ICloneablein der Definition nie angegeben wurde, ob der Klon tief oder flach war, sodass Sie nicht bestimmen können, welche Art von Klonoperation ausgeführt wird, wenn ein Objekt ihn implementiert. Dies bedeutet, dass Sie, wenn Sie einen tiefen Klon von erstellen möchten, dies List<T>tun müssen, ohne ICloneablesicher zu sein, dass es sich um eine tiefe Kopie handelt.
Jeff Yates
5
Warum nicht die AddRange-Methode verwenden? ( newList.AddRange(oldList.Select(i => i.Clone())oder newList.AddRange(oldList.Select(i => new YourType(i))
Phoog
5
@phoog: Ich denke, dass es beim Scannen des Codes etwas weniger lesbar / verständlich ist, das ist alles. Lesbarkeit gewinnt für mich.
Jeff Yates
1
@JeffYates: Eine unzureichend berücksichtigte Falte ist, dass Dinge im Allgemeinen nur kopiert werden müssen, wenn ein Ausführungspfad vorhanden ist, der sie mutieren würde. Es ist sehr üblich, dass unveränderliche Typen einen Verweis auf eine Instanz eines veränderlichen Typs enthalten, diese Instanz jedoch niemals etwas aussetzen, das sie mutiert. Das unnötige Kopieren von Dingen, die sich nie ändern werden, kann manchmal zu einem erheblichen Leistungsverlust führen und die Speichernutzung um Größenordnungen erhöhen.
Supercat
84

Für eine flache Kopie können Sie stattdessen die GetRange-Methode der generischen List-Klasse verwenden.

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

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

Zitiert aus: Generika-Rezepte

Anthony Potts
quelle
43
Sie können dies auch erreichen, indem Sie mit dem Konstruktor der Liste <T> eine Liste <T> angeben, aus der kopiert werden soll. zB var flatClonedList = neue Liste <MyObject> (originalList);
Arkiliknam
9
Ich benutze oft List<int> newList = oldList.ToList(). Gleicher Effekt. Die Lösung von Arkiliknam ist meiner Meinung nach jedoch am besten für die Lesbarkeit geeignet.
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;
}

Dies ist eine Möglichkeit, dies mit C # und .NET 2.0 zu tun. Ihr Objekt muss sein [Serializable()]. Ziel ist es, alle Referenzen zu verlieren und neue zu erstellen.

Patrick Desjardins
quelle
11
+1 - Ich mag diese Antwort - sie ist schnell, schmutzig, böse und sehr effektiv. Ich habe in Silverlight verwendet und den DataContractSerializer verwendet, da der BinarySerializer nicht verfügbar war. Wer muss Seiten mit Objektkloncode schreiben, wenn Sie dies einfach tun können? :)
Slugster
3
Ich mag das. Während es schön ist, Dinge "richtig" zu machen, ist schnell und schmutzig oft nützlich.
Odrade
3
Schnell! aber: Warum dreckig?
Raiserle
2
Dieser tiefe Klon ist schnell und einfach. Seien Sie vorsichtig mit anderen Vorschlägen auf dieser Seite. Ich habe mehrere ausprobiert und sie klonen nicht tief.
RandallTo
2
Der einzige negative Aspekt, wenn Sie es so nennen können, ist, dass Ihre Klassen als serialisierbar markiert sein müssen, damit dies funktioniert.
Tuukka Haapaniemi
29

Um eine Liste zu klonen, rufen Sie einfach .ToList () auf. Dadurch wird eine flache Kopie erstellt.

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
quelle
3
Mit Abstand
einfachste
29
Eine kleine Warnung, dies ist eine flache Kopie ... Dadurch werden zwei Listenobjekte erstellt, aber die darin enthaltenen Objekte sind dieselben. Das Ändern einer Eigenschaft ändert dasselbe Objekt / dieselbe Eigenschaft in der ursprünglichen Liste.
Mark G
22

Nach einer geringfügigen Änderung können Sie auch 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;
}
Ajith
quelle
Vergessen Sie nicht, dass das T serialisierbar sein sollte, sonst erhalten Sie die System.Runtime.Serialization.SerializationException.
Bence Végert
Gute Antwort. Ein Hinweis: Sie können if (!obj.GetType().IsSerializable) return default(T);als erste Anweisung hinzufügen , die die Ausnahme verhindert. Und wenn Sie es in eine Erweiterungsmethode ändern, können Sie sogar den Elvis-Operator wie var b = a?.DeepClone();( var a = new List<string>() { "a", "b" }; zum Beispiel angegeben) verwenden.
Matt
15

Wenn Sie List<T>keinen tatsächlichen Klon für jedes einzelne Objekt in Ihrem Objekt benötigen, können Sie eine Liste am besten klonen, indem Sie eine neue Liste mit der alten Liste als Auflistungsparameter erstellen.

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

Änderungen myListwie Einfügen oder Entfernen wirken sich nicht aus cloneOfMyListund umgekehrt.

Die tatsächlichen Objekte, die die beiden Listen enthalten, sind jedoch immer noch dieselben.

Jader Feijo
quelle
Ich stimme user49126 zu. Ich sehe, dass es sich um eine flache Kopie handelt und Änderungen an einer Liste in der anderen Liste wiedergegeben werden.
Seidleroni
1
@Seidleroni, du liegst falsch. Die an den Listenelementen vorgenommenen Änderungen wirken sich auf die andere Liste aus, Änderungen an der Liste selbst jedoch nicht.
Wellington Zanelli
Dies ist eine flache Kopie.
Elliot Chen
Wie ist das eine flache Kopie?
mko
2
@WellingtonZanelli Gerade bestätigt, dass durch das Entfernen eines Elements aus myList auch dieses aus cloneOfMyList entfernt wird.
Nick Gallimore
13

Die Verwendung von AutoMapper (oder einer von Ihnen bevorzugten Zuordnungsbibliothek) zum Klonen ist einfach und sehr wartbar.

Definieren Sie Ihr Mapping:

Mapper.CreateMap<YourType, YourType>();

Mach die Magie:

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

Wenn Sie sich nur für Werttypen interessieren ...

Und Sie kennen den Typ:

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

Wenn Sie den Typ vorher nicht kennen, benötigen Sie eine Hilfsfunktion:

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

Das Gerechte:

List<string> myNewList = Clone(myOldList);
James Curran
quelle
15
Dadurch werden die Elemente nicht geklont.
Jeff Yates
10

Wenn Sie in Ihrem Projekt bereits auf Newtonsoft.Json verwiesen haben und Ihre Objekte serialisierbar sind, können Sie immer Folgendes verwenden:

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

Möglicherweise nicht der effizienteste Weg, dies zu tun, aber wenn Sie es nicht 100 oder 1000 Mal tun, werden Sie möglicherweise nicht einmal den Geschwindigkeitsunterschied bemerken.

ProfNimrod
quelle
4
Es geht nicht um den Geschwindigkeitsunterschied, es geht um die Lesbarkeit. Wenn ich zu dieser Codezeile käme, würde ich mir den Kopf schlagen und mich fragen, warum sie eine Drittanbieter-Bibliothek eingeführt haben, um ein Objekt zu serialisieren und dann zu deserialisieren, von dem ich keine Ahnung hätte, warum es passiert. Dies würde auch für eine Modellliste mit Objekten mit kreisförmiger Struktur nicht funktionieren.
Jonathon Cwik
1
Dieser Code hat für mich beim Deep Cloning hervorragend funktioniert. Die App migriert das Dokument-Boilerplate von Dev zu QA zu Prod. Jedes Objekt ist ein Paket aus mehreren Dokumentvorlagenobjekten, und jedes Dokument besteht wiederum aus einer Liste von Absatzobjekten. Mit diesem Code kann ich die .NET "Quell" -Objekte serialisieren und sofort in neue "Ziel" -Objekte deserialisieren, die dann in einer anderen Umgebung in einer SQL-Datenbank gespeichert werden. Nach tonnenweise Recherchen fand ich viele Dinge, von denen viele zu umständlich waren, und beschloss, dies zu versuchen. Dieser kurze und flexible Ansatz war "genau richtig"!
Developer63
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
quelle
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
quelle
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
quelle
3

Mein Freund Gregor Martinovic und ich haben diese einfache Lösung mit einem JavaScript Serializer entwickelt. Es ist nicht erforderlich, Klassen als serialisierbar zu kennzeichnen und in unseren Tests mit dem Newtonsoft JsonSerializer noch schneller als mit BinaryFormatter. Mit Erweiterungsmethoden, die für jedes Objekt verwendet werden können.

Standardoption für .NET JavascriptSerializer:

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

    string json = js.Serialize(value);

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

Schnellere Option mit Newtonsoft JSON :

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

    return JsonConvert.DeserializeObject<T>(json);
}
FH
quelle
2
Private Mitglieder werden nicht mit der JSON-Methode geklont. stackoverflow.com/a/78612/885627
himanshupareek66
3
 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();
Steve
quelle
3

Ich habe Glück, wenn jemand dies jemals liest ... aber um keine Liste von Typobjekten in meinen Klonmethoden zurückzugeben, habe ich eine Schnittstelle erstellt:

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

Dann habe ich die Erweiterung angegeben:

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

Und hier ist eine Implementierung der Schnittstelle in meiner A / V-Markierungssoftware. Ich wollte, dass meine Clone () -Methode eine Liste von VidMark zurückgibt (während die ICloneable-Schnittstelle wollte, dass meine Methode eine Liste von Objekten zurückgibt):

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

Und schließlich die Verwendung der Erweiterung innerhalb einer Klasse:

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

//Other methods instantiate and fill the lists

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

Gefällt es jemandem? Irgendwelche Verbesserungen?

John Kurtz
quelle
2

Sie können die Liste auch einfach mit in ein Array konvertieren ToArrayund das Array dann mit klonen Array.Clone(...). Abhängig von Ihren Anforderungen können die in der Array-Klasse enthaltenen Methoden Ihren Anforderungen entsprechen.

JHaps
quelle
Das funktioniert nicht; Änderungen an den Werten im geklonten Array STILL ändern die Werte in der ursprünglichen Liste.
Bernoulli Lizard
Sie können var clonedList = ListOfStrings.ConvertAll (p => p) verwenden. wie von @IbrarMumtaz gegeben .... Funktioniert effektiv ... Änderungen an einer Liste werden für sich behalten und nicht in einer anderen reflektiert
zainul
2

Sie können die Erweiterungsmethode verwenden:

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

}

Sie können alle Objekte klonen, indem Sie beispielsweise ihre Werttypelemente verwenden. Betrachten Sie diese 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 
    }
};

Hinweis: Wenn Sie beim Kopieren (oder Klonen) Änderungen vornehmen, wirkt sich dies nicht auf das ursprüngliche Objekt aus.

Furkan Katı
quelle
2

Wenn Sie eine geklonte Liste mit derselben Kapazität benötigen, können Sie Folgendes versuchen:

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

Ich habe selbst eine Erweiterung erstellt, die die ICollection von Elementen konvertiert, die IClonable nicht implementieren

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
quelle
Anscheinend überspringen einige Sammlungen (z. B. DataGrids SelectedItems bei Silverlight) die Implementierung von CopyTo, was bei diesem Ansatz ein Problem darstellt
George Birbilis,
1

Ich benutze Automapper, um ein Objekt zu kopieren. Ich habe gerade eine Zuordnung eingerichtet, die ein Objekt auf sich selbst abbildet. Sie können diesen Vorgang beliebig umbrechen.

http://automapper.codeplex.com/

Dan H.
quelle
1

Die Verwendung einer Besetzung kann in diesem Fall für eine flache Kopie hilfreich sein:

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

angewendet auf generische Liste:

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

Für eine tiefe Kopie ist ICloneable die richtige Lösung, aber hier ist ein ähnlicher Ansatz wie bei ICloneable, bei dem der Konstruktor anstelle der ICloneable-Schnittstelle verwendet wird.

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

Sie benötigen die folgende Bibliothek, in der Sie die Kopie erstellen

using System.Linq

Sie können auch eine for-Schleife anstelle von System.Linq verwenden, aber Linq macht sie präzise und sauber. Ebenso könnten Sie tun, was andere Antworten vorgeschlagen haben, und Erweiterungsmethoden usw. erstellen, aber nichts davon ist notwendig.

ztorstri
quelle
Das nennt man einen "Kopierkonstruktor". Es ist ein fehleranfälliger Ansatz. Wenn Sie Student ein neues Feld hinzufügen, müssen Sie daran denken, es dem Kopierkonstruktor hinzuzufügen. Die Hauptidee hinter "Klon" ist es, dieses Problem zu vermeiden.
Kenno
2
Selbst mit ICloneable müssen Sie eine "Clone" -Methode für Ihre Klasse haben. Sofern Sie keine Reflektion verwenden (die Sie auch im obigen Ansatz verwenden könnten), wird diese Clone-Methode dem obigen Copy-Konstruktor-Ansatz sehr ähnlich sein und unter dem gleichen Problem leiden, dass für neue / geänderte Felder aktualisiert werden muss. Aber das heißt "Die Klasse muss aktualisiert werden, wenn sich Felder der Klasse ändern". Natürlich tut es das;)
ztorstri
0

Der folgende Code sollte mit minimalen Änderungen in eine Liste übertragen werden.

Grundsätzlich funktioniert es, indem mit jeder aufeinanderfolgenden Schleife eine neue Zufallszahl aus einem größeren Bereich eingefügt wird. Wenn es bereits Zahlen gibt, die gleich oder höher sind, verschieben Sie diese Zufallszahlen um eins nach oben, damit sie in den neuen größeren Bereich von Zufallsindizes übertragen werden.

// 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
quelle
0

Eine andere Sache: Sie könnten Reflexion gebrauchen. Wenn Sie dies richtig zwischenspeichern, werden 1.000.000 Objekte in 5,6 Sekunden geklont (leider 16,4 Sekunden bei inneren Objekten).

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

Ich habe es auf einfache Weise mit der Watcher-Klasse gemessen.

 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;

ERGEBNIS: Mit dem inneren Objekt PersonInstance - 16.4 ist PersonInstance = null - 5.6

CopyFactory ist nur meine Testklasse, in der ich Dutzende von Tests einschließlich der Verwendung von Ausdruck habe. Sie können dies in einer anderen Form in einer Erweiterung oder was auch immer implementieren. Vergessen Sie nicht das Caching.

Ich habe die Serialisierung noch nicht getestet, bezweifle aber eine Verbesserung mit einer Million Klassen. Ich werde etwas schnelles Protobuf / Newton versuchen.

PS: Der Einfachheit halber habe ich hier nur Auto-Property verwendet. Ich könnte mit FieldInfo aktualisieren, oder Sie sollten dies einfach selbst implementieren.

Ich habe kürzlich den Serializer für Protokollpuffer mit der sofort einsatzbereiten DeepClone-Funktion getestet . Es gewinnt mit 4,2 Sekunden auf einer Million einfacher Objekte, aber wenn es um innere Objekte geht, gewinnt es mit dem Ergebnis 7,4 Sekunden.

Serializer.DeepClone(personList);

ZUSAMMENFASSUNG: Wenn Sie keinen Zugriff auf die Klassen haben, hilft dies. Ansonsten kommt es auf die Anzahl der Objekte an. Ich denke, Sie könnten Reflexion von bis zu 10.000 Objekten verwenden (vielleicht etwas weniger), aber für mehr als dies wird der Serializer für Protokollpuffer eine bessere Leistung erzielen.

Roma Borodov
quelle
0

Es gibt eine einfache Möglichkeit, Objekte in C # mit einem JSON-Serializer und -Deserializer zu klonen.

Sie können eine Erweiterungsklasse erstellen:

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 und Einwenden:

obj clonedObj = originalObj.jsonCloneObject;
Albert Arnau
quelle