it-swarm-tr.com

Derin klonlama nesneleri

Gibi bir şey yapmak istiyorum:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

Ardından, orijinal nesneye yansıtılmayan yeni nesnede değişiklikler yapın.

Sık sık bu işleve ihtiyacım yok, bu yüzden gerektiğinde, yeni bir nesne yaratmaya ve ardından her bir mülkü ayrı ayrı kopyalamaya çalıştım, ancak her zaman daha iyi ya da daha zarif bir kullanım yöntemi olduğu hissine kapılıyor. durum.

Bir nesneyi, klonlanan nesnenin, orijinal nesneye herhangi bir değişiklik yapılmadan değiştirilebilmesi için değiştirilebilmesi için nasıl kopyalayabilir veya derin bir şekilde kopyalayabilirim?

2028
NakedBrunch

Standart uygulama ICloneable arabirimini (tanımlanmış burada , bu nedenle yetersiz kalmayacağım) uygulamak olsa da, burada Kod Projesi bir süre önce bulduğum bir güzel derin klon nesne kopyalayıcı ve bunu eşyalarımıza dahil etti.

Başka bir yerde de belirtildiği gibi, nesnelerinizin seri hale getirilebilir olmasını gerektirir.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
    /// <summary>
    /// Perform a deep Copy of the object.
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
}

Buradaki fikir, nesnenizi seri hale getirmesi ve ardından yeni bir nesneye seri hale getirmesidir. Bunun yararı, bir nesne çok karmaşıklaştığında her şeyi klonlamak konusunda endişelenmenize gerek olmamasıdır.

Ve uzatma yöntemlerinin kullanımıyla (başlangıçta referans alınan kaynaktan da):

C/3.0'ın yeni uzatma yöntemlerini kullanmayı tercih ediyorsanız, aşağıdaki imzayı alacak şekilde yöntemi değiştirin:

public static T Clone<T>(this T source)
{
   //...
}

Şimdi yöntem çağrısı basitçe objectBeingCloned.Clone(); olur.

EDIT(10 Ocak 2015) Bunu tekrar ziyaret edeceğimi düşündüm, geçenlerde (Newtonsoft) Json kullanmaya başladım, bunu yapmak için şunu kullandım: olmalıdır çakmak, ve [ Seri hale getirilebilir] etiketleri. (NB@atconway, özel üyelerin JSON yöntemi kullanılarak klonlanmadığını belirtti.

/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{            
    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    // initialize inner objects individually
    // for example in default constructor some list property initialized with some values,
    // but in 'source' these items are cleaned -
    // without ObjectCreationHandling.Replace default constructor values will be added to result
    var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};

    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
1592
johnc

Çoğunlukla ilkel ve listelerden oluşan basit nesneler için bir klonlayıcı istedim. Nesneniz JSON serileştirilebilir kutunun dışındaysa, bu yöntem hile yapar. Bu, klonlanmış sınıfta hiçbir arayüz veya uygulama gerektirmez, sadece JSON.NET gibi bir JSON seri hale getirici gerektirir.

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

Ayrıca, bu eklenti yöntemini kullanabilirsiniz

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}
241
craastad

ICloneable kullanılmamasının nedeni değil çünkü genel bir arayüze sahip değil. Kullanmamak için nedeni belirsiz olmasıdır . Sığ mı yoksa derin bir kopya mı edindiğiniz belli değil; uygulayıcıya kalmış.

Evet, MemberwiseClonesığ bir kopya oluşturur, ancak MemberwiseClonename__'in tersi Clonedeğildir; Belki de varolmayan DeepCloneolabilir. ICloneable arayüzü üzerinden bir nesneyi kullandığınızda, alttaki nesnenin ne tür bir klonlama gerçekleştirdiğini bilemezsiniz. (Ve XML yorumları netleştirmez, çünkü nesnenin Clone yöntemindeki yorumlar yerine arabirim yorumlarını alırsınız.)

Genelde yaptığım şey, istediğim şeyi yapan bir Copymetodu yapmak.

162
Ryan Lundy

Burada bağlantılı seçeneklerin birçoğu ve bu sorunla ilgili olası çözümler hakkında çok fazla okumadan sonra, tüm seçeneklerin Ian P 'in linkinde oldukça iyi bir şekilde özetlendiğine inanıyorum (diğer tüm seçenekler bu seçeneklerin varyasyonlarıdır). ) ve en iyi çözüm soru yorumları üzerine Pedro77 'in bağlantısı tarafından sağlanmıştır.

Bu yüzden bu 2 referansın ilgili kısımlarını buraya kopyalayacağım. Bu şekilde sahip olabiliriz:

Keskin c nesneleri klonlamak için yapılacak en iyi şey!

Her şeyden önce, hepsi bizim seçeneklerimiz:

İfade Ağaçları ile Hızlı Derin Kopyalama , ayrıca Seri hale getirme, Yansıma ve İfade Ağaçları ile klonlamanın performans karşılaştırmasını da yapar.

Ben seçim Neden ICloneable (yani elle)

Sayın Venkat Subramaniam (burada gereksiz bağlantı) neden 'in çok detaylı olduğunu açıklıyor.

Tüm makalesi, 3 nesne kullanarak çoğu durumda uygulanabilir olmaya çalışan bir örnek etrafında dönüyor: Kişi, Beyin ve Şehir. Biz kendi beynini ama aynı şehri olacak bir kişi, klonlamak istiyorum. Diğer yöntemlerden herhangi yukarıda getirmek veya yazı okuyabilir tüm sorunları çizebilirim ya.

Bu benim sonucumun biraz değiştirilmiş hali:

Bir nesneyi New ve ardından sınıf adını belirterek kopyalamak, genellikle genişletilemeyen bir koda yol açar. klon, prototip kalıbı uygulaması kullanarak bunu başarmak için daha iyi bir yoldur. Ancak, klon kullanarak o da oldukça sorunlu olabilir C # (ve Java) olarak sağlandığı. Bir korumalı (herkese açık olmayan) kopya kurucu sağlamak ve klon yönteminden o çağırmak için daha iyidir. Bu bize, bir nesneyi bir sınıfın kendi örneğine yaratma görevini devretme, böylece genişletilebilirlik sağlama ve ayrıca korumalı kopya kurucuyu kullanarak nesneleri güvenli bir şekilde oluşturma yeteneği verir.

Umarım bu uygulama işleri netleştirebilir:

public class Person : ICloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    protected Person(Person another)
    {
        Brain refBrain = null;
        try
        {
            refBrain = (Brain) another.brain.clone();
            // You can set the brain in the constructor
        }
        catch(CloneNotSupportedException e) {}
        brain = refBrain;
        age = another.age;
    }
    public String toString()
    {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    public Object clone()
    {
        return new Person(this);
    }
    …
}

Şimdi, bir sınıfın Kişi'den geldiğini düşünün.

public class SkilledPerson extends Person
{
    private String theSkills;
    public SkilledPerson(Brain aBrain, int theAge, String skills)
    {
        super(aBrain, theAge);
        theSkills = skills;
    }
    protected SkilledPerson(SkilledPerson another)
    {
        super(another);
        theSkills = another.theSkills;
    }

    public Object clone()
    {
        return new SkilledPerson(this);
    }
    public String toString()
    {
        return "SkilledPerson: " + super.toString();
    }
}

Aşağıdaki kodu çalıştırmayı deneyebilirsiniz:

public class User
{
    public static void play(Person p)
    {
        Person another = (Person) p.clone();
        System.out.println(p);
        System.out.println(another);
    }
    public static void main(String[] args)
    {
        Person sam = new Person(new Brain(), 1);
        play(sam);
        SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer");
        play(bob);
    }
}

Üretilen çıktı:

This is person with [email protected]
This is person with [email protected]
SkilledPerson: This is person with [email protected]
SkilledPerson: This is person with [email protected]

Eğer gözlemlenen sayıda obje tutarsak, burada uygulanan klonun obje sayısını doğru tutacağını gözleyin.

102
cregox

Bir kopya kurucuyu bir klona tercih ederim. Amaç daha açık.

77
Nick

Tüm genel özellikleri kopyalamak için basit bir uzatma yöntemi. Herhangi bir nesne için çalışır ve, sınıfının [Serializable] olmasını gerektirmez. Diğer erişim seviyesi için genişletilebilir.

public static void CopyTo( this object S, object T )
{
    foreach( var pS in S.GetType().GetProperties() )
    {
        foreach( var pT in T.GetType().GetProperties() )
        {
            if( pT.Name != pS.Name ) continue;
            ( pT.GetSetMethod() ).Invoke( T, new object[] 
            { pS.GetGetMethod().Invoke( S, null ) } );
        }
    };
}
38

Silverlight'ta ICloneable'ı kullanmada sorun yaşıyordum, ancak Seralizasyon fikrini sevdim, XML'i seralize edebilirim, bu yüzden şunu yaptım:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //[email protected]
    public static T DeserializeXML<T>(string xmlData) where T:new()
    {
        if (string.IsNullOrEmpty(xmlData))
            return default(T);

        TextReader tr = new StringReader(xmlData);
        T DocItms = new T();
        XmlSerializer xms = new XmlSerializer(DocItms.GetType());
        DocItms = (T)xms.Deserialize(tr);

        return DocItms == null ? default(T) : DocItms;
    }

    public static string SeralizeObjectToXML<T>(T xmlObject)
    {
        StringBuilder sbTR = new StringBuilder();
        XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
        XmlWriterSettings xwsTR = new XmlWriterSettings();

        XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
        xmsTR.Serialize(xmwTR,xmlObject);

        return sbTR.ToString();
    }

    public static T CloneObject<T>(T objClone) where T:new()
    {
        string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);
        return SerializeHelper.DeserializeXML<T>(GetString);
    }
}
30
Michael White

Az önce CloneExtensions library projesi oluşturdum. İfade Ağacı çalışma zamanı kod derlemesi tarafından oluşturulan basit atama işlemlerini kullanarak hızlı ve derin klonlama gerçekleştirir.

Bu nasıl kullanılır?

Alanlar ve özellikler arasındaki atamalar tonuyla kendi Clone veya Copy yöntemlerinizi yazmak yerine, İfade Ağacı kullanarak programı kendiniz için yapın. Eklenti yöntemi olarak işaretlenmiş GetClone<T>() yöntemi, sadece örneğinizde onu çağırmanıza izin verir:

var newInstance = source.GetClone();

source enum kullanarak newInstance'dan CloningFlags'ye nelerin kopyalanacağını seçebilirsiniz:

var newInstance 
    = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

Ne klonlanabilir?

  • İlkel (int, uint, byte, double, char vb.), Bilinen değişmez türleri (DateTime, TimeSpan, String) ve delegeler (Action, Func, vb.)
  • Null
  • T [] dizileri
  • Genel sınıflar ve yapılar dahil, özel sınıflar ve yapılar.

Aşağıdaki sınıf/yapı üyeleri dahili olarak klonlanır:

  • Genel değerler, salt okunur alanlar
  • Hem olsun hem de ayarlanan erişimcilerle birlikte halka açık mülklerin değerleri
  • ICollection uygulayan türler için koleksiyon öğeleri

Ne kadar hızlı?

Çözüm, yansıma işleminden daha hızlıdır, çünkü üyelerin bilgileri yalnızca bir kez, bir kez GetClone<T> verilen T türü için ilk kez kullanılmadan önce toplanmalıdır.

Aynı türden T örneğini çiftledikten sonra klonladığınızda serileştirme tabanlı çözümden daha hızlıdır.

ve dahası...

dokümantasyon üzerinde üretilen ifadeler hakkında daha fazla bilgi edinin.

List<int> için örnek ifade hata ayıklama listesi:

.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
    System.Collections.Generic.List`1[System.Int32] $source,
    CloneExtensions.CloningFlags $flags,
    System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
    .Block(System.Collections.Generic.List`1[System.Int32] $target) {
        .If ($source == null) {
            .Return #Label1 { null }
        } .Else {
            .Default(System.Void)
        };
        .If (
            .Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
        ) {
            $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
            ).Invoke((System.Object)$source)
        } .Else {
            $target = .New System.Collections.Generic.List`1[System.Int32]()
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
        ) {
            .Default(System.Void)
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
        ) {
            .Block() {
                $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
                    $source.Capacity,
                    $flags,
                    $initializers)
            }
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
        ) {
            .Block(
                System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
                System.Collections.Generic.ICollection`1[System.Int32] $var2) {
                $var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
                $var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
                .Loop  {
                    .If (.Call $var1.MoveNext() != False) {
                        .Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
                                $var1.Current,
                                $flags,


                         $initializers))
                } .Else {
                    .Break #Label2 { }
                }
            }
            .LabelTarget #Label2:
        }
    } .Else {
        .Default(System.Void)
    };
    .Label
        $target
    .LabelTarget #Label1:
}

}

c # kodunu takip etmekle aynı anlamı taşıyanlar:

(source, flags, initializers) =>
{
    if(source == null)
        return null;

    if(initializers.ContainsKey(typeof(List<int>))
        target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
    else
        target = new List<int>();

    if((flags & CloningFlags.Properties) == CloningFlags.Properties)
    {
        target.Capacity = target.Capacity.GetClone(flags, initializers);
    }

    if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
    {
        var targetCollection = (ICollection<int>)target;
        foreach(var item in (ICollection<int>)source)
        {
            targetCollection.Add(item.Clone(flags, initializers));
        }
    }

    return target;
}

List<int> için kendi Clone yönteminizi nasıl yazdığınıza benzemiyor mu?

27
MarcinJuraszek

Zaten ValueInjecter veya Automapper gibi bir üçüncü taraf uygulaması kullanıyorsanız, şunun gibi bir şey yapabilirsiniz:

MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

Bu yöntemi kullanarak, nesnelerinize ISerializable veya ICloneable uygulamak zorunda değilsiniz. Bu, MVC/MVVM paterni ile ortaktır, bu yüzden bunun gibi basit araçlar yaratılmıştır.

bakınız CodePlex'te valueinjecter derin klonlama çözümü .

26
Michael Cox

Kısa cevap, ICloneable arabirimden miras almanız ve ardından .clone işlevini uygulamanızdır. Klon, üye olarak bir kopya yapmalı ve onu gerektiren herhangi bir üye üzerinde derin bir kopya yapmalı, ardından ortaya çıkan nesneyi döndürmelidir. Bu özyinelemeli bir işlemdir (klonlamak istediğiniz sınıfın tüm üyelerinin değer türleri veya ICloneable uygulaması ve üyelerinin değer türleri veya ICloneable uygulaması vb. Olmasını gerektirir).

ICloneable kullanarak klonlama hakkında daha ayrıntılı bir açıklama için, bu makale göz atın.

long answer "bağlıdır". Diğerleri tarafından belirtildiği gibi, ICloneable jenerikler tarafından desteklenmez, dairesel sınıf referansları için özel hususlar gerektirir ve gerçekte bazıları tarafından .NET Framework'de "hata" olarak görülür. Serileştirme yöntemi nesnelerinizin serileştirilebilir olmasına bağlıdır, bunlar olmayabilir ve üzerinde kontrol sahibi olamayabilirsiniz. Üzerinde "en iyi" uygulama olan toplulukta hala çok fazla tartışma var. Gerçekte, çözümlerin hiçbiri ICloneable gibi başlangıçta olduğu gibi tüm durumlar için en iyi uygulama tek beden uyan değildir.

Birkaç seçenek için bu Geliştiricinin Köşesi makalesi bölümüne bakın (Ian'a kredi verin).

20
Zach Burlingame

En iyisi bir uzatma yöntemi gibi uygulamaktır

public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

ve çözümün herhangi bir yerinde kullanın.

var copy = anyObject.DeepClone();

Aşağıdaki üç uygulamayı yapabiliriz:

  1. Sıralamaya Göre (en kısa kod)
  2. Yansımayla - 5 kat daha hızlı
  3. İfade Ağaçlarına Göre - 20x daha hızlı

Tüm bağlantılı yöntemler iyi çalışıyor ve derinlemesine test edildi.

19
frakon
  1. Temel olarak ICloneable arayüzünü uygulamanız ve daha sonra nesne yapısı kopyalama işlemini gerçekleştirmeniz gerekir.
  2. Tüm üyelerin derin kopyası ise, tüm çocukların da klonlanabilir olduğundan emin olmanız gerekir (seçtiğiniz çözüme ilişkin değildir).
  3. Bazen, bu işlem sırasında bazı kısıtlamaların farkında olmanız gerekir; örneğin, ORM nesnelerini kopyaladığınızda, çerçevelerin çoğu oturuma yalnızca bir nesnenin eklenmesine izin veriyorsa ve bu nesnenin klonlarını YAPMAYIN, ya da mümkünse özen göstermeniz gerekir. Bu nesnelerin oturumu ekleme hakkında.

Şerefe.

16
dimarzionist

Bilinmeyen türlere doğru klonlama yapmak istiyorsanız, fastclone 'ye bakabilirsiniz.

Bu, ifadeye dayalı klonlamanın ikili serileştirme işleminden yaklaşık 10 kat daha hızlı çalışması ve tam nesne grafiği bütünlüğünü sağlamasıdır.

Bunun anlamı şudur: hiyerarşinizdeki aynı nesneye birden çok kez başvurursanız, klon ayrıca referans verilen tek bir örnek arıcılığına sahip olacaktır.

Klonlanan nesnelerde arayüzlere, niteliklere veya başka herhangi bir değişikliğe gerek yoktur.

15
Michael Sander

İşleri basitleştirmek ve kullanımı AutoMapper diğerleri de belirtildiği gibi, bu, tek ihtiyacınız kod üç satır aynı tip başka bir nesne kopyalamak için başka ... bir nesneyi eşlemek için basit bir küçük kütüphane var:

MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);

Hedef nesne şimdi kaynak nesnenin bir kopyasıdır. Yeterince basit değil mi? Çözümünüzdeki her yerde kullanmak için bir uzantı yöntemi oluşturun:

public static T Copy<T>(this T source)
{
    T copy = default(T);
    Mapper.CreateMap<T, T>();
    copy = Mapper.Map<T, T>(source);
    return copy;
}

Eklenti yöntemini kullanarak, üç satır bir satır olur:

MyType copy = source.Copy();
11
Stacked

Bunu, . NET eksikliğinin üstesinden gelmek için Liste <T> 'yi elle kopyalamak zorunda kaldım.

Bunu kullanıyorum:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

Ve başka bir yerde:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

Bunu yapan oneliner ile gelmeye çalıştım, ancak isimsiz yöntem bloklarında çalışmadığı için verim mümkün değil.

Daha da iyisi, genel List <T> klonlayıcı kullanın:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}
10

S. Neden bu cevabı seçeyim?

  • .NET'in sahip olabileceği en yüksek hızda istiyorsanız, bu cevabı seçin.
  • Gerçekten ama gerçekten kolay bir klonlama yöntemi istiyorsanız bu cevabı yoksayın.

Başka bir deyişle, düzeltme gerektiren bir performans darboğazınız yoksa ve bir profiler ile kanıtlayabilirsiniz) başka bir cevapla gidin .

Diğer yöntemlerden 10 kat daha hızlı

Derin bir klon gerçekleştirmenin aşağıdaki yöntemi:

  • Serileştirme/seri hale getirme içeren her şeyden 10 kat daha hızlı;
  • Oldukça lanet olası teorik maksimum hıza yakın.

Ve yöntem ...

En yüksek hız için, derin bir kopya yapmak için Nested MemberwiseClone öğesini kullanabilirsiniz . Neredeyse bir değer yapısını kopyalamakla aynı hızdadır ve (a) yansıma veya (b) serileştirme işleminden (bu sayfadaki diğer cevaplarda açıklandığı gibi) çok daha hızlıdır.

if kullanıyorsanız Derin bir kopya için iç içe MemberwiseClone , sınıftaki her yuvalanmış seviye için bir ShallowCopy el ile ve oluşturmak için tüm bahsedilen ShallowCopy yöntemlerini çağıran bir DeepCopy uygulamanız gerekir. tam bir klon. Bu basittir: toplamda sadece birkaç satır vardır, aşağıdaki demo koduna bakın.

İşte 100.000 klonun nispi performans farkını gösteren kodun çıktısı:

  • Yuvalanmış yapılarda Nested MemberwiseClone için 1.08 saniye
  • Yuvalanmış sınıflardaki Yuvalanmış MemberwiseClone için 4.77 saniye
  • Serileştirme/Serileştirme için 39.93 saniye

Nested MemberwiseClone bir sınıfta bir yapıyı kopyalamak kadar hızlı ve bir yapıyı kopyalamak, .NET'in yapabileceği teorik maksimum hıza oldukça yakın.

Demo 1 of shallow and deep copy, using classes and MemberwiseClone:
  Create Bob
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Clone Bob >> BobsSon
  Adjust BobsSon details
    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Elapsed time: 00:00:04.7795670,30000000

Demo 2 of shallow and deep copy, using structs and value copying:
  Create Bob
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Clone Bob >> BobsSon
  Adjust BobsSon details:
    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Elapsed time: 00:00:01.0875454,30000000

Demo 3 of deep copy, using class and serialize/deserialize:
  Elapsed time: 00:00:39.9339425,30000000

MemberwiseCopy kullanarak derin bir kopyanın nasıl yapılacağını anlamak için, yukarıdaki zamanları oluşturmak için kullanılan demo proje:

// Nested MemberwiseClone example. 
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
    public Person(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    [Serializable] // Not required if using MemberwiseClone
    public class PurchaseType
    {
        public string Description;
        public PurchaseType ShallowCopy()
        {
            return (PurchaseType)this.MemberwiseClone();
        }
    }
    public PurchaseType Purchase = new PurchaseType();
    public int Age;
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person DeepCopy()
    {
            // Clone the root ...
        Person other = (Person) this.MemberwiseClone();
            // ... then clone the nested class.
        other.Purchase = this.Purchase.ShallowCopy();
        return other;
    }
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
    public PersonStruct(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    public struct PurchaseType
    {
        public string Description;
    }
    public PurchaseType Purchase;
    public int Age;
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct ShallowCopy()
    {
        return (PersonStruct)this;
    }
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct DeepCopy()
    {
        return (PersonStruct)this;
    }
}
// Added only for a speed comparison.
public class MyDeepCopy
{
    public static T DeepCopy<T>(T obj)
    {
        object result = null;
        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            result = (T)formatter.Deserialize(ms);
            ms.Close();
        }
        return (T)result;
    }
}

Ardından demosu ana telefondan arayın:

void MyMain(string[] args)
{
    {
        Console.Write("Demo 1 of shallow and deep copy, using classes and MemberwiseCopy:\n");
        var Bob = new Person(30, "Lamborghini");
        Console.Write("  Create Bob\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Console.Write("  Clone Bob >> BobsSon\n");
        var BobsSon = Bob.DeepCopy();
        Console.Write("  Adjust BobsSon details\n");
        BobsSon.Age = 2;
        BobsSon.Purchase.Description = "Toy car";
        Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
        Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Debug.Assert(Bob.Age == 30);
        Debug.Assert(Bob.Purchase.Description == "Lamborghini");
        var sw = new Stopwatch();
        sw.Start();
        int total = 0;
        for (int i = 0; i < 100000; i++)
        {
            var n = Bob.DeepCopy();
            total += n.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
    }
    {               
        Console.Write("Demo 2 of shallow and deep copy, using structs:\n");
        var Bob = new PersonStruct(30, "Lamborghini");
        Console.Write("  Create Bob\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Console.Write("  Clone Bob >> BobsSon\n");
        var BobsSon = Bob.DeepCopy();
        Console.Write("  Adjust BobsSon details:\n");
        BobsSon.Age = 2;
        BobsSon.Purchase.Description = "Toy car";
        Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
        Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);                
        Debug.Assert(Bob.Age == 30);
        Debug.Assert(Bob.Purchase.Description == "Lamborghini");
        var sw = new Stopwatch();
        sw.Start();
        int total = 0;
        for (int i = 0; i < 100000; i++)
        {
            var n = Bob.DeepCopy();
            total += n.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
    }
    {
        Console.Write("Demo 3 of deep copy, using class and serialize/deserialize:\n");
        int total = 0;
        var sw = new Stopwatch();
        sw.Start();
        var Bob = new Person(30, "Lamborghini");
        for (int i = 0; i < 100000; i++)
        {
            var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
            total += BobsSon.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
    }
    Console.ReadKey();
}

Yine, if kullanıyorsanız Derin bir kopya için iç içe MemberwiseClone , sınıftaki her yuvalanmış seviye için bir ShallowCopy el ile tüm ShallowCopy yöntemlerini çağıran bir DeepCopy uygulamanız gerekir. tam bir klon oluşturmak için. Bu basittir: toplamda sadece birkaç satır vardır, yukarıdaki demo koduna bakın.

Değer türleri ve Referans Türleri

Bir nesneyi klonlamak söz konusu olduğunda, " struct " ve " class " arasında büyük bir fark olduğunu unutmayın:

  • Eğer bir " struct " 'a sahipseniz, a değer türü böylece sadece kopyalayabilirsiniz ve içerikler klonlanır (ancak kullanmazsanız sadece sığ bir klon yapar bu yazıdaki teknikler).
  • Eğer bir " class " varsa, bu bir reference type 'dır, eğer kopyalarsanız, tek yaptığınız imleci kopyalamaktır. Gerçek bir klon oluşturmak için daha yaratıcı olmanız ve bellekteki orijinal nesnenin başka bir kopyasını oluşturan değer türleri ve referans türleri arasındaki farklar kullanmanız gerekir.

Değer türleri ve referans türleri arasındaki farklar bölümüne bakın.

Hata ayıklamada yardımcı olmak için sağlama toplamı

  • Nesneleri yanlış şekilde klonlamak, sıkışması zor hatalara yol açabilir. Üretim kodunda, nesnenin doğru şekilde klonlandığını ve başka bir referans tarafından bozulmadığını kontrol etmek için bir sağlama toplamı uygulama eğilimindeyim. Bu sağlama toplamı Serbest Bırakma modunda kapatılabilir.
  • Bu yöntemi oldukça faydalı buluyorum: genellikle, her şeyi değil, yalnızca nesnenin bölümlerini klonlamak istersiniz.

Diğer birçok başlıktan birçok parçayı ayırmak için gerçekten yararlı

Bu kod için mükemmel bir kullanım örneği, üretici/tüketici modelini uygulamak için iç içe bir sınıf veya yapı klonlarını bir kuyruğa beslemektir.

  • Sahip oldukları bir sınıfı değiştiren ve ardından bu sınıfın tam bir kopyasını ConcurrentQueue içine iten bir (veya daha fazla) iş parçacığımız olabilir.
  • Daha sonra, bu sınıfların kopyalarını çekip bunlarla ilgilenen bir (veya daha fazla) iş parçacığımız var.

Bu pratikte son derece iyi çalışır ve bir veya daha fazla sayıda iplikten (tüketiciler) birçok ipliği (üreticiler) ayırmamıza izin verir.

Ve bu yöntem de gözle görülür derecede hızlı: iç içe geçmiş yapılar kullanırsak, iç içe geçmiş sınıfları serileştirme/seri hale getirme işleminden 35 kat daha hızlıdır ve makinede bulunan tüm iş parçacıklarından yararlanmamızı sağlar.

Güncelleştirme

Anlaşılan ExpressMapper, daha hızlı değilse de yukarıdaki gibi elle kodlamaktan daha hızlı. Bir profilciyle nasıl karşılaştırıldıklarını görmem gerekebilir.

7
Contango

Bunun yansıma yoluyla da uygulandığını gördüm. Temel olarak, bir nesnenin üyeleri arasında yinelenen ve bunları yeni nesneye uygun şekilde kopyalayacak bir yöntem vardı. Referans türlerine veya koleksiyonlarına ulaştığında, kendisine özyinelemeli bir çağrı yaptığını düşünüyorum. Yansıma pahalıdır, ancak oldukça iyi çalıştı.

7
xr280xr

Farklı projelerdeki tüm gereksinimlerimi karşılayan bir klonlayıcı bulamadığım için, kodumu klon gereksinimlerini karşılayacak şekilde uyarlamak yerine, farklı kod yapılarına uyarlanabilen ve uyarlanabilen derin bir klon oluşturdum. Klonlanacak koda ek açıklamalar ekleyerek elde edilir veya varsayılan davranışa sahip olduğunuz gibi kodu bırakmanız yeterlidir. Yansıma kullanır, önbellekleri yazar ve fasterflect yöntemine dayanır. Klonlama işlemi çok fazla veri ve yüksek nesne hiyerarşisi için çok hızlıdır (diğer yansıma/serileştirme tabanlı algoritmalara kıyasla).

https://github.com/kalisohn/CloneBehave

Bir nuget paketi olarak da mevcuttur: https://www.nuget.org/packages/Clone.Behave/1.0.0

Örneğin: Aşağıdaki kod Adres Adresini derinden koyacaktır, ancak yalnızca _currentJob alanının sığ bir kopyasını gerçekleştirir.

public class Person 
{
  [DeepClone(DeepCloneBehavior.Shallow)]
  private Job _currentJob;      

  public string Name { get; set; }

  public Job CurrentJob 
  { 
    get{ return _currentJob; }
    set{ _currentJob = value; }
  }

  public Person Manager { get; set; }
}

public class Address 
{      
  public Person PersonLivingHere { get; set; }
}

Address adr = new Address();
adr.PersonLivingHere = new Person("John");
adr.PersonLivingHere.BestFriend = new Person("James");
adr.PersonLivingHere.CurrentJob = new Job("Programmer");

Address adrClone = adr.Clone();

//RESULT
adr.PersonLivingHere == adrClone.PersonLivingHere //false
adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false
adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true
adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true
7
kalisohn

Genel olarak, ICloneable arabirimini uygular ve Clone'u kendiniz uygularsınız. C # nesneleri, tüm ilkeller için size yardımcı olabilecek sığ bir kopya gerçekleştiren yerleşik bir MemberwiseClone yöntemine sahiptir.

Derin bir kopya için, otomatik olarak nasıl yapılacağını bilemez.

7
HappyDude

İşte derin bir kopya uygulaması:

public static object CloneObject(object opSource)
{
    //grab the type and create a new instance of that type
    Type opSourceType = opSource.GetType();
    object opTarget = CreateInstanceOfType(opSourceType);

    //grab the properties
    PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    //iterate over the properties and if it has a 'set' method assign it from the source TO the target
    foreach (PropertyInfo item in opPropertyInfo)
    {
        if (item.CanWrite)
        {
            //value types can simply be 'set'
            if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
            {
                item.SetValue(opTarget, item.GetValue(opSource, null), null);
            }
            //object/complex types need to recursively call this method until the end of the tree is reached
            else
            {
                object opPropertyValue = item.GetValue(opSource, null);
                if (opPropertyValue == null)
                {
                    item.SetValue(opTarget, null, null);
                }
                else
                {
                    item.SetValue(opTarget, CloneObject(opPropertyValue), null);
                }
            }
        }
    }
    //return the new item
    return opTarget;
}
7
dougajmcdonald

Bu yöntem benim için sorunu çözdü:

private static MyObj DeepCopy(MyObj source)
        {

            var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

            return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);

        }

Bu şekilde kullanın: MyObj a = DeepCopy(b);

6
JerryGoyal

Copyconstructors'ı şöyle seviyorum:

    public AnyObject(AnyObject anyObject)
    {
        foreach (var property in typeof(AnyObject).GetProperties())
        {
            property.SetValue(this, property.GetValue(anyObject));
        }
        foreach (var field in typeof(AnyObject).GetFields())
        {
            field.SetValue(this, field.GetValue(anyObject));
        }
    }

Kopyalanacak başka şeyler varsa ekleyin

5
LuckyLikey

Kod üreteci

Serileştirme işleminden manuel uygulamaya geçişten yansımaya kadar pek çok fikir gördük ve CGbR Code Generator kullanarak tamamen farklı bir yaklaşım önermek istiyorum. Oluşturma klonlama yöntemi bellek ve CPU açısından verimlidir ve standart DataContractSerializer'dan 300 kat daha hızlıdır.

İhtiyacınız olan tek şey ICloneable ile kısmi bir sınıf tanımıdır ve jeneratör gerisini halleder:

public partial class Root : ICloneable
{
    public Root(int number)
    {
        _number = number;
    }
    private int _number;

    public Partial[] Partials { get; set; }

    public IList<ulong> Numbers { get; set; }

    public object Clone()
    {
        return Clone(true);
    }

    private Root()
    {
    }
} 

public partial class Root
{
    public Root Clone(bool deep)
    {
        var copy = new Root();
        // All value types can be simply copied
        copy._number = _number; 
        if (deep)
        {
            // In a deep clone the references are cloned 
            var tempPartials = new Partial[Partials.Length];
            for (var i = 0; i < Partials.Length; i++)
            {
                var value = Partials[i];
                value = value.Clone(true);
                tempPartials[i] = value;
            }
            copy.Partials = tempPartials;
            var tempNumbers = new List<ulong>(Numbers.Count);
            for (var i = 0; i < Numbers.Count; i++)
            {
                var value = Numbers[i];
                tempNumbers.Add(value);
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}

Not: En son sürüm daha boş denetimlere sahip, ancak daha iyi anlamak için onları dışarıda bıraktım.

5
Toxantron

İşte benim için seri hale getirme/seri hale getirme işlemine başlamadan hızlı ve kolay bir çözüm.

public class MyClass
{
    public virtual MyClass DeepClone()
    {
        var returnObj = (MyClass)MemberwiseClone();
        var type = returnObj.GetType();
        var fieldInfoArray = type.GetRuntimeFields().ToArray();

        foreach (var fieldInfo in fieldInfoArray)
        {
            object sourceFieldValue = fieldInfo.GetValue(this);
            if (!(sourceFieldValue is MyClass))
            {
                continue;
            }

            var sourceObj = (MyClass)sourceFieldValue;
            var clonedObj = sourceObj.DeepClone();
            fieldInfo.SetValue(returnObj, clonedObj);
        }
        return returnObj;
    }
}

EDIT: gerektirir

    using System.Linq;
    using System.Reflection;

Ben böyle kullandım

public MyClass Clone(MyClass theObjectIneededToClone)
{
    MyClass clonedObj = theObjectIneededToClone.DeepClone();
}
5
Daniele D.

Bunu deneyebileceğini düşünüyorum.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it
4
Sudhanva Kotabagi

Bu adımları takip et:

  • Self döndüren ve ISelf<T> 'dan türetilen ve T Clone() yöntemini içeren ICloneable<out T>, T özelliğini içeren bir ISelf<T> tanımlayın.
  • Ardından, iletilen türe bir protected virtual generic VirtualClone cast CloneBase uygulayan bir MemberwiseClone türü tanımlayın.
  • Her türetilmiş tür, temel klonlama yöntemini çağırarak ve daha sonra, ana VirtualClone yönteminin henüz işleme koymadığı türetilmiş türün bu yönlerini düzgün bir şekilde klonlamak için yapılması gerekenleri yaparak VirtualClone öğesini uygulamalıdır.

En yüksek kalıtım çok yönlülüğü için, kamu klonlama işlevselliğini ortaya çıkaran sınıflar sealed olmalıdır, ancak klonlama eksikliği dışında aynı olan temel bir sınıftan türetilmelidir. Açık klonlanabilir türdeki değişkenleri geçmek yerine, ICloneable<theNonCloneableType> türünde bir parametre alın. Bu, Foo'nın klonlanabilir bir türevinin DerivedFoo'nın klonlanabilir bir türevi ile çalışmasını bekleyecek, fakat aynı zamanda Foo'nın klonlanamaz türevlerinin oluşturulmasına izin verecek bir rutine izin verecektir.

4
supercat

'[Seri hale getirilebilir]' ve '[DataContract]' ile çalışan kabul edilen yanıtın bir sürümünü oluşturdum. Yazdığımdan bu yana bir süre geçti, ama doğru hatırlıyorsam [DataContract] farklı bir serileştiriciye ihtiyaç duyuyordu.

Gerektirir Sistem, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;

public static class ObjectCopier
{

    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]'
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T Clone<T>(T source)
    {
        if (typeof(T).IsSerializable == true)
        {
            return CloneUsingSerializable<T>(source);
        }

        if (IsDataContract(typeof(T)) == true)
        {
            return CloneUsingDataContracts<T>(source);
        }

        throw new ArgumentException("The type must be Serializable or use DataContracts.", "source");
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]'
    /// </summary>
    /// <remarks>
    /// Found on http://stackoverflow.com/questions/78536/cloning-objects-in-c-sharp
    /// Uses code found on CodeProject, which allows free use in third party apps
    /// - http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
    /// </remarks>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T CloneUsingSerializable<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[DataContract]'
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T CloneUsingDataContracts<T>(T source)
    {
        if (IsDataContract(typeof(T)) == false)
        {
            throw new ArgumentException("The type must be a data contract.", "source");
        }

        // ** Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        DataContractSerializer dcs = new DataContractSerializer(typeof(T));
        using(Stream stream = new MemoryStream())
        {
            using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
            {
                dcs.WriteObject(writer, source);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);
                using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                {
                    return (T)dcs.ReadObject(reader);
                }
            }
        }
    }


    /// <summary>
    /// Helper function to check if a class is a [DataContract]
    /// </summary>
    /// <param name="type">The type of the object to check.</param>
    /// <returns>Boolean flag indicating if the class is a DataContract (true) or not (false) </returns>
    public static bool IsDataContract(Type type)
    {
        object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
        return attributes.Length == 1;
    }

} 
3
Jeroen Ritmeijer

Nesne Ağacınız Sıralanabilirse, bunun gibi bir şey de kullanabilirsiniz

static public MyClass Clone(MyClass myClass)
{
    MyClass clone;
    XmlSerializer ser = new XmlSerializer(typeof(MyClass), _xmlAttributeOverrides);
    using (var ms = new MemoryStream())
    {
        ser.Serialize(ms, myClass);
        ms.Position = 0;
        clone = (MyClass)ser.Deserialize(ms);
    }
    return clone;
}

bu Çözümün oldukça kolay olduğu ancak diğer çözümlerin olabileceği kadar performans göstermediği konusunda bilgi sahibi olun.

Ve eğer Sınıf büyürse, sadece serileştirilmiş olan klonlanmış alanların olacağından emin olun.

3
LuckyLikey

Sınıf nesnenizi klonlamak için Object.MemberwiseClone yöntemini kullanabilirsiniz,

sadece bu fonksiyonu sınıfınıza ekleyin:

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

sonra derinlemesine bağımsız bir kopya yapmak için sadece DeepCopy yöntemini çağırın:

yourClass newLine = oldLine.DeepCopy();

bu yardımcı olur umarım.

3
Chtiwi Malek

Tamam, bu yazıdaki yansımaların bazı açık örnekleri var, ancak onu doğru şekilde önbelleğe almaya başlayana kadar BUT yansıması genellikle yavaştır.

düzgün bir şekilde önbelleğe alırsanız, 1000000 nesnesini 4,6 s (Kepçe tarafından ölçülen) kadar derin klonlar.

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

önbelleğe alınmış özellikleri almanız veya sözlüğe yeni eklediğinizden ve bunları basitçe

foreach (var prop in propList)
{
        var value = prop.GetValue(source, null);   
        prop.SetValue(copyInstance, value, null);
}

tam kod, gönderimimi başka bir cevapta kontrol et

https://stackoverflow.com/a/34365709/4711853

3
Roma Borodov

Bu sorunun cevaplarının neredeyse tamamı tatmin edici değildi ya da benim durumumda açıkça işe yaramadığı için, AnyClone 'yi tamamen yansıtma ile uygulayıp buradaki tüm ihtiyaçları çözdüm. Seri hale getirmeyi karmaşık bir yapıya sahip karmaşık bir senaryoda çalıştıramadım ve IClonable idealden daha az - aslında gerekli olmamalıydı.

Standart yoksay özellikleri [IgnoreDataMember], [NonSerialized] kullanılarak desteklenir. Karmaşık koleksiyonları, ayarlayıcı olmayan özellikleri, salt okunur alanları vb. Destekler.

Umarım, benim yaptığım sorunları çözen başka birine yardım eder.

2
Michael Brown

Serileştiriciniz olarak Marc Gravells protobuf-net kullanılırken, kabul edilecek cevabın bazı küçük değişikliklere ihtiyacı vardır, çünkü kopyalanacak nesne [Serializable] ile ilişkilendirilmez ve bu nedenle seri hale getirilmez ve Clone yöntemi bir istisna atar.
Protobuf-net ile çalışacak şekilde değiştirdim:

public static T Clone<T>(this T source)
{
    if(Attribute.GetCustomAttribute(typeof(T), typeof(ProtoBuf.ProtoContractAttribute))
           == null)
    {
        throw new ArgumentException("Type has no ProtoContract!", "source");
    }

    if(Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    IFormatter formatter = ProtoBuf.Serializer.CreateFormatter<T>();
    using (Stream stream = new MemoryStream())
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}

Bu bir [ProtoContract] niteliğinin varlığını kontrol eder ve nesneyi serileştirmek için protobufs kendi biçimlendiricisini kullanır.

1
Basti M

"Not ISerializable " türlerini de destekleyecek C # uzantısı.

 public static class AppExtensions
 {                                                                      
       public static T DeepClone<T>(this T a)
       {
           using (var stream = new MemoryStream())
           {
               var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

               serializer.Serialize(stream, a);
               stream.Position = 0;
               return (T)serializer.Deserialize(stream);
           }
       }                                                                    
 }

Kullanım

       var obj2 = obj1.DeepClone()
1
Sameera R.

Bu IClonable arayüzü ile geçirebilirsiniz ne kadar çaba inanılmaz - Ağır sınıf hiyerarşileri var özellikle. Ayrıca MemberwiseClone nasılsa tuhaf çalışır - bu tam olarak yapıların bile normal bir Liste türü tür klonlamak etmez.

Ve serileştirme için tabii en ilginç ikilem başvuruları geri seri hale getirmek olduğunu - örneğin Eğer çocuk-ebeveyn ilişkileri var sınıf hiyerarşileri. İkili seri hale bu durumda size yardımcı olacaktır şüphesiz. (Bu özyinelemeli döngüler + yığın taşması ile sona erecek).

Bir şekilde sevdim çözüm burada önerdi: (özellikl C #) .NET bir nesnenin derin bir kopyasını nasıl yaparsınız?

ancak - bu Listelerini destek vermedi, destek de hesap yeniden ebeveynlik içine aldı belirtti. o zaman DeepClone tarafından göz ardı edilecektir ben "üst" adlı olmalıdır bu alanı veya özelliği yapmış tek kural, ebeveynlik için. Sen arka referanslar için kendi kurallarını karar vermek isteyebilirsiniz - ağaç hiyerarşileri için vb "sol/sağ" olabilir ...

Burada test kodu dahil bütün kod parçacığı geçerli:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace TestDeepClone
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.name = "main_A";
            a.b_list.Add(new B(a) { name = "b1" });
            a.b_list.Add(new B(a) { name = "b2" });

            A a2 = (A)a.DeepClone();
            a2.name = "second_A";

            // Perform re-parenting manually after deep copy.
            foreach( var b in a2.b_list )
                b.parent = a2;


            Debug.WriteLine("ok");

        }
    }

    public class A
    {
        public String name = "one";
        public List<String> list = new List<string>();
        public List<String> null_list;
        public List<B> b_list = new List<B>();
        private int private_pleaseCopyMeAsWell = 5;

        public override string ToString()
        {
            return "A(" + name + ")";
        }
    }

    public class B
    {
        public B() { }
        public B(A _parent) { parent = _parent; }
        public A parent;
        public String name = "two";
    }


    public static class ReflectionEx
    {
        public static Type GetUnderlyingType(this MemberInfo member)
        {
            Type type;
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    type = ((FieldInfo)member).FieldType;
                    break;
                case MemberTypes.Property:
                    type = ((PropertyInfo)member).PropertyType;
                    break;
                case MemberTypes.Event:
                    type = ((EventInfo)member).EventHandlerType;
                    break;
                default:
                    throw new ArgumentException("member must be if type FieldInfo, PropertyInfo or EventInfo", "member");
            }
            return Nullable.GetUnderlyingType(type) ?? type;
        }

        /// <summary>
        /// Gets fields and properties into one array.
        /// Order of properties / fields will be preserved in order of appearance in class / struct. (MetadataToken is used for sorting such cases)
        /// </summary>
        /// <param name="type">Type from which to get</param>
        /// <returns>array of fields and properties</returns>
        public static MemberInfo[] GetFieldsAndProperties(this Type type)
        {
            List<MemberInfo> fps = new List<MemberInfo>();
            fps.AddRange(type.GetFields());
            fps.AddRange(type.GetProperties());
            fps = fps.OrderBy(x => x.MetadataToken).ToList();
            return fps.ToArray();
        }

        public static object GetValue(this MemberInfo member, object target)
        {
            if (member is PropertyInfo)
            {
                return (member as PropertyInfo).GetValue(target, null);
            }
            else if (member is FieldInfo)
            {
                return (member as FieldInfo).GetValue(target);
            }
            else
            {
                throw new Exception("member must be either PropertyInfo or FieldInfo");
            }
        }

        public static void SetValue(this MemberInfo member, object target, object value)
        {
            if (member is PropertyInfo)
            {
                (member as PropertyInfo).SetValue(target, value, null);
            }
            else if (member is FieldInfo)
            {
                (member as FieldInfo).SetValue(target, value);
            }
            else
            {
                throw new Exception("destinationMember must be either PropertyInfo or FieldInfo");
            }
        }

        /// <summary>
        /// Deep clones specific object.
        /// Analogue can be found here: https://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically
        /// This is now improved version (list support added)
        /// </summary>
        /// <param name="obj">object to be cloned</param>
        /// <returns>full copy of object.</returns>
        public static object DeepClone(this object obj)
        {
            if (obj == null)
                return null;

            Type type = obj.GetType();

            if (obj is IList)
            {
                IList list = ((IList)obj);
                IList newlist = (IList)Activator.CreateInstance(obj.GetType(), list.Count);

                foreach (object elem in list)
                    newlist.Add(DeepClone(elem));

                return newlist;
            } //if

            if (type.IsValueType || type == typeof(string))
            {
                return obj;
            }
            else if (type.IsArray)
            {
                Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);

                for (int i = 0; i < array.Length; i++)
                    copied.SetValue(DeepClone(array.GetValue(i)), i);

                return Convert.ChangeType(copied, obj.GetType());
            }
            else if (type.IsClass)
            {
                object toret = Activator.CreateInstance(obj.GetType());

                MemberInfo[] fields = type.GetFieldsAndProperties();
                foreach (MemberInfo field in fields)
                {
                    // Don't clone parent back-reference classes. (Using special kind of naming 'parent' 
                    // to indicate child's parent class.
                    if (field.Name == "parent")
                    {
                        continue;
                    }

                    object fieldValue = field.GetValue(obj);

                    if (fieldValue == null)
                        continue;

                    field.SetValue(toret, DeepClone(fieldValue));
                }

                return toret;
            }
            else
            {
                // Don't know that type, don't know how to clone it.
                if (Debugger.IsAttached)
                    Debugger.Break();

                return null;
            }
        } //DeepClone
    }

}
1
TarmoPikaro

Yine başka bir JSON.NET cevap. Bu sürüm ISerializable uygulamayan sınıflarla çalışır.

public static class Cloner
{
    public static T Clone<T>(T source)
    {
        if (ReferenceEquals(source, null))
            return default(T);

        var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };

        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, settings), settings);
    }

    class ContractResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Select(p => base.CreateProperty(p, memberSerialization))
                .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    .Select(f => base.CreateProperty(f, memberSerialization)))
                .ToList();
            props.ForEach(p => { p.Writable = true; p.Readable = true; });
            return props;
        }
    }
}
1
Matthew Watson

Bir eşleştirici derin kopya yapar. Nesnenizin Foreach üyesi yeni bir nesne oluşturur ve tüm değerlerini atar. Her ilkel olmayan iç eleman üzerinde özyinelemeli çalışır.

Size en hızlı, şu anda aktif olarak geliştirilenlerden birini öneririm. Ben UltraMapper https://github.com/maurosampietro/UltraMapper

Nuget paketleri: https://www.nuget.org/packages/UltraMapper/

1
Mauro Sampietro

Derin klonlama kopyalamakla ilgilidir durum. .net için durum, alanlar anlamına gelir.

Diyelim ki bir hiyerarşi var:

static class RandomHelper
{
    private static readonly Random random = new Random();

    public static int Next(int maxValue) => random.Next(maxValue);
}

class A
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(A).Name}.{nameof(random)} = {random}";
}

class B : A
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(B).Name}.{nameof(random)} = {random} {base.ToString()}";
}

class C : B
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(C).Name}.{nameof(random)} = {random} {base.ToString()}";
}

Klonlama yapılabilir:

static class DeepCloneExtension
{
    // consider instance fields, both public and non-public
    private static readonly BindingFlags bindingFlags =
        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

    public static T DeepClone<T>(this T obj) where T : new()
    {
        var type = obj.GetType();
        var result = (T)Activator.CreateInstance(type);

        do
            // copy all fields
            foreach (var field in type.GetFields(bindingFlags))
                field.SetValue(result, field.GetValue(obj));
        // for every level of hierarchy
        while ((type = type.BaseType) != typeof(object));

        return result;
    }
}

Demo1:

Console.WriteLine(new C());
Console.WriteLine(new C());

var c = new C();
Console.WriteLine($"{Environment.NewLine}Image: {c}{Environment.NewLine}");

Console.WriteLine(new C());
Console.WriteLine(new C());

Console.WriteLine($"{Environment.NewLine}Clone: {c.DeepClone()}{Environment.NewLine}");

Console.WriteLine(new C());
Console.WriteLine(new C());

Sonuç:

C.random = 92 B.random = 66 A.random = 71
C.random = 36 B.random = 64 A.random = 17

Image: C.random = 96 B.random = 18 A.random = 46

C.random = 60 B.random = 7 A.random = 37
C.random = 78 B.random = 11 A.random = 18

Clone: C.random = 96 B.random = 18 A.random = 46

C.random = 33 B.random = 63 A.random = 38
C.random = 4 B.random = 5 A.random = 79

Dikkat, tüm yeni nesneler random alanı için rasgele değerlere sahiptir, ancak clone tam olarak image ile eşleşir

demo2 dosyası:

class D
{
    public event EventHandler Event;
    public void RaiseEvent() => Event?.Invoke(this, EventArgs.Empty);
}

// ...

var image = new D();
Console.WriteLine($"Created obj #{image.GetHashCode()}");

image.Event += (sender, e) => Console.WriteLine($"Event from obj #{sender.GetHashCode()}");
Console.WriteLine($"Subscribed to event of obj #{image.GetHashCode()}");

image.RaiseEvent();
image.RaiseEvent();

var clone = image.DeepClone();
Console.WriteLine($"obj #{image.GetHashCode()} cloned to obj #{clone.GetHashCode()}");

clone.RaiseEvent();
image.RaiseEvent();

Sonuç:

Created obj #46104728
Subscribed to event of obj #46104728
Event from obj #46104728
Event from obj #46104728
obj #46104728 cloned to obj #12289376
Event from obj #12289376
Event from obj #46104728

Dikkat, etkinlik destek alanı da kopyalanır ve istemci de klon etkinliğine abone olur.

1
Ted Mucuzany

Genel yaklaşımların hepsi teknik olarak geçerlidir, ancak nadiren gerçekten derin bir kopyaya ihtiyaç duyduğumuzdan kendimden bir not eklemek istedim, ve gerçek iş uygulamalarında genel derin kopyalamayı kullanmak şiddetle karşı çıkacağım. nesnelerin kopyalandığı ve daha sonra açıkça değiştirildiği yerler, kaybolması kolaydır.

Gerçek hayattaki birçok durumda, sadece veri erişim çerçevesine bağlı değil, aynı zamanda pratikte kopyalanan iş nesnelerinin nadiren% 100 olması gerektiği için, kopyalama işlemi üzerinde mümkün olduğunca ayrıntılı bir kontrol sahibi olmak istersiniz. ORM tarafından nesne referanslarını tanımlamak için kullanılan örnek bir başvuru kimliğini düşünün, tam bir kopya da bu kimliği kopyalar, böylece bellekteki nesneler farklı olur, veri deposuna gönderir göndermez şikayet eder, yine de kopyaladıktan sonra bu özellikleri el ile değiştirmeniz gerekir ve nesne değişirse, genel derin kopyalamayı kullanan tüm yerlerde ayarlamanız gerekir.

ICloneable ile @cregox cevabını genişleterek, aslında derin bir kopya nedir? Öbek üzerinde, orijinal nesneyle aynı olan ancak genel bir klonlama işlevi kullanmak yerine, yalnızca yeni bir nesne oluşturmak yerine, farklı bir bellek alanı kaplayan, yeni ayrılmış bir nesnedir.

Etki alanı nesnelerimde statik fabrika yöntemleri fikrini kişisel olarak kullanıyorum.

Örnek:

    public class Client
    {
        public string Name { get; set; }

        protected Client()
        {
        }

        public static Client Clone(Client copiedClient)
        {
            return new Client
            {
                Name = copiedClient.Name
            };
        }
    }

    public class Shop
    {
        public string Name { get; set; }

        public string Address { get; set; }

        public ICollection<Client> Clients { get; set; }

        public static Shop Clone(Shop copiedShop, string newAddress, ICollection<Client> clients)
        {
            var copiedClients = new List<Client>();
            foreach (var client in copiedShop.Clients)
            {
                copiedClients.Add(Client.Clone(client));
            }

            return new Shop
            {
                Name = copiedShop.Name,
                Address = newAddress,
                Clients = copiedClients
            };
        }
    }

Birisi, kopyalama işlemini tam kontrol altında tutarken, kişisel olarak çok başarılı olduğum bir çözümdür. Korunan inşaatçılar da bunu yapar, diğer geliştiriciler, nesnenin içindeki inşaat mantığını içine alan temiz ve tek bir nesne başlatma noktası veren fabrika yöntemlerini kullanmak zorunda kalırlar. Ayrıca, yöntemi aşırı yükleyebilir ve gerekirse farklı yerler için birkaç klon mantığına sahip olabilirsiniz.

0

Sorumluluk reddi: Söz konusu paketin yazarıyım.

2019'da bu soruya verilen cevapların hala serileştirme veya yansıma nasıl kullandığına şaşırdım.

Seri hale getirme sınırlayıcıdır (özellikler, belirli yapıcılar vb. Gerektirir) ve çok yavaştır

BinaryFormatter, Serializable niteliğini gerektirir, JsonConverter, parametresiz bir kurucu veya nitelikler gerektirir, hiçbiri salt okunur alanları veya arabirimleri çok iyi kullanmaz ve her ikisi de gerekenden 10-30 kat daha yavaştır.

İfade Ağaçları

Bunun yerine İfade Ağaçları veya Reflection.Emit öğesini yalnızca bir kez klonlama kodu oluşturmak için kullanabilirsiniz, daha sonra yavaş derleme yerine bu derlenmiş kodu kullanın veya seri hale getirme.

Problemi kendim görerek tatmin edici bir çözüm göremediğimde, tam da bunu yapan ve her tipte çalışan ve neredeyse her zamanki özel yazılı kod kadar hızlı bir paket oluşturmaya karar verdim .

Projeyi GitHub'da bulabilirsiniz: https://github.com/marcelltoth/ObjectCloner

Kullanım

NuGet'ten yükleyebilirsiniz. ObjectCloner paketini alın ve aşağıdaki gibi kullanın:

var clone = ObjectCloner.DeepClone(original);

veya nesne türünüzü uzantılarla kirletmeyi sakıncası yoksa ObjectCloner.Extensions değerini de yazın ve şunu yazın:

var clone = original.DeepClone();

Performans

Bir sınıf hiyerarşisini klonlamanın basit bir ölçütü, Yansıma'yı kullanmaktan ~ 3 kat daha hızlı, Newtonsoft'tan 12 kat daha hızlıydı. Jackson serileştirmesi ve önerilenden ~ 36 kat daha hızlı BinaryFormatter.

0
Marcell Toth

System.Text.Json kullanarak:

https://devblogs.Microsoft.com/dotnet/try-the-new-system-text-json-apis/

public static T DeepCopy<T>(this T source)
{
    return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}

Yeni API Span<T> kullanıyor. Bu hızlı olmalı, bazı kıyaslamalar yapmak iyi olurdu.

Not: Json.NET'teki gibi ObjectCreationHandling.Replace gibi, koleksiyon değerlerini varsayılan olarak değiştireceği için gerek yoktur. Her şey yeni resmi API ile değiştirileceği için Json.NET'i şimdi unutmalısınız.

Bunun özel alanlarla çalışacağından emin değilim.

0
Konrad

Bunu yapmanın yeni bir yolunu buldum, Emit.

Uygulamayı uygulamak ve çalıştırmak için IL'yi eklemek için Emit'i kullanabiliriz. Fakat bunun cevabımı yazmam için mükemmelleştirmek istediğim için bunun iyi bir yol olduğunu sanmıyorum.

Emit, resmi belgeyi ve Rehber 'i görebilir.

Kodu okumak için biraz IL öğrenmelisin. Özelliği sınıfta kopyalayabilecek kodu yazacağım.

public static class Clone
{        
    // ReSharper disable once InconsistentNaming
    public static void CloneObjectWithIL<T>(T source, T los)
    {
        //see http://lindexi.oschina.io/lindexi/post/C-%E4%BD%BF%E7%94%A8Emit%E6%B7%B1%E5%85%8B%E9%9A%86/
        if (CachedIl.ContainsKey(typeof(T)))
        {
            ((Action<T, T>) CachedIl[typeof(T)])(source, los);
            return;
        }
        var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
        ILGenerator generator = dynamicMethod.GetILGenerator();

        foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
        {
            //do not copy static that will except
            if (temp.GetAccessors(true)[0].IsStatic)
            {
                continue;
            }

            generator.Emit(OpCodes.Ldarg_1);// los
            generator.Emit(OpCodes.Ldarg_0);// s
            generator.Emit(OpCodes.Callvirt, temp.GetMethod);
            generator.Emit(OpCodes.Callvirt, temp.SetMethod);
        }
        generator.Emit(OpCodes.Ret);
        var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
        CachedIl[typeof(T)] = clone;
        clone(source, los);
    }

    private static Dictionary<Type, Delegate> CachedIl { set; get; } = new Dictionary<Type, Delegate>();
}

Kod derin kopya olabilir, ancak özelliği kopyalayabilir. IL için değiştirebileceğiniz bu derin kopyasına yapmak istiyorsanız, bunu yapamayacağım kadar zor.

0
lindexi

Klonlamayı çözmek için hızlı, kolay, etkili Nuget paketi

Tüm cevapları okuduktan sonra kimsenin bu mükemmel paketten bahsetmediğine şaşırdım:

https://github.com/force-net/DeepCloner

Benioku üzerinde biraz ayrıntılı, işte seçtiğimiz nedeni işte:

Yasal Uyarı - gereksinimler:

  • . NET 4.0 veya üstü veya .NET Standard 1.3 (.NET Core)
  • Tam Güven izni seti veya Yansıtma izni gerektiriyor (MemberAccess)
  • Derin veya sığ kopya olabilir
  • Derin klonlamada tüm nesne grafiği korunur.
  • Çalışma zamanında kod oluşturmayı kullanır, çünkü sonuç klonlaması caydırıcı derecede hızlıdır
  • Dahili yapı tarafından kopyalanan nesneler, çağrılan yöntem veya bağlayıcı yok
  • Sınıfları bir şekilde işaretlemeniz gerekmez (Serializable-niteliği veya arayüzleri uygulama gibi)
  • Klonlama için nesne türünü belirtme zorunluluğu yoktur. Nesne, arabirime veya soyut bir nesne olarak atılabilir (örneğin, bir dizi diziyi soyut Array veya IEnumerable olarak klonlayabilirsiniz; boş olsa bile hiçbir hata olmadan klonlanabilir)
  • Klonlanmış nesnenin klon olduğunu belirleme yeteneği yoktur (çok özel yöntemler hariç)

Kullanımı bu kadar kolay:

  var deepClone = new { Id = 1, Name = "222" }.DeepClone();
  var shallowClone = new { Id = 1, Name = "222" }.ShallowClone();
0
alexlomba87

temelde bir otomatik kopya kurucuyu çağırması gereken bir yöntemin içinde yeniden yayınlamaya ne dersiniz

T t = new T();
T t2 = (T)t;  //eh something like that

        List<myclass> cloneum;
        public void SomeFuncB(ref List<myclass> _mylist)
        {
            cloneum = new List<myclass>();
            cloneum = (List < myclass >) _mylist;
            cloneum.Add(new myclass(3));
            _mylist = new List<myclass>();
        }

bana iş gibi görünüyor

0
will_m

Bu, bir nesnenin okunabilir ve yazılabilir özelliklerini bir başkasına kopyalar.

 public class PropertyCopy<TSource, TTarget> 
                        where TSource: class, new()
                        where TTarget: class, new()
        {
            public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
            {
                if (src==null) return trg;
                if (trg == null) trg = new TTarget();
                var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
                if (properties != null && properties.Count() > 0)
                    fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
                if (fulllist == null || fulllist.Count() == 0) return trg;

                fulllist.ForEach(c =>
                    {
                        c.SetValue(trg, c.GetValue(src));
                    });

                return trg;
            }
        }

ve bu nasıl kullandığınız:

 var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
                                                            "Creation",
                                                            "Description",
                                                            "IdTicketStatus",
                                                            "IdUserCreated",
                                                            "IdUserInCharge",
                                                            "IdUserRequested",
                                                            "IsUniqueTicketGenerated",
                                                            "LastEdit",
                                                            "Subject",
                                                            "UniqeTicketRequestId",
                                                            "Visibility");

veya her şeyi kopyalamak için:

var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);
0
Ylli Prifti