[c#]깊은 복제 물체

IT / / 2022. 4. 15. 11:39
반응형

깊은 복제 물체


질문

 

나는 같은 것을하고 싶다 :

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 via serialization.
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>A deep copy of the object.</returns>
    public static T Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", nameof(source));
        }

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

        using var Stream stream = new MemoryStream();
        IFormatter formatter = new BinaryFormatter();
        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을 사용하기 위해, 그것은 가볍게되어야하며, [직렬화 가능] 태그의 오버 헤드를 피할 수있다.(NB @atconway는 개인 회원이 JSON 메소드를 사용하여 복제되지 않은 의견을 지적했습니다)

/// <summary>
/// Perform a deep Copy of the object, using Json as a serialization 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 (ReferenceEquals(source, null)) return default;

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


답변

나는 대부분의 프리미티브와 목록의 매우 간단한 개체를 위해 클로너를 원했습니다.객체가 Box 밖으로 나오면 JSON Serializable이 메서드는 트릭을 수행합니다.이는 JSON.NET과 같은 JSON Serializer (JSON.NET)의 JSON Serializer의 수정 또는 구현을 수정하거나 구현할 필요가 없습니다.

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


답변

여기에 연결된 많은 옵션에 대해 많이 읽고이 문제에 대한 해결책을 읽은 후 모든 옵션이 Ian P의 링크에서 꽤 잘 요약되어 있으며 Pedro77의 링크가 최상의 솔루션을 제공합니다.의견에 대한 질문에.

그래서 여기에 2 개의 참고 문헌의 관련 부분을 복사 할 것입니다.그런 식으로 우리는 다음을 가질 수 있습니다 :

C 날카로운 물체를 복제하기 위해하는 가장 좋은 일!

첫 번째와 가장 먼저 모든 옵션입니다.

iclonable을 사용하여 수동으로 얕아지고 유형 안전이 아닙니다. iCloneable을 사용하는 MemberwiseClone activator.createInstance 및 재귀 적 회원 론체를 사용하여 반사 johnc의 선호하는 답변이 가리키는 직렬화 중급 언어, 내가 어떻게 작동하지 않는지 전혀 모르겠다. Havard Straden의 사용자 정의 복제 프레임 워크와 같은 확장 방법 표현 나무

발현 나무에 의한 신속한 깊은 사본은 직렬화, 반사 및 발현 나무에 의한 복제의 성능 비교도 있습니다.

왜 내가 iCloneable을 선택하는 (즉, 수동으로)

Mr Venkat Subramaniam (Redundant Link)은 왜 자세히 설명합니다.

그의 모든 기사는 사람, 뇌 및 도시를 사용하여 대부분의 경우에 적용 할 예정인 예제 주위에 있습니다.우리는 자신의 두뇌를 가지고 있지만 같은 도시가 될 사람을 복제하고자합니다.위의 모든 문제가 기사를 가져 오거나 읽을 수있는 모든 문제를 그림으로 할 수 있습니다.

이것은 그의 결론의 약간 수정 된 버전입니다.

클래스 이름과 새 이름을 지정하여 객체를 복사하면 종종 확장 가능하지 않은 코드로 이어집니다.Clone을 사용하여 프로토 타입 패턴의 적용은이를 달성하는 더 좋은 방법입니다.그러나 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);
    }
    …
}

이제 수업이 사람으로부터 파생되는 것을 고려하십시오.

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

우리가 객체 수를 유지하면 여기에 구현 된 복제본이 객체 수의 올바른 수를 유지할 수 있습니다.



답변

DEEPCLONER : 신속하고 쉽고 효과적인 NUGET 패키지 복제를 해결할 수 있습니다.

모든 답변을 읽은 후에 나는이 우수한 패키지를 언급하지 않았습니다.

DeepCloner Github 프로젝트

DeepCloner Nuget 패키지

그것의 Readme에서 조금 정교 해, 여기서 우리가 직장에서 그것을 선택한 이유는 다음과 같습니다.

그것은 깊은 또는 얕은 사본을 수 있습니다 깊은 클로닝에서 모든 객체 그래프가 유지됩니다. 결과 복제가 뻔뻔스럽게 빠르므로 런타임에 코드 생성을 사용합니다. 내부 구조로 복사 된 물체, 호출 된 방법 또는 ctors가 없습니다. Serializable-Attribute 또는 구현 인터페이스와 같은 클래스를 어떻게 든 표시 할 필요가 없습니다. 복제를 위해 객체 유형을 지정하는 것이 필요하지 않습니다.객체는 인터페이스 또는 추상 객체로 캐스팅 할 수 있습니다 (예 : ints의 추상 배열 또는 iEnumerable로서의 int의 배열을 복제 할 수 있습니다. 아무도 오류없이 복제 할 수 있습니다) 복제 된 객체는 그가 복제하다는 것을 결정할 수있는 능력이 없습니다 (매우 구체적인 방법을 제외하고)

용법:

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

성능:

README에는 다양한 복제 라이브러리 및 메소드의 성능 비교가 포함됩니다. DEPHCLONER 성능.

요구 사항 :

.NET 4.0 이상 또는 .NET 표준 1.3 (.NET 코어) 전체 신뢰 권한 세트 또는 반영 허가가 필요합니다 (구성원)



답변

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

예, Memberwiseclone은 얕은 복사본을 만듭니다. 그러나 MemberwiseClone의 반대는 복제되지 않습니다.그것은 아마도, 딥 클론 (deepclone)이 존재하지 않을 것입니다.iClonable 인터페이스를 통해 객체를 사용하면 기본 오브젝트가 수행하는 종류의 복제를 알 수 없습니다.(및 XML 주석은 객체의 복제 방식보다는 인터페이스 의견이 아닌 인터페이스 의견을 얻을 수 없으므로 분명히하지 않습니다.)

내가 보통하는 것은 단순히 내가 원하는 것을 정확하게하는 복사 방법을 만드는 것입니다.



답변

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

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

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

var copy = anyObject.DeepClone();

우리는 다음과 같은 세 가지 구현을 가질 수 있습니다.

  1. By Serialization (the shortest code)
  2. By Reflection - 5x faster
  3. By Expression Trees - 20x faster

연결된 모든 방법은 잘 작동하며 깊이 테스트되었습니다.

출처:https://stackoverflow.com/questions/78536/deep-cloning-objects
반응형