Câu hỏi Các đối tượng nhân bản sâu


Tôi muốn làm một cái gì đó như:

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

Và sau đó thực hiện thay đổi đối tượng mới không được phản ánh trong đối tượng gốc.

Tôi không thường cần chức năng này, vì vậy khi cần thiết, tôi đã sử dụng để tạo một đối tượng mới và sau đó sao chép từng thuộc tính riêng lẻ, nhưng nó luôn khiến tôi cảm thấy có cách xử lý tốt hơn hoặc thanh lịch hơn tình huống.

Làm thế nào tôi có thể sao chép hoặc sao chép sâu một đối tượng để đối tượng nhân bản có thể được sửa đổi mà không có bất kỳ thay đổi nào được phản ánh trong đối tượng gốc?


1831
2017-09-17 00:06


gốc


Có thể hữu ích: "Tại sao Sao chép một đối tượng là một điều khủng khiếp phải làm?" agiledeveloper.com/articles/cloning072002.htm - Pedro77
stackoverflow.com/questions/8025890/… Giải pháp khác... - Felix K.
Bạn nên có một cái nhìn tại AutoMapper - Daniel Little
Giải pháp của bạn phức tạp hơn nhiều, tôi đã mất đọc nó ... hehehe. Tôi đang sử dụng giao diện DeepClone. giao diện công cộng IDeepCloneable <T> {T DeepClone (); } - Pedro77
@ Pedro77: Một mối quan tâm tôi có IDeepCloneable đó không phải là tất cả các bộ sưu tập các tham chiếu đến những thứ có thể được nhân bản sâu; các hành vi thích hợp khi nhân bản một List<T> không chỉ phụ thuộc vào T, nhưng cũng theo mục đích của danh sách. Nếu không có mục nào trong danh sách sẽ được tiếp xúc với bất kỳ thứ gì sẽ làm thay đổi chúng, thì ngay cả khi các mục trong danh sách có thể được nhân bản, thì tốt hơn là sao chép trực tiếp các tham chiếu. - supercat


Các câu trả lời:


Trong khi thực hành tiêu chuẩn là thực hiện ICloneable giao diện (được mô tả đây, vì vậy tôi sẽ không nôn nao), đây là một máy sao chép đối tượng bản sao sâu sắc đẹp mà tôi tìm thấy Dự án mã một thời gian trước và kết hợp nó trong công cụ của chúng tôi.

Như đã đề cập ở nơi khác, nó đòi hỏi các đối tượng của bạn phải được tuần tự hóa.

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

Ý tưởng là nó serializes đối tượng của bạn và sau đó deserializes nó vào một đối tượng tươi. Lợi ích là bạn không phải lo lắng về bản sao mọi thứ khi đối tượng trở nên quá phức tạp.

Và với việc sử dụng các phương pháp mở rộng (cũng từ nguồn tham chiếu ban đầu):

Trong trường hợp bạn thích sử dụng phương pháp mở rộng của C # 3.0, thay đổi phương thức để có chữ ký sau:

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

Bây giờ gọi phương thức đơn giản trở thành objectBeingCloned.Clone();.

CHỈNH SỬA (10 tháng 1 năm 2015) Nghĩ rằng tôi sẽ xem lại điều này, đề cập đến gần đây tôi đã bắt đầu sử dụng (Newtonsoft) Json để làm điều này, nó nên là nhẹ hơn và tránh chi phí của các thẻ [Serializable]. (NB @atconway đã chỉ ra trong các ý kiến ​​rằng các thành viên riêng không được nhân bản bằng cách sử dụng phương thức JSON)

/// <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/… có liên kết đến mã ở trên [và tham chiếu hai triển khai khác như vậy, một trong số đó là phù hợp hơn trong ngữ cảnh của tôi] - Ruben Bartelink
Serialization / deserialization liên quan đến chi phí đáng kể đó là không cần thiết. Xem giao diện ICloneable và .MemberWise () các phương thức sao chép trong C #. - 3Dave
@ David, được cấp, nhưng nếu các đối tượng là ánh sáng, và hiệu suất đạt khi sử dụng nó không phải là quá cao cho các yêu cầu của bạn, sau đó nó là một mẹo hữu ích. Tôi đã không sử dụng nó một cách mạnh mẽ với một lượng lớn dữ liệu trong một vòng lặp, tôi thừa nhận, nhưng tôi chưa bao giờ thấy một mối quan tâm hiệu suất duy nhất. - johnc
@Amir: thực sự, không: typeof(T).IsSerializable cũng đúng nếu loại đã được đánh dấu bằng [Serializable] thuộc tính. Nó không phải thực hiện ISerializable giao diện. - Daniel Gehriger
Chỉ cần nghĩ rằng tôi muốn đề cập rằng trong khi phương pháp này là hữu ích, và tôi đã sử dụng nó bản thân mình một thời gian, nó không phải ở tất cả tương thích với Medium Trust - vì vậy xem ra nếu bạn đang viết mã cần tương thích. BinaryFormatter truy cập các trường riêng và do đó không thể làm việc trong các permissionet mặc định cho các môi trường tin cậy một phần. Bạn có thể thử serializer khác, nhưng hãy chắc chắn rằng người gọi của bạn biết rằng bản sao có thể không hoàn hảo nếu đối tượng đến dựa vào các trường riêng. - Alex Norcliffe


Tôi muốn một cloner cho các đối tượng rất đơn giản của chủ yếu là nguyên thủy và danh sách. Nếu đối tượng của bạn nằm ngoài hộp JSON serializable thì phương thức này sẽ thực hiện thủ thuật. Điều này không yêu cầu phải sửa đổi hoặc thực hiện các giao diện trên lớp nhân bản, chỉ là một trình nối tiếp JSON như JSON.NET.

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

183
2017-09-17 01:12



solutiojn thậm chí còn nhanh hơn giải pháp BinaryFormatter, So sánh hiệu suất tuần tự hóa .NET - esskar
Cám ơn vì cái này. Tôi đã có thể làm cơ bản cùng một điều với serializer BSON mà tàu với trình điều khiển MongoDB cho C #. - Mark Ewer
Đây là cách tốt nhất cho tôi, Tuy nhiên, tôi sử dụng Newtonsoft.Json.JsonConvert nhưng nó giống nhau - Pierre
Cách tiếp cận JSON là Ok đối với các đối tượng phẳng nhỏ, nhưng câu hỏi ban đầu là về bất kỳ đối tượng nào, kể cả đối tượng sâu. Trình serializer NFX.Slim làm việc với các đơn vị nhanh hơn trên bất kỳ loại .NET nào miễn là nó không có các đại biểu và các con trỏ không được quản lý, đây là nguồn hoạt động rất giống BinaryFormnatter nhanh hơn ít nhất 5 lần: github.com/aumcode/nfx/blob/master/Source/NFX/Serialization/…    Bộ thử nghiệm: github.com/aumcode/serbench  chứng minh rằng serialier duy nhất nhanh hơn là Protobuf thiếu tính năng động và tham chiếu kiểu - itadapter DKh
Đối với điều này để làm việc đối tượng để sao chép cần phải được serializable như đã đề cập - điều này cũng có nghĩa là ví dụ rằng nó có thể không có phụ thuộc vòng tròn - radomeit


Lý do không sử dụng ICloneable Là không phải bởi vì nó không có giao diện chung. Lý do không sử dụng nó là bởi vì nó mơ hồ. Nó không làm rõ cho dù bạn đang nhận được một bản sao nông hoặc sâu; đó là tùy thuộc vào người thực hiện.

Vâng, MemberwiseClone tạo một bản sao nông, nhưng ngược lại MemberwiseClone không phải Clone; nó sẽ là, có lẽ, DeepClone, không tồn tại. Khi bạn sử dụng một đối tượng thông qua giao diện ICloneable của nó, bạn không thể biết loại nhân bản nào mà đối tượng bên dưới thực hiện. (Và các chú thích XML sẽ không làm cho nó rõ ràng, bởi vì bạn sẽ nhận được các chú thích giao diện hơn là các chú thích trên phương thức Clone của đối tượng.)

Những gì tôi thường làm chỉ đơn giản là làm một Copy phương pháp thực hiện chính xác những gì tôi muốn.


147
2017-09-26 20:18



Tôi không rõ tại sao ICloneable được coi là mơ hồ. Với một kiểu như Dictionary (Of T, U), tôi mong rằng ICloneable.Clone nên làm bất cứ mức độ sao chép sâu và nông nào là cần thiết để làm cho từ điển mới trở thành một từ điển độc lập có cùng T và U (struct contents, và / hoặc tham chiếu đối tượng) như là bản gốc. Sự mơ hồ ở đâu? Để chắc chắn, một ICloneable chung (Of ​​T), được thừa hưởng ISelf (Of T), bao gồm một phương pháp "Tự", sẽ tốt hơn nhiều, nhưng tôi không thấy sự mơ hồ về nhân bản sâu so với nông. - supercat
Ví dụ của bạn minh họa vấn đề. Giả sử bạn có từ điển <string, Customer>. Nếu từ điển nhân bản có tương tự Đối tượng khách hàng là bản gốc hoặc bản sao của những đối tượng khách hàng đó? Có một số trường hợp sử dụng hợp lý cho một trong hai trường hợp. Nhưng ICloneable không làm rõ bạn sẽ nhận được cái nào. Đó là lý do tại sao nó không hữu ích. - Ryan Lundy
@Kyralessa Bài viết MSDN của Microsoft thực sự nêu rõ vấn đề này là không biết liệu bạn có yêu cầu bản sao sâu hay nông. - crush
Câu trả lời từ bản sao stackoverflow.com/questions/129389/… mô tả phần mở rộng Copy, dựa trên MembershipClone đệ quy - Michael Freidgeim


Sau nhiều lần đọc về nhiều tùy chọn được liên kết ở đây và các giải pháp khả thi cho vấn đề này, tôi tin rằng tất cả các tùy chọn được tóm tắt khá tốt tại Ian Pliên kết của (tất cả các tùy chọn khác là các biến thể của những tùy chọn này) và giải pháp tốt nhất được cung cấp bởi Pedro77liên kết của trên các bình luận câu hỏi.

Vì vậy, tôi sẽ chỉ sao chép các phần liên quan của 2 tài liệu tham khảo ở đây. Bằng cách đó chúng ta có thể có:

Điều tốt nhất để làm cho nhân bản các đối tượng trong c sắc nét!

Đầu tiên và quan trọng nhất, đó là tất cả các tùy chọn của chúng tôi:

Các bài viết Sao chép sâu nhanh bằng cây biểu thức   cũng đã so sánh hiệu suất của nhân bản bằng cách nối tiếp, Reflection và Expression Trees.

Tại sao tôi chọn ICloneable (ví dụ: theo cách thủ công)

Ông Venkat Subramaniam (liên kết dư thừa ở đây) giải thích chi tiết tại sao.

Tất cả bài viết của anh ấy xoay quanh một ví dụ cố gắng áp dụng cho hầu hết các trường hợp, sử dụng 3 đối tượng: Người, Óc và Thành phố. Chúng tôi muốn sao chép một người, mà sẽ có bộ não của riêng mình nhưng cùng một thành phố. Bạn có thể hình ảnh tất cả các vấn đề bất kỳ của các phương pháp khác ở trên có thể mang lại hoặc đọc bài viết.

Đây là phiên bản được sửa đổi một chút của kết luận của anh ấy:

Sao chép một đối tượng bằng cách chỉ định New theo sau là tên lớp thường dẫn đến mã không mở rộng được. Sử dụng bản sao, ứng dụng của mẫu thử nghiệm, là một cách tốt hơn để đạt được điều này. Tuy nhiên, việc sử dụng bản sao vì nó được cung cấp trong C # (và Java) cũng có thể khá rắc rối. Tốt hơn là cung cấp một hàm tạo bản sao được bảo vệ (không công khai) và gọi nó từ phương thức sao chép. Điều này cho chúng ta khả năng ủy nhiệm nhiệm vụ tạo một đối tượng cho một cá thể của một lớp, do đó cung cấp khả năng mở rộng và cũng có thể tạo ra một cách an toàn các đối tượng bằng cách sử dụng hàm tạo bản sao được bảo vệ.

Hy vọng rằng việc triển khai này có thể làm cho mọi thứ rõ ràng:

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

Bây giờ hãy xem xét việc có một lớp xuất phát từ Person.

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

Bạn có thể thử chạy đoạn mã sau:

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

Đầu ra được tạo ra sẽ là:

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

Quan sát rằng, nếu chúng ta giữ một số lượng các đối tượng, bản sao được thực hiện ở đây sẽ giữ một số lượng chính xác về số đối tượng.


84
2017-09-17 00:13



MS khuyến cáo không sử dụng ICloneable cho các thành viên công cộng. "Bởi vì người gọi Clone không thể phụ thuộc vào phương pháp thực hiện một hoạt động nhân bản có thể dự đoán được, chúng tôi khuyên rằng ICloneable không được triển khai trong các API công cộng." msdn.microsoft.com/en-us/library/… Tuy nhiên, dựa trên lời giải thích của Venkat Subramaniam trong bài viết liên kết của bạn, tôi nghĩ rằng nó có ý nghĩa để sử dụng trong tình huống này miễn là những người sáng tạo của các đối tượng ICloneable có một sự hiểu biết sâu sắc về tài sản nào nên sâu hơn so với các bản sao nông (ví dụ: Brain copy sâu, thành phố copy nông) - BateTech
Trước hết, tôi ở xa một chuyên gia trong chủ đề này (API công cộng). tôi suy nghĩ một lần mà nhận xét MS làm cho rất nhiều ý nghĩa. Và tôi không nghĩ rằng nó an toàn để giả định người dùng của API đó sẽ có một sự hiểu biết sâu sắc như vậy. Vì vậy, nó chỉ có ý nghĩa thực hiện nó trên một API công khainếu nó thực sự sẽ không quan trọng cho bất cứ ai sẽ sử dụng nó. tôi phỏng đoán có một số loại UML rất rõ ràng làm cho sự khác biệt trên mỗi tài sản có thể giúp đỡ. Nhưng tôi muốn nghe từ một người có nhiều kinh nghiệm hơn. : P - cregox
Bạn có thể dùng Máy phát điện Clone CGbR và nhận được kết quả tương tự mà không cần viết mã theo cách thủ công. - Toxantron
Triển khai ngôn ngữ trung gian hữu ích - Michael Freidgeim


Tôi thích một nhà xây dựng bản sao cho một bản sao. Mục đích rõ ràng hơn.


69
2018-03-16 11:38



.Net không có các hàm tạo bản sao. - Pop Catalin
Chắc chắn nó làm: mới MyObject (objToCloneFrom) Chỉ cần khai báo một ctor mà có đối tượng để nhân bản như một tham số. - Nick
Nó không giống nhau. Bạn phải thêm nó vào mọi lớp theo cách thủ công và thậm chí bạn không biết mình có đang che giấu một bản sao sâu hay không. - Dave Van den Eynde
1 cho ctor sao chép. Bạn phải tự viết một hàm clone () cho từng loại đối tượng, và chúc may mắn với điều đó khi phân cấp lớp của bạn nhận được một vài cấp độ sâu. - Andrew Grant
Với các nhà xây dựng sao chép, bạn mất hệ thống phân cấp mặc dù. agiledeveloper.com/articles/cloning072002.htm - Will


Phương pháp mở rộng đơn giản để sao chép tất cả các thuộc tính công khai. Hoạt động cho mọi đối tượng và không làm yêu cầu lớp học [Serializable]. Có thể được mở rộng cho cấp truy cập khác.

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



Điều này, thật không may, là thiếu sót. Nó tương đương với việc gọi objectOne.MyProperty = objectTwo.MyProperty (tức là, nó sẽ chỉ sao chép tham chiếu trên). Nó sẽ không sao chép các giá trị của các thuộc tính. - Alex Norcliffe
Alex Norcliffe: tác giả của câu hỏi được hỏi về "sao chép từng tài sản" thay vì nhân bản. trong hầu hết các trường hợp, không cần phải sao chép chính xác các thuộc tính. - Konstantin Salavatov
tôi nghĩ về cách sử dụng phương pháp này nhưng với đệ quy. vì vậy nếu giá trị của một thuộc tính là một tham chiếu, hãy tạo một đối tượng mới và gọi CopyTo một lần nữa. tôi chỉ thấy một vấn đề, rằng tất cả các lớp được sử dụng phải có một hàm tạo không có tham số. Ai đã thử cái này chưa? tôi cũng tự hỏi, nếu điều này thực sự sẽ làm việc với các thuộc tính có chứa các lớp .net như DataRow và DataTable? - Koryu


Vâng, tôi đã gặp vấn đề khi sử dụng ICloneable trong Silverlight, nhưng tôi thích ý tưởng của việc seralization, tôi có thể seralize XML, vì vậy tôi đã làm điều này:

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





Nếu bạn đã sử dụng ứng dụng của bên thứ 3 như ValueInjecter hoặc là Automapper, bạn có thể làm một cái gì đó như thế này:

MyObject oldObj; // The existing object to clone

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

Sử dụng phương pháp này bạn không phải thực hiện ISerializable hoặc ICloneable trên các đối tượng của bạn. Điều này là phổ biến với mô hình MVC / MVVM, vì vậy các công cụ đơn giản như thế này đã được tạo ra.

xem giải pháp nhân bản giá trị sâu sắc trên CodePlex.


26
2017-12-24 22:56





Tôi vừa tạo CloneExtensions thư viện dự án. Nó thực hiện nhanh chóng, bản sao sâu bằng cách sử dụng các phép gán đơn giản được tạo ra bởi trình biên dịch mã thời gian chạy của Expression Tree.

Làm thế nào để sử dụng nó?

Thay vì viết của riêng bạn Clone hoặc là Copy phương pháp với một giai điệu của các bài tập giữa các lĩnh vực và tài sản làm cho chương trình làm điều đó cho chính mình, bằng cách sử dụng Expression Tree. GetClone<T>() phương thức được đánh dấu là phương thức mở rộng cho phép bạn chỉ cần gọi nó trên cá thể của bạn:

var newInstance = source.GetClone();

Bạn có thể chọn những gì cần được sao chép từ source đến newInstance sử dụng CloningFlags enum:

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

Những gì có thể được nhân bản?

  • Nguyên thủy (int, uint, byte, double, char, vv), được biết đến không thay đổi các loại (DateTime, TimeSpan, String) và các đại biểu (bao gồm Hành động, Func, v.v.)
  • Nullable
  • T [] mảng
  • Các lớp và cấu trúc tùy chỉnh, bao gồm các lớp và cấu trúc chung.

Sau lớp / cấu trúc thành viên được nhân bản nội bộ:

  • Giá trị của các trường công khai, không chỉ đọc
  • Giá trị của các thuộc tính công khai với cả hai bộ nhận và đặt
  • Các mục bộ sưu tập cho các loại triển khai ICollection

Nhanh như thế nào?

Giải pháp nhanh hơn sau đó phản ánh, bởi vì thông tin thành viên phải được thu thập chỉ một lần, trước khi GetClone<T> được sử dụng lần đầu tiên cho loại đã cho T.

Nó cũng nhanh hơn so với giải pháp dựa trên serialization khi bạn sao chép nhiều hơn sau đó vài trường hợp cùng loại T.

và hơn thế nữa...

Đọc thêm về các biểu thức được tạo trên tài liệu.

Danh sách gỡ lỗi biểu thức mẫu cho List<int>:

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

}

những gì có cùng ý nghĩa như sau c # code:

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

Nó không hoàn toàn giống như cách bạn tự viết Clone phương pháp cho List<int>?


25
2017-09-17 00:14



Cơ hội nhận được điều này trên NuGet là gì? Nó có vẻ như là giải pháp tốt nhất. So sánh với NClone? - crush
Tôi nghĩ câu trả lời này nên được upvoted nhiều lần hơn. Việc thực thi ICloneable theo cách thủ công là tẻ nhạt và dễ bị lỗi, việc sử dụng sự phản chiếu hoặc tuần tự hóa sẽ chậm nếu hiệu suất là quan trọng và bạn cần sao chép hàng nghìn đối tượng trong một khoảng thời gian ngắn. - nightcoder
Không phải ở tất cả, bạn sai về sự phản chiếu, bạn nên chỉ đơn giản là bộ nhớ cache này đúng cách. Kiểm tra câu trả lời của tôi bên dưới stackoverflow.com/a/34368738/4711853 - Roma Borodov


Câu trả lời ngắn gọn là bạn kế thừa từ giao diện ICloneable và sau đó thực hiện chức năng .clone. Sao chép phải sao chép thành viên và thực hiện một bản sao sâu trên bất kỳ thành viên nào yêu cầu nó, sau đó trả về đối tượng kết quả. Đây là thao tác đệ quy (yêu cầu tất cả các thành viên của lớp bạn muốn sao chép là một trong hai loại giá trị hoặc thực thi ICloneable và thành viên của chúng là một trong hai loại giá trị hoặc thực thi ICloneable, vv).

Để có giải thích chi tiết hơn về Nhân bản sử dụng ICloneable, hãy xem bài viết này.

Các Dài câu trả lời là "nó phụ thuộc". Như đã đề cập bởi những người khác, ICloneable không được hỗ trợ bởi generics, đòi hỏi phải cân nhắc đặc biệt cho tài liệu tham khảo lớp tròn, và thực sự được xem bởi một số như là một "sai lầm" trong Khuôn khổ .NET. Phương thức tuần tự phụ thuộc vào các đối tượng của bạn đang được tuần tự hóa, mà chúng có thể không được và bạn có thể không có quyền kiểm soát. Vẫn còn nhiều tranh luận trong cộng đồng về việc thực hành "tốt nhất". Trong thực tế, không có giải pháp nào là một kích thước phù hợp với tất cả thực hành tốt nhất cho tất cả các tình huống như ICloneable ban đầu được hiểu là.

Xem cái này Bài viết góc của nhà phát triển cho một vài tùy chọn khác (tín dụng cho Ian).


20
2018-02-16 11:30



ICloneable không có giao diện chung, vì vậy không nên sử dụng giao diện đó. - Karg
Giải pháp của bạn hoạt động cho đến khi nó cần xử lý các tham chiếu vòng tròn, sau đó mọi thứ bắt đầu phức tạp, tốt hơn là hãy thử triển khai nhân bản sâu bằng cách sử dụng tuần tự hóa sâu. - Pop Catalin
Thật không may, không phải tất cả các đối tượng đều có thể tuần tự hóa được, vì vậy bạn không thể luôn sử dụng phương thức đó. Liên kết của Ian là câu trả lời toàn diện nhất cho đến nay. - Zach Burlingame
1 để đề cập đến bài viết của Brad Abrams. - Merlyn Morgan-Graham


Nếu bạn muốn nhân bản thực sự với các loại không xác định, bạn có thể xem fastclone.

Đó là biểu thức nhân bản dựa trên làm việc nhanh hơn gấp 10 lần so với tuần tự nhị phân và duy trì tính toàn vẹn đồ thị đối tượng hoàn chỉnh.

Điều đó có nghĩa là: nếu bạn giới thiệu nhiều lần đến cùng một đối tượng trong hierachy của bạn, bản sao cũng sẽ có một cá thể dụ duy nhất được tham chiếu.

Không cần giao diện, thuộc tính hoặc bất kỳ sửa đổi nào khác đối với các đối tượng đang được nhân bản.


15
2017-08-03 22:24



Điều này có vẻ khá hữu ích - LuckyLikey
Sẽ dễ dàng hơn khi bắt đầu làm việc từ một mã chụp nhanh hơn cho toàn bộ hệ thống, đặc biệt là hệ thống đóng. Khá dễ hiểu là không thư viện nào có thể giải quyết tất cả các vấn đề với một cảnh quay. Một số thư giãn nên được thực hiện. - TarmoPikaro
Tôi đã thử giải pháp của bạn và nó có vẻ hoạt động tốt, cảm ơn! Tôi nghĩ câu trả lời này nên được upvoted nhiều lần hơn. Việc thực thi ICloneable theo cách thủ công là tẻ nhạt và dễ bị lỗi, việc sử dụng sự phản chiếu hoặc tuần tự hóa sẽ chậm nếu hiệu suất là quan trọng và bạn cần sao chép hàng nghìn đối tượng trong một khoảng thời gian ngắn. - nightcoder