문제

나는 다음과 같은 일을 하고 싶다:

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

그런 다음 원래 개체에 반영되지 않은 새 개체를 변경합니다.

나는 이 기능이 자주 필요하지 않기 때문에 필요할 때 새 개체를 만든 다음 각 속성을 개별적으로 복사했지만 항상 더 좋고 더 우아한 처리 방법이 있다는 느낌을 받았습니다. 그 상황.

원본 개체에 변경 사항이 반영되지 않고 복제된 개체를 수정할 수 있도록 개체를 복제하거나 전체 복사하려면 어떻게 해야 합니까?

도움이 되었습니까?

해결책

표준 관행은 다음을 구현하는 것입니다. ICloneable 인터페이스(설명 여기, 그래서 역류하지 않겠습니다) 여기 제가 찾은 멋진 딥 클론 개체 복사기가 있습니다. 코드 프로젝트 얼마 전에 그것을 우리 물건에 통합했습니다.

다른 곳에서 언급했듯이 개체를 직렬화할 수 있어야 합니다.

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

아이디어는 개체를 직렬화한 다음 이를 새로운 개체로 역직렬화한다는 것입니다.이점은 객체가 너무 복잡해지면 모든 것을 복제하는 것에 대해 걱정할 필요가 없다는 것입니다.

그리고 확장 메서드를 사용하면(원래 참조된 소스에서도):

새로운 것을 사용하고 싶다면 확장 방법 C# 3.0에서는 다음 서명을 갖도록 메서드를 변경합니다.

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

이제 메소드 호출은 단순히 objectBeingCloned.Clone();.

편집하다 (2015년 1월 10일) 이것을 다시 방문할 것이라고 생각했는데, 최근에 이 작업을 위해 (Newtonsoft) Json을 사용하기 시작했습니다. 해야한다 더 가볍고 [Serialize] 태그의 오버헤드를 방지합니다.(주의 @atconway는 비공개 멤버가 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);
}

다른 팁

나는 주로 기본 요소와 목록으로 구성된 매우 간단한 개체에 대한 복제기를 원했습니다.객체가 JSON 직렬화 가능한 기본 객체인 경우 이 방법이 트릭을 수행합니다.이를 위해서는 복제된 클래스의 인터페이스를 수정하거나 구현할 필요가 없으며 JSON.NET과 같은 JSON 직렬 변환기만 있으면 됩니다.

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

또한 이 확장 방법을 사용할 수 있습니다.

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

사용하지 않는 이유 ICloneable ~이다 ~ 아니다 일반적인 인터페이스가 없기 때문입니다. 사용하지 않는 이유는 모호하기 때문입니다..얕은 복사본을 얻는지 깊은 복사본을 얻는지는 명확하지 않습니다.그것은 구현자에게 달려 있습니다.

예, MemberwiseClone 얕은 복사본을 만들지 만 그 반대입니다. MemberwiseClone 그렇지 않다 Clone;아마도 그럴 것이다. DeepClone, 존재하지 않습니다.ICloneable 인터페이스를 통해 객체를 사용하는 경우 기본 객체가 어떤 종류의 복제를 수행하는지 알 수 없습니다.(객체의 Clone 메서드에 대한 주석이 아닌 인터페이스 주석을 받게 되므로 XML 주석에서는 명확하지 않습니다.)

내가 보통 하는 일은 단순히 Copy 내가 원하는 것을 정확히 수행하는 방법.

여기에 링크된 많은 옵션과 이 문제에 대한 가능한 해결책에 대해 많이 읽은 후 모든 옵션이 꽤 잘 요약되어 있습니다. 이안 P님의 링크 (다른 모든 옵션은 해당 옵션의 변형임) 최상의 솔루션은 다음에서 제공됩니다. 페드로77님의 링크 질문 댓글에.

따라서 여기에 2개의 참고문헌 중 관련 부분을 복사하겠습니다.그렇게 하면 다음과 같은 결과를 얻을 수 있습니다.

C Sharp에서 객체를 복제하는 가장 좋은 방법은!

무엇보다도 우리가 선택할 수 있는 옵션은 다음과 같습니다.

그만큼 기사 표현식 트리를 통한 빠른 전체 복사 또한 Serialization, Reflection 및 Expression Tree를 통한 복제 성능 비교도 제공합니다.

내가 선택하는 이유 ICloneable (즉.수동으로)

Mr Venkat Subramaniam(여기에 중복 링크)이 그 이유를 자세히 설명합니다..

그의 모든 기사는 3가지 개체를 사용하여 대부분의 경우에 적용할 수 있는 예제를 중심으로 진행됩니다. 사람, 그리고 도시.우리는 자신의 뇌를 갖고 있지만 도시는 같은 사람을 복제하고 싶습니다.위의 다른 방법으로 발생할 수 있는 모든 문제를 상상해 보거나 기사를 읽을 수 있습니다.

이것은 그의 결론을 약간 수정한 것입니다.

지정하여 객체 복사 New 클래스 이름이 뒤에 오면 확장할 수 없는 코드가 발생하는 경우가 많습니다.프로토타입 패턴을 적용한 클론을 사용하는 것이 이를 달성하는 더 좋은 방법입니다.그러나 C#(및 Java)에서 제공되는 복제본을 사용하는 것도 상당히 문제가 될 수 있습니다.보호된(비공개) 복사 생성자를 제공하고 이를 복제 메서드에서 호출하는 것이 더 좋습니다.이를 통해 객체 생성 작업을 클래스 자체의 인스턴스에 위임할 수 있으므로 확장성이 제공되고 보호된 복사 생성자를 사용하여 객체를 안전하게 생성할 수 있습니다.

이 구현을 통해 상황이 명확해지기를 바랍니다.

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

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

다음 코드를 실행해 보세요:

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

생성된 출력은 다음과 같습니다.

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

객체 수를 유지하면 여기에 구현된 복제본이 객체 수의 정확한 수를 유지한다는 점에 유의하세요.

나는 복제본보다 복사 생성자를 선호합니다.의도가 더 명확합니다.

모든 공용 속성을 복사하는 간단한 확장 방법입니다.모든 개체에 대해 작동하며 하지 않습니다 수업이 필요하다 [Serializable].다른 액세스 수준으로 확장될 수 있습니다.

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

Silverlight에서 ICloneable을 사용하는 데 문제가 있었지만 직렬화 아이디어가 마음에 들었고 XML을 직렬화할 수 있었기 때문에 다음과 같이 했습니다.

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

방금 만들었어요 CloneExtensions 도서관 프로젝트.표현식 트리 런타임 코드 컴파일을 통해 생성된 간단한 할당 작업을 사용하여 빠르고 심층적인 복제를 수행합니다.

사용 방법?

직접 작성하는 대신 Clone 또는 Copy 필드와 속성 간의 할당 톤이 있는 메서드를 사용하면 프로그램에서 표현식 트리를 사용하여 자동으로 할당할 수 있습니다. GetClone<T>() 확장 메서드로 표시된 메서드를 사용하면 인스턴스에서 간단히 호출할 수 있습니다.

var newInstance = source.GetClone();

복사할 항목을 선택할 수 있습니다. source 에게 newInstance 사용하여 CloningFlags 열거형:

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

복제할 수 있는 항목은 무엇입니까?

  • Primitive (int, uint, byte, double, char 등), 알려진 불변 유형 (datetime, times, string) 및 대의원 (행동, 기능 포함)
  • 널 입력 가능
  • T[] 배열
  • 일반 클래스 및 구조체를 포함한 사용자 정의 클래스 및 구조체.

다음 클래스/구조체 멤버는 내부적으로 복제됩니다.

  • 읽기 전용 필드가 아닌 공개 값
  • get 및 set 접근자가 모두 포함된 공용 속성 값
  • ICollection을 구현하는 형식의 컬렉션 항목

얼마나 빠른가요?

회원 정보는 한 번만 수집하면 되기 때문에 반영보다 해결이 빠릅니다. GetClone<T> 특정 유형에 대해 처음으로 사용되었습니다. T.

또한 동일한 유형의 인스턴스를 두 개 이상 복제하는 경우 직렬화 기반 솔루션보다 빠릅니다. T.

그리고 더...

생성된 표현식에 대해 자세히 알아보세요. 선적 서류 비치.

샘플 표현식 디버그 목록 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:
}

}

다음 C# 코드와 동일한 의미는 무엇입니까?

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

당신이 직접 쓰는 것과 아주 비슷하지 않나요? Clone 방법 List<int>?

이미 다음과 같은 타사 애플리케이션을 사용하고 있는 경우 가치주입기 또는 자동매퍼, 다음과 같이 할 수 있습니다:

MyObject oldObj; // The existing object to clone

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

이 방법을 사용하면 개체에 ISerialized 또는 ICloneable을 구현할 필요가 없습니다.이는 MVC/MVVM 패턴에서 일반적이므로 이와 같은 간단한 도구가 만들어졌습니다.

보다 CodePlex의 valueinjecter 딥 클로닝 솔루션.

짧은 대답은 ICloneable 인터페이스에서 상속한 다음 .clone 기능을 구현하는 것입니다.클론은 멤버별 복사를 수행하고 이를 필요로 하는 모든 멤버에 대해 전체 복사를 수행한 다음 결과 개체를 반환해야 합니다.이는 재귀 작업입니다. 복제하려는 클래스의 모든 멤버가 값 유형이거나 ICloneable을 구현해야 하고 해당 멤버가 값 유형이거나 ICloneable을 구현해야 하는 등의 작업이 필요합니다.

ICloneable을 사용한 복제에 대한 자세한 설명은 다음을 확인하세요. 이 기사.

그만큼 대답은 "상황에 따라 다르다"이다.다른 사람들이 언급했듯이 ICloneable은 제네릭에서 지원되지 않으며 순환 클래스 참조에 대한 특별한 고려 사항이 필요하며 실제로 일부에서는 "실수" .NET Framework에서.직렬화 방법은 직렬화 가능한 개체에 따라 달라지며 직렬화되지 않을 수도 있고 제어할 수 없을 수도 있습니다.커뮤니티에서는 어떤 것이 "최상의" 관행인지에 대해 여전히 많은 논쟁이 있습니다.실제로 ICloneable과 같은 모든 상황에 대해 모든 모범 사례에 맞는 단일 크기의 솔루션은 없습니다.

이것을 보아라 개발자 코너 기사 몇 가지 추가 옵션이 있습니다(Ian에게 제공).

가장 좋은 방법은 다음을 구현하는 것입니다. 확장 방법 좋다

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

그런 다음 솔루션의 어느 곳에서나 사용하십시오.

var copy = anyObject.DeepClone();

우리는 다음 세 가지 구현을 가질 수 있습니다:

  1. 직렬화별 (가장 짧은 코드)
  2. 반사로 - 5배 더 빨라짐
  3. 표현식 트리별 - 20배 더 빨라짐

연결된 모든 방법은 잘 작동하며 심층적인 테스트를 거쳤습니다.

  1. 기본적으로 ICloneable 인터페이스를 구현한 다음 객체 구조 복사를 실현해야 합니다.
  2. 모든 구성원의 전체 복사본인 경우 모든 하위 항목도 복제할 수 있는지 확인해야 합니다(선택한 솔루션과 관련 없음).
  3. 때로는 이 프로세스 중에 몇 가지 제한 사항을 인식해야 합니다. 예를 들어 ORM 개체를 복사하는 경우 대부분의 프레임워크는 세션에 연결된 개체를 하나만 허용하고 이 개체의 복제를 만들어서는 안 됩니다. 또는 가능하다면 주의해야 합니다. 이러한 개체의 세션 연결에 대해 설명합니다.

건배.

알 수 없는 유형에 대한 진정한 복제를 원한다면 다음을 살펴보세요.빠른 클론.

이는 바이너리 직렬화보다 약 10배 빠르게 작동하고 완전한 개체 그래프 무결성을 유지하는 표현식 기반 복제입니다.

그 의미는:계층 구조에서 동일한 개체를 여러 번 참조하는 경우 복제본에도 참조되는 단일 인스턴스가 있습니다.

복제되는 객체에 대한 인터페이스, 속성 또는 기타 수정이 필요하지 않습니다.

간단하게 유지하고 사용하세요 자동매퍼 다른 사람들이 언급했듯이 한 개체를 다른 개체에 매핑하는 간단한 작은 라이브러리입니다.동일한 유형의 다른 개체에 개체를 복사하려면 다음 세 줄의 코드만 있으면 됩니다.

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

이제 대상 개체는 소스 개체의 복사본입니다.충분히 간단하지 않습니까?솔루션의 모든 곳에서 사용할 확장 메서드를 만듭니다.

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

확장 방법을 사용하면 세 줄이 한 줄이 됩니다.

MyType copy = source.Copy();

나는 이것을 극복하기 위해 이것을 생각해 냈습니다. .그물 List<T>를 수동으로 전체 복사해야 하는 단점이 있습니다.

나는 이것을 사용합니다 :

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

그리고 다른 곳에서는:

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

나는 이 작업을 수행하는 oneliner를 만들려고 노력했지만 익명 메소드 블록 내부에서 작동하지 않는 Yield로 인해 불가능합니다.

더 나은 방법은 일반 List<T> 클로너를 사용하는 것입니다.

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

큐.내가 왜 이 답변을 선택하겠는가?

  • .NET이 제공할 수 있는 가장 빠른 속도를 원한다면 이 답변을 선택하세요.
  • 정말, 정말 쉬운 복제 방법을 원한다면 이 답변을 무시하세요.

다시 말해서, 수정이 필요한 성능 병목 현상이 있고 프로파일러를 사용하여 이를 증명할 수 없다면 다른 답변을 선택하세요..

다른 방법보다 10배 빠릅니다.

딥 클론을 수행하는 방법은 다음과 같습니다.

  • 직렬화/역직렬화와 관련된 모든 것보다 10배 빠릅니다.
  • .NET이 가능한 이론적 최대 속도에 매우 가깝습니다.

그리고 방법은 ...

최고의 속도를 위해 다음을 사용할 수 있습니다. 전체 복사를 수행하기 위한 중첩된 MemberwiseClone.값 구조체를 복사하는 것과 거의 동일한 속도이며 (a) 반사 또는 (b) 직렬화(이 페이지의 다른 답변에 설명된 대로)보다 훨씬 빠릅니다.

참고하세요 만약에 너는 사용한다 전체 복사를 위한 중첩된 MemberwiseClone, 클래스의 각 중첩 수준에 대해 ShallowCopy를 수동으로 구현해야 하며, 전체 복제본을 생성하기 위해 언급된 모든 ShallowCopy 메서드를 호출하는 DeepCopy를 구현해야 합니다.이것은 간단합니다:총 몇 줄만 있습니다. 아래 데모 코드를 참조하세요.

다음은 100,000개의 클론에 대한 상대적 성능 차이를 보여주는 코드의 출력입니다.

  • 중첩된 구조체의 중첩된 MemberwiseClone에 대해 1.08초
  • 중첩 클래스의 중첩 MemberwiseClone에 대해 4.77초
  • 직렬화/역직렬화에 39.93초

클래스에서 Nested MemberwiseClone을 사용하는 것은 구조체를 복사하는 것만큼 빠르며, 구조체를 복사하는 것은 .NET이 가능한 이론적 최대 속도에 매우 가깝습니다.

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를 사용하여 전체 복사를 수행하는 방법을 이해하기 위해 위의 시간을 생성하는 데 사용된 데모 프로젝트는 다음과 같습니다.

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

그런 다음 기본에서 데모를 호출합니다.

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

다시 한번 말씀드리지만 만약에 너는 사용한다 전체 복사를 위한 중첩된 MemberwiseClone, 클래스의 각 중첩 수준에 대해 ShallowCopy를 수동으로 구현해야 하며, 전체 복제본을 생성하기 위해 언급된 모든 ShallowCopy 메서드를 호출하는 DeepCopy를 구현해야 합니다.이것은 간단합니다:총 몇 줄만 있습니다. 위의 데모 코드를 참조하세요.

값 유형과참고문헌 유형

객체를 복제할 때 "구조체" 그리고 "수업":

  • "구조체", 그건 값 유형 따라서 복사만 하면 내용이 복제됩니다(그러나 이 게시물의 기술을 사용하지 않는 한 얕은 복제만 생성됩니다).
  • "수업", 그건 참조 유형, 따라서 복사하면 포인터를 복사하는 것뿐입니다.진정한 클론을 만들려면 더 창의적이어야 하며 다음을 사용해야 합니다. 값 유형과 참조 유형의 차이점 이는 메모리에 원본 객체의 또 다른 복사본을 생성합니다.

보다 값 유형과 참조 유형의 차이점.

디버깅에 도움이 되는 체크섬

  • 개체를 잘못 복제하면 핀다운하기 매우 어려운 버그가 발생할 수 있습니다.프로덕션 코드에서는 객체가 제대로 복제되었는지, 객체에 대한 다른 참조로 인해 손상되지 않았는지 다시 확인하기 위해 체크섬을 구현하는 경향이 있습니다.이 체크섬은 릴리스 모드에서 끌 수 있습니다.
  • 나는 이 방법이 매우 유용하다고 생각합니다.전체가 아닌 객체의 일부만 복제하려는 경우가 많습니다.

많은 스레드를 다른 많은 스레드로부터 분리하는 데 매우 유용합니다.

이 코드의 훌륭한 사용 사례 중 하나는 중첩된 클래스 또는 구조체의 복제본을 대기열에 공급하여 생산자/소비자 패턴을 구현하는 것입니다.

  • 우리는 자신이 소유한 클래스를 수정하는 하나 이상의 스레드를 가질 수 있으며, 그런 다음 이 클래스의 전체 복사본을 ConcurrentQueue.
  • 그런 다음 이러한 클래스의 복사본을 가져와서 처리하는 하나 이상의 스레드가 있습니다.

이는 실제로 매우 잘 작동하며 여러 스레드(생산자)를 하나 이상의 스레드(소비자)에서 분리할 수 있게 해줍니다.

그리고 이 방법은 너무 빠릅니다:중첩된 구조체를 사용하면 중첩된 클래스를 직렬화/역직렬화하는 것보다 35배 빠르며 시스템에서 사용 가능한 모든 스레드를 활용할 수 있습니다.

업데이트

분명히 ExpressMapper는 위와 같은 수동 코딩보다 빠르지는 않더라도 빠릅니다.프로파일러와 어떻게 비교되는지 확인해야 할 수도 있습니다.

일반적으로 ICloneable 인터페이스를 구현하고 Clone을 직접 구현합니다.C# 개체에는 모든 기본 요소에 대해 도움이 될 수 있는 얕은 복사본을 수행하는 내장 MemberwiseClone 메서드가 있습니다.

전체 복사의 경우 자동으로 수행하는 방법을 알 수 있는 방법이 없습니다.

리플렉션을 통해서도 구현되는 것을 보았습니다.기본적으로 객체의 멤버를 반복하고 이를 새 객체에 적절하게 복사하는 메서드가 있었습니다.참조 유형이나 컬렉션에 도달하면 자체적으로 재귀 호출을 수행한 것 같습니다.Reflection은 비용이 많이 들지만 꽤 잘 작동했습니다.

다음은 전체 복사 구현입니다.

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

다양한 프로젝트에서 내 요구 사항을 모두 충족하는 클로너를 찾을 수 없었기 때문에 클로너 요구 사항을 충족하도록 코드를 조정하는 대신 다양한 코드 구조에 맞게 구성하고 조정할 수 있는 딥 클로너를 만들었습니다.복제할 코드에 주석을 추가하거나 코드를 그대로 두어 기본 동작을 유지하면 됩니다.리플렉션, 유형 캐시를 사용하며 다음을 기반으로 합니다. 더 빠른 반사.복제 프로세스는 엄청난 양의 데이터와 높은 개체 계층 구조에 대해 매우 빠릅니다(다른 반사/직렬화 기반 알고리즘에 비해).

https://github.com/kalisohn/CloneBehave

너겟 패키지로도 사용 가능합니다.https://www.nuget.org/packages/Clone.Behave/1.0.0

예를 들어:다음 코드는 deepClone 주소를 지정하지만 _currentJob 필드의 단순 복사본만 수행합니다.

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

이 방법으로 문제가 해결되었습니다.

private static MyObj DeepCopy(MyObj source)
        {

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

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

        }

다음과 같이 사용하세요: MyObj a = DeepCopy(b);

나는 다음과 같은 Copyconstructors를 좋아합니다.

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

복사할 항목이 더 있으면 추가하세요.

코드 생성기

우리는 수동 구현을 통한 직렬화부터 반영까지 많은 아이디어를 보았으며 저는 다음을 사용하여 완전히 다른 접근 방식을 제안하고 싶습니다. CGbR 코드 생성기.복제 생성 방법은 메모리와 CPU 효율성이 높기 때문에 표준 DataContractSerializer보다 300배 더 빠릅니다.

필요한 것은 부분 클래스 정의뿐입니다. ICloneable 나머지는 생성기가 수행합니다.

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

메모: 최신 버전에는 더 많은 null 검사가 있지만 더 나은 이해를 위해 생략했습니다.

직렬화/역직렬화를 거치지 않고도 빠르고 쉽게 사용할 수 있는 솔루션이 있습니다.

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

편집하다:필요하다

    using System.Linq;
    using System.Reflection;

내가 사용한 방법이야

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

다음과 같이하세요:

  • 정의 ISelf<T> 읽기 전용으로 Self 반환하는 속성 T, 그리고 ICloneable<out T>, 이는 다음에서 파생됩니다. ISelf<T> 방법이 포함되어 있습니다. T Clone().
  • 그런 다음 CloneBase 구현하는 유형 protected virtual generic VirtualClone 주조 MemberwiseClone 전달된 유형에.
  • 각 파생 유형은 구현해야 합니다. VirtualClone 기본 복제 메소드를 호출한 다음 상위 VirtualClone 메소드가 아직 처리하지 않은 파생 유형의 측면을 올바르게 복제하기 위해 수행해야 하는 모든 작업을 수행합니다.

상속의 다양성을 극대화하려면 공개 복제 기능을 노출하는 클래스를 사용해야 합니다. sealed, 그러나 복제가 없다는 점을 제외하면 동일한 기본 클래스에서 파생됩니다.명시적인 복제 가능 유형의 변수를 전달하는 대신 유형의 매개변수를 사용하십시오. ICloneable<theNonCloneableType>.이렇게 하면 복제 가능한 파생물을 기대하는 루틴이 허용됩니다. Foo 복제 가능한 파생물을 사용하여 작업하려면 DerivedFoo, 을 생성할 뿐만 아니라 복제할 수 없는 파생물 생성도 허용합니다. Foo.

나는 이것을 시도해 볼 수 있다고 생각합니다.

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

'[Serialize]' 및 '[DataContract]' 모두에서 작동하는 허용된 답변 버전을 만들었습니다.작성한 지 꽤 시간이 지났지만, 제 기억이 맞다면 [DataContract]에는 다른 직렬 변환기가 필요했습니다.

필요하다 시스템, 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;
    }

} 

클래스 객체를 복제하려면 Object.MemberwiseClone 메서드를 사용할 수 있습니다.

이 함수를 클래스에 추가하세요.

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

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

그런 다음 깊은 독립 복사를 수행하려면 DeepCopy 메서드를 호출하면 됩니다.

yourClass newLine = oldLine.DeepCopy();

도움이 되었기를 바랍니다.

좋아, 이 게시물에는 리플렉션에 대한 몇 가지 명백한 예가 있지만 리플렉션은 적절하게 캐시하기 시작할 때까지 일반적으로 느립니다.

올바르게 캐시하면 1000000개 개체를 4,6초(Watcher로 측정)로 딥 클론합니다.

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

캐시된 속성을 가져오거나 사전에 새 속성을 추가하고 간단히 사용하는 것보다

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

다른 답변의 내 게시물에서 전체 코드 확인

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

객체 트리가 직렬화 가능한 경우 다음과 같은 것을 사용할 수도 있습니다.

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

이 솔루션은 매우 쉽지만 다른 솔루션만큼 성능이 좋지는 않습니다.

그리고 클래스가 커지더라도 해당 필드만 복제되고 직렬화됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top