Soru Derin klonlama nesneleri


Ben şöyle bir şey yapmak istiyorum:

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

Ve sonra özgün nesnesine yansıtılmamış yeni nesnede değişiklikler yapın.

Bu işleve sık sık ihtiyacım yok, bu yüzden gerekli olduğunda, yeni bir nesne oluşturmaya ve sonra her bir özelliği tek tek kopyalamaya çalıştım ama her zaman bana daha iyi ya da daha zarif bir yolun olduğu hissini veriyor. durum.

Bir nesneyi nasıl kopyalayabilirim veya kopyalayabilirim ki klonlanan nesne, orijinal nesneye yansıtılan herhangi bir değişiklik olmadan değiştirilebilir mi?


1831
2017-09-17 00:06


Menşei


Faydalı olabilir: "Bir Nesneyi Kopyalama neden yapılması gereken korkunç bir şeydir?" agiledeveloper.com/articles/cloning072002.htm - Pedro77
stackoverflow.com/questions/8025890/... Başka bir çözüm... - Felix K.
AutoMapper'a bir göz atmalısın. - Daniel Little
Çözümün çok daha karmaşık, onu okumayı kaybettim ... hehehe. DeepClone arayüzü kullanıyorum. public interface IDeepCloneable <T> {T DeepClone (); } - Pedro77
@ Pedro77: Sahip olduğum bir endişe IDeepCloneable derin klonlanabilecek şeylere yapılan tüm referansların toplanması gerekli değildir; klonlama yaparken uygun davranış List<T> sadece bağlı değil Tama aynı zamanda listelerin amacı üzerine. Listelerdeki herhangi bir öğe, hiçbir zaman bunları değiştirecek bir şeye maruz kalmayacaksa, listelerdeki öğeler klonlanabilse bile, doğrudan referansları kopyalamak daha iyi olacaktır. - supercat


Cevaplar:


Standart uygulama, uygulamaktır. ICloneable arabirim (tarif edilen İşte, bu yüzden yeniden doğurtmayacağım), işte bulduğum güzel bir derin klonlu nesne fotokopi makinesi. Kod Projesi bir süre önce ve bizim eşyalarımıza dahil ettik.

Başka yerlerde belirtildiği gibi, nesnelerin serileştirilmesini 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 serileştirmesi ve daha sonra onu yeni bir nesneye dönüştürmesidir. Bir faydası, bir nesne çok karmaşık hale geldiğinde her şeyi klonlamakla ilgili olarak endişelenmenize gerek olmamasıdır.

Ve uzatma yöntemlerinin kullanımıyla (ayrıca, orijinal olarak başvurulan kaynaktan):

Eğer yeni kullanmayı tercih ederseniz uzatma yöntemleri C # 3.0, aşağıdaki imzanın olması için yöntemi değiştirin:

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

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

DÜZENLE (10 Ocak 2015) Bunu tekrarlayacağımı düşündüğümde, bunu yapmak için (Newtonsoft) Json'u kullanmaya başladığımı belirtmek için olmalı Daha hafiftir ve [Serializable] etiketlerinin yükünü ortadan kaldırır. (NB @atconway, yorumlarda özel üyelerin JSON yöntemi kullanılarak klonlamadığı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);
}

1467
2018-04-03 13:31



stackoverflow.com/questions/78536/cloning-objects-in-c/... Yukarıdaki koda bir bağlantı vardır [ve bu bağlamda daha uygun olan diğer iki uygulamadan bahseder] - Ruben Bartelink
Serileştirme / serileştirme, gerekli olmayan önemli ek yükü içerir. C # 'de ICONeable arabirim ve .MemberWise () klon yöntemleri bakın. - 3Dave
@David, verilen, ancak nesneler hafif ve bunları kullanırken performans isabet, ihtiyaçlarınız için çok yüksek değil, o zaman bu yararlı bir ipucu. Bir döngüde büyük miktarda veriyle yoğun bir şekilde kullanmadım, itiraf ediyorum ama tek bir performans kaygısı görmedim. - johnc
@Amir: aslında, hayır: typeof(T).IsSerializable eğer tip ile işaretlenmişse de doğrudur [Serializable] bağlıyor. Uygulamak zorunda değil ISerializable arayüz. - Daniel Gehriger
Sadece bu yöntemin yararlı olduğunu ve kendimi birçok kez kullandım, Medium Trust ile uyumlu olmadığını söyledim, bu yüzden uyumluluk gerektiren bir kod yazıyorsanız dikkat edin. BinaryFormatter özel alanlara erişir ve bu nedenle kısmi güven ortamları için varsayılan izin kümesinde çalışamaz. Başka bir serileştirici deneyebilirsiniz, ancak arayan kişinin gelen nesnenin özel alanlara dayanması durumunda klonun mükemmel olmayabileceğini bildiğinden emin olun. - Alex Norcliffe


Çoğunlukla ilkel ve listelerin çok basit nesneleri için bir klon istedim. Nesneniz JSON serileştirilebilir kutusunun dışındaysa, bu yöntem hile yapacaktır. Bu, klonlanmış sınıfta, JSON.NET gibi bir JSON serileştiricisi üzerinde herhangi bir değişiklik yapılmasını veya arayüzlerin uygulanmasını gerektirmez.

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

183
2017-09-17 01:12



Solutiojn, BinaryFormatter çözümünden bile daha hızlıdır. .NET Serileştirme Performansı Karşılaştırması - esskar
Bunun için teşekkürler. Aslında C # için MongoDB sürücüsü ile birlikte gelen BSON serializer ile aynı şeyi yapabildim. - Mark Ewer
Bu benim için en iyi yoldur. Newtonsoft.Json.JsonConvert ama aynı - Pierre
JSON yaklaşımı küçük düz nesneler için Tamam, ama asıl soru derin olanlar da dahil olmak üzere herhangi bir nesne hakkındaydı. NFX.Slim serileştiricisi, delegeleri ve yönetilmeyen işaretçileri olmadığı sürece herhangi bir .NET türünde büyüklükteki siparişleri daha hızlı çalışır, burada BinaryFormnatter gibi çok az çalışan, yalnızca en az 5 kat daha hızlı çalışan kaynaktır: github.com/aumcode/nfx/blob/master/Source/NFX/Serialization/...    Test paketi: github.com/aumcode/serbenchdaha hızlı olan tek seriverenin, dinamizm ve referanslardan yoksun Protobuf olduğunu kanıtlıyor. - itadapter DKh
Bunun çalışması için nesnenin klonlanması, daha önce de belirtildiği gibi serileştirilmeye ihtiyaç duyar - bu aynı zamanda örneğin, dairesel bağımlılıklara sahip olmayabilir. - radomeit


Kullanmama nedeni ICloneable olduğu değil çünkü genel bir arayüze sahip değil. Bunu kullanmamanın sebebi, belirsiz olması.. Sığ veya derin bir kopya alıp almadığınız belli değil; Bu uygulayıcıya kadar.

Evet, MemberwiseClone sığ bir kopya yapar, ancak tersi MemberwiseClone değil Clone; belki olurdu DeepClonevar olmayan var. Bir nesneyi ICONeable arabirimiyle kullandığınızda, temel nesnenin hangi tür klonlama yaptığını bilemezsiniz. (Ve XML yorumları bunu açıklığa kavuşturmayacaktır, çünkü nesnenin Clone yöntemindeki arayüzlerden ziyade, arayüz yorumlarını alacaksınız.)

Genelde yaptığım şey sadece bir Copy Tam olarak istediğimi yapan yöntem.


147
2017-09-26 20:18



ICONeable'un neden belirsiz olarak kabul edildiğini açık değilim. Dictionary (Of T, U) gibi bir tür verildiğinde, ICONeable.Clone'un, ne olursa olsun, yeni sözlüğü, aynı T ve U'ları içeren bağımsız bir sözlük haline getirmek için ne kadar derin ve sığ kopyalama yapılması gerektiğini beklerim. ve / veya nesne referansları) orijinal olarak. Belirsizlik nerede? Emin olmak gerekirse, “Kendilik” yöntemini içeren, kendiliğinden (T'den) miras alan genel bir ICON olabilir (Of T) çok daha iyi olurdu, ama ben derinlikle sığ klonlama üzerinde muğlaklık görmüyorum. - supercat
Örneğiniz sorunu gösterir. Bir Sözlük <string, Müşteri> olduğunu varsayalım. Klonlanmış sözlük aynı Müşteri nesneleri orijinal olarak veya kopyalar Bu müşteri nesnelerinin? Her ikisi için makul kullanım durumları vardır. Ancak ICONeable, hangisini alacağınızı açıklığa kavuşturmaz. Bu yüzden kullanışlı değil. - Ryan Lundy
@Kyralessa Microsoft MSDN makalesinde, derin veya sığ bir kopyasını talep edip etmediğinizi bilmemeniz gereken bu çok sorun var. - crush
Yinelenen cevap stackoverflow.com/questions/129389/... özyinelemeli Üyeliğe dayalı Kopyalama uzantısını açıklar - Michael Freidgeim


Burada bağlanan seçeneklerin çoğunu ve bu sorun için olası çözümleri okuduktan sonra, inanıyorum. tüm seçenekler oldukça iyi özetlenmiştir Ian Pbağlantısı (tüm diğer seçenekler, bunların çeşitleridir) ve en iyi çözüm, Pedro77bağlantısı soru yorumlarında.

Bu yüzden, bu 2 referansın ilgili bölümlerini buraya kopyalayacağım. Bu şekilde sahip olabiliriz:

Keskin c nesneleri klonlama için en iyi şey!

İlk ve en önemlisi, hepsi bizim seçeneklerdir:

İfade Ağaçları ile Hızlı Derin Kopyalama   Aynı zamanda seri hale getirme, Yansıma ve İfade Ağaçları ile klonlamanın performans karşılaştırmasına da sahiptir.

Neden seçiyorum ICloneable (manuel olarak)

Bay Venkat Subramaniam (burada gereksiz bağlantı) neden daha detaylı açıklıyor.

Tüm makalesi, 3 nesne kullanarak çoğu vaka için geçerli olmaya çalışan bir örnek etrafında: Kişi, Beyin ve Kent. Kendi beynine ama aynı şehre sahip olacak bir kişiyi klonlamak istiyoruz. Yukarıdaki diğer yöntemlerden herhangi birini yazdırabilir veya okuyabilirsiniz.

Bu onun sonucunun biraz değiştirilmiş versiyonudur:

Bir nesneyi belirterek kopyalama New onu takip eden sınıf adı genellikle genişletilemeyen kodlara yol açar. Klon kullanarak, prototip desen uygulaması, bunu başarmak için daha iyi bir yoldur. Bununla birlikte, C # (ve Java) içinde sağlandığı gibi klon kullanımı da oldukça sorunlu olabilir. Korunan (halka açık olmayan) bir kopya oluşturucunun sağlanması ve klon yönteminden yararlanılması daha iyidir. Bu bize, bir nesneyi bir sınıfın kendisinin bir örneğine verme görevini devretme yeteneği verir, böylece genişletilebilirlik sağlar ve ayrıca korunan kopya oluşturucuyu kullanarak nesneleri güvenli bir şekilde yaratır.

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 Kişi'den türeyen bir sınıfa sahip olmayı 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 Brain@1fcc69
This is person with Brain@253498
SkilledPerson: This is person with SmarterBrain@1fef6f
SkilledPerson: This is person with SmarterBrain@209f4e

Nesne sayısının bir miktarını tutarsak, burada uygulanan klonun, nesne sayısının doğru bir sayısını muhafaza edeceğini gözlemleyin.


84
2017-09-17 00:13



MS kullanmamanızı önerir ICloneable kamu üyeleri için. "Klon arayanlar, öngörülebilir bir klonlama işlemini gerçekleştiren yönteme bağlı olamayacağı için, genel API'lerde ICONeable uygulamasının uygulanmamasını öneririz." msdn.microsoft.com/en-us/library/... Ancak, bağlantılı yazınızdaki Venkat Subramaniam tarafından verilen açıklamaya dayanarak, bu durumda kullanımının mantıklı olduğunu düşünüyorum. ICONeable nesnelerin yaratıcıları, hangi özelliklerin derin ve sığ kopyalara sahip olması gerektiği konusunda derin bir anlayışa sahip oldukları sürece (yani derin kopya beyin, sığ kopya şehir) - BateTech
İlk olarak, bu konudaki bir uzmandan çok uzaktayım (public APIs). ben düşünmek Bir keresinde MS sözünün çok anlamlı olduğunu söyledi. Ve sanırım bunu kabul etmek güvenli değil. kullanıcılar Bu API'nın böyle derin bir anlayışı olacaktır. Yani, sadece bir genel API eğer kim kullanacak olursa olsun gerçekten önemli değil. ben tahmin Her mülk üzerinde çok açık bir şekilde ayrım yapan bir tür UML'ye sahip olmak yardımcı olabilir. Ama daha fazla deneyime sahip birinden duymak isterim. : P - cregox
Kullanabilirsiniz CGbR Klon Üreticisi ve kodu elle yazmadan benzer bir sonuç elde edin. - Toxantron
Orta Dil uygulaması yararlıdır - Michael Freidgeim


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


69
2018-03-16 11:38



.Net'de kopya kurucular yok. - Pop Catalin
Elbette yapar: yeni MyObject (objToCloneFrom) Sadece nesneyi bir parametre olarak klonlayan bir ctor bildir. - Nick
Aynı şey değil. Her sınıfa manuel olarak eklemelisiniz, ve hatta derin bir kopyasını garanti edip etmediğinizi bile bilmiyorsunuz. - Dave Van den Eynde
Kopya ctor için +1. Her sınıftaki nesne için bir clone () işlevini elle yazmanız ve sınıf hiyerarşiniz birkaç düzeye geldiğinde bununla iyi şanslar oluşturmanız gerekir. - Andrew Grant
Kopya kurucularla birlikte hiyerarşi kaybedersiniz. agiledeveloper.com/articles/cloning072002.htm - Will


Tüm kamu özelliklerini kopyalamak için basit uzatma yöntemi. Herhangi bir nesne için çalışır ve değil olmak için sınıf gerektirir [Serializable]. Diğer erişim seviyesi için uzatılabilir.

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

36
2017-12-02 17:39



Bu maalesef kusurlu. ObjectOne.MyProperty = objectTwo.MyProperty öğesini çağırmakla eşdeğerdir (yani, yalnızca referansı kopyalayacaktır). Özelliklerin değerlerini klonlamayacaktır. - Alex Norcliffe
Alex Norcliffe'e: Sorunun yazarı "her özelliği kopyalama" dan sonra klonlama hakkında sordu. Çoğu durumda, özelliklerin tam olarak çoğaltılması gerekli değildir. - Konstantin Salavatov
Bu yöntemi kullanmayı düşünüyorum ama yineleme ile. Bu nedenle, bir özelliğin değeri bir başvuruysa, yeni bir nesne oluşturun ve CopyTo'yu tekrar arayın. Sadece bir problem görüyorum, kullanılan tüm sınıfların parametresiz bir kurucuya sahip olması gerekir. Bunu zaten deneyen var mı? Ayrıca bu gerçekten DataRow ve DataTable gibi .net sınıfları içeren özellikleri ile çalışacak mı merak ediyorum? - Koryu


Ben Silverlight'da ICONeable kullanarak problemlerim vardı, fakat ben seralizasyon fikrini sevdim, XML'i seralebilirim, bu yüzden şunu yaptım:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //michael@hollyspringsconsulting.com
    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);
    }
}

28
2017-10-15 17:55