it-swarm-ko.tech

딥 복제 개체

나는 다음과 같이하고 싶다.

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

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

필자는이 기능이 자주 필요하지 않기 때문에 필요할 때마다 새로운 개체를 만든 다음 각 속성을 개별적으로 복사하는 방법을 사용했지만보다 나은 또는보다 우아한 처리 방법이 있다는 느낌이 항상 떠올랐다. 그 상황.

원본 개체에 변경 사항을 반영하지 않고 복제 된 개체를 수정할 수 있도록 개체를 복제 또는 완전 복사하려면 어떻게합니까?

2028
NakedBrunch

표준 관행은 ICloneable 인터페이스 ( here 에 설명되어 있으므로 역류하지 않을 것입니다)를 구현하는 것이지만 여기서는 코드 프로젝트 얼마 전에 우리의 것들에 통합했습니다.

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

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.", nameof(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();이됩니다.

EDIT (2015 년 1 월 10 일) 최근에 (Newtonsoft) Json을 사용하여이 작업을 다시 시작했다고 언급했습니다. 해야 함 더 가벼우 며 [Serializable] 태그의 오버 헤드를 피합니다. (NB @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);
}
1628
johnc

주로 원시와리스트라는 매우 단순한 객체에 대한 복제자를 원했습니다. 개체가 상자 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);
    }
}
241
craastad

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

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

내가 보통하는 것은 단순히 내가 원하는 것을하는 Copy 메소드를 만드는 것입니다.

169
Ryan Lundy

여기에 링크 된 많은 옵션 과이 문제에 대한 가능한 해결책에 대해 많이 읽은 후 모든 옵션은 Ian P의 링크에 요약되어 있음 (모든 다른 옵션은 그것들의 변형입니다) 그리고 가장 좋은 해결책은 질문 의견에 Pedro77의 링크 에 의해 제공됩니다.

여기서는 2 개의 참고 문헌 중 관련 부분을 복사하겠습니다. 그렇게 할 수있는 방법은 다음과 같습니다.

C 선명하게 객체를 복제하는 가장 좋은 방법!

무엇보다도, 우리의 모든 옵션은 다음과 같습니다.

표현식 트리 별 빠른 딥 카피 는 직렬화, 리플렉션 및 표현식 트리 별 복제 성능 비교도 제공합니다.

ICloneable (즉, 수동)을 선택하는 이유

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

그의 모든 기사는 Person, BrainCity의 3 가지 개체를 사용하여 대부분의 경우에 적용하려고하는 예제를 중심으로 진행됩니다. 우리는 자신의 두뇌를 가지지 만 같은 도시를 가진 사람을 복제하려고합니다. 위의 다른 방법으로 기사를 가져 오거나 읽을 수있는 모든 문제를 파악할 수 있습니다.

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

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

이 구현이 다음 사항을 명확하게 할 수 있기를 바랍니다.

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 [email protected]
This is person with [email protected]
SkilledPerson: This is person with [email protected]
SkilledPerson: This is person with [email protected]

개체 수를 유지하는 경우 여기에 구현 된 복제본은 개체 수를 올바르게 유지합니다.

106
cregox

나는 복제 생성자를 선호한다. 의도가 분명합니다.

77
Nick

모든 public 속성을 복사하는 간단한 확장 메소드. 모든 객체에 대해 작동하고 않습니다 클래스가 [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 ) } );
        }
    };
}
38

글쎄 Silverlight에서 ICloneable을 사용하는 데 문제가 있었지만 나는 seralization에 대한 아이디어를 좋아했으며, XML을 seralize 할 수 있으므로 이렇게했습니다.

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

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

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

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

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

        return sbTR.ToString();
    }

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

방금 CloneExtensions library 프로젝트를 만들었습니다. Expression Tree 런타임 코드 컴파일로 생성 된 간단한 할당 작업을 사용하여 빠르고 깊은 복제를 수행합니다.

사용 방법]

필드와 속성 사이에 할당 톤이있는 고유 한 Clone 또는 Copy 메서드를 작성하는 대신 Expression Tree를 사용하여 프로그램에서 직접 수행 할 수 있습니다. 확장 메서드로 표시된 GetClone<T>() 메서드를 사용하면 인스턴스에서 간단하게 호출 할 수 있습니다.

var newInstance = source.GetClone();

source 열거 형을 사용하여 newInstance에서 CloningFlags (으)로 복사 할 항목을 선택할 수 있습니다.

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

무엇을 복제 할 수 있습니까?

  • 기본 (int, uint, byte, double, char 등), 알려진 불변 유형 (DateTime, TimeSpan, String) 및 대리자 (Action, Func 등 포함)
  • 널 입력 가능
  • T [] 배열
  • 일반 클래스 및 구조체를 포함한 사용자 정의 클래스 및 구조체.

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

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

얼마나 빠릅니까?

지정된 유형 T에 대해 GetClone<T>을 (를) 처음 사용하기 전에 멤버 정보를 한 번만 수집해야하기 때문에 솔루션이 반영보다 빠릅니다.

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

및 기타 ...

documentation 에서 생성 된 표현식에 대해 자세히 알아보십시오.

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

List<int>에 대해 자신 만의 Clone 메서드를 작성하는 방법과는 다른가요?

29
MarcinJuraszek

ValueInjecter 또는 Automapper 와 같은 타사 응용 프로그램을 이미 사용하고 있다면 다음과 같이 할 수 있습니다.

MyObject oldObj; // The existing object to clone

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

이 메서드를 사용하면 개체에 ISerializable 또는 ICloneable을 구현할 필요가 없습니다. 이것은 MVC/MVVM 패턴과 공통이므로이 같은 간단한 도구가 만들어졌습니다.

CodePlex의 값 주사기 심층 복제 솔루션 을 참조하십시오.

26
Michael Cox

간단한 대답은 ICloneable 인터페이스를 상속 한 다음 .clone 함수를 구현하는 것입니다. Clone은 구성원별로 복사본을 만들고이를 필요로하는 멤버에 대해 완전 복사본을 수행 한 다음 결과 개체를 반환해야합니다. 이는 재귀 적 작업입니다 (복제하려는 클래스의 모든 멤버가 값 유형이거나 ICloneable을 구현하고 해당 멤버가 값 유형이거나 ICloneable을 구현해야 함 등).

ICloneable을 사용한 복제에 대한 자세한 설명은 this article 을 참조하십시오.

long 답은 "의존적"입니다. 다른 사람들이 언급했듯이 ICloneable은 제네릭에서 지원되지 않으며 원형 클래스 참조에 대한 특별한 고려가 필요하며 실제로 .NET Framework에서 "실수" 로 볼 수 있습니다. 직렬화 방법은 직렬화 할 수있는 객체에 따라 달라지며 그렇지 않을 수도 있으며 제어 할 수도 없습니다. "최고의"관행 인 공동체에서 여전히 많은 논쟁이 있습니다. 실제로, ICloneable과 같은 모든 상황에 대해 모범 사례를 적용하는 유일한 해결책은 없습니다.

몇 가지 추가 옵션 (이안에 대한 크레딧)은이 개발자 코너 기사 를 참조하십시오.

20
Zach Burlingame

가장 좋은 방법은 확장 메소드 구현하는 것입니다.

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

그런 다음 솔루션의 어느 곳에서나 사용할 수 있습니다.

var copy = anyObject.DeepClone();

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

  1. 직렬화로 (가장 짧은 코드)
  2. By Reflection - 5 배 빠름
  3. 표현식 트리로 - 20 배 빨라짐

링크 된 모든 메소드는 잘 작동하며 심층적으로 테스트되었습니다.

19
frakon
  1. 기본적으로 ICloneable 인터페이스를 구현하고 객체 구조 복사를 구현해야합니다.
  2. 모든 회원의 완전한 사본 인 경우 모든 아동이 복제 가능하다는 것을 보장해야합니다 (선택하는 솔루션과 관련이 없음).
  3. 때로는이 과정에서 몇 가지 제한 사항을 알아야합니다. 예를 들어 ORM 개체를 복사하면 대부분의 프레임 워크에서 세션에 첨부 된 하나의 개체 만 허용되며이 개체의 복제본을 만들지 않아도됩니다. 이러한 개체의 세션 연결에 대해 설명합니다.

건배.

16
dimarzionist

알 수없는 유형에 대한 진정한 복제를 원하면 fastclone 을 살펴보십시오.

이는 바이너리 직렬화보다 약 10 배 빠른 표현 기반의 복제로 완벽한 객체 그래프 무결성을 유지합니다.

즉, 계층 구조에서 동일한 객체로 여러 번 참조하는 경우 복제본에 단일 인스턴스가 참조됩니다.

인터페이스, 속성 또는 복제 될 오브젝트에 대한 기타 수정은 필요하지 않습니다.

15
Michael Sander

일을 간단하게 유지하고 AutoMapper 다른 언급과 같이 하나의 객체를 다른 객체에 매핑하는 간단한 라이브러리입니다 ... 동일한 유형의 다른 객체에 객체를 복사하려면 3 줄의 코드 만 있으면됩니다.

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();
11
Stacked

필자는 List <T>를 직접 복사해야하는 .NET 단점을 극복하기 위해이 문제를 해결했습니다.

나는 이것을 사용한다 :

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> cloner 사용 :

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

Q. 왜이 답변을 선택해야합니까?

  • 가장 빠른 속도의 .NET이 가능한 경우이 답변을 선택하십시오.
  • 정말로, 정말로 쉬운 복제 방법을 원한다면이 답변을 무시하십시오.

즉, 고정이 필요한 성능 병목 현상이없고 프로파일 러로이를 입증 할 수없는 경우가 아니면 다른 답변으로 이동하십시오 .

다른 방법보다 10 배 빠름

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

  • 직렬화/역 직렬화와 관련된 것보다 10 배 빠릅니다.
  • 이론상 최대 속도 인 .NET과 거의 비슷합니다.

그리고 방법은 ...

최고의 속도를 위해 Nested MemberwiseClone을 사용하여 딥 카피 를 수행 할 수 있습니다. 값 구조체를 복사하는 것과 거의 같은 속도이며 (a) 리플렉션 또는 (b) 직렬화 (이 페이지의 다른 답변에서 설명 된 것)보다 훨씬 빠릅니다.

if 딥 카피에 필요한 MemberwiseClone 사용 클래스의 각 중첩 레벨에 대해 ShallowCopy를 수동으로 구현해야합니다. 및 모든 ShallowCopy 메소드를 호출하여 완전한 복제본을 작성하는 DeepCopy. 이것은 간단합니다. 총 몇 줄 밖에되지 않습니다. 아래 데모 코드를 참조하십시오.

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

  • 중첩 된 구조체에서 Nested MemberwiseClone의 경우 1.08 초
  • 중첩 클래스에서 중첩 MemberwiseClone의 경우 4.77 초
  • 직렬화/직렬화 해제시 39.93 초

Struct를 복사하는 것만 큼 빠른 클래스에서 Nested MemberwiseClone을 사용하고 struct를 복사하는 것은 .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;
    }
}

그런 다음 main에서 데모를 호출하십시오.

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

다시 if 딥 카피에 필요한 MemberwiseClone 을 사용하려면 각 중첩 수준에 대해 ShallowCopy를 수동으로 구현해야합니다. 클래스, 그리고 모든 ShallowCopy 메소드를 호출하여 완전한 복제본을 작성하는 DeepCopy. 이것은 간단합니다. 총 몇 줄 밖에되지 않습니다. 위의 데모 코드를 참조하십시오.

값 유형과 참조 유형

객체 복제와 관련하여 " struct "와 " class "사이에는 큰 차이가 있습니다. :

  • " struct "인 경우 value type 이므로 복사 만하면 내용이 복제됩니다 ( 그러나이 게시물의 기술을 사용하지 않으면 얕은 복제본 만 생성됩니다.
  • " class "인 경우 reference type 이므로 복사하면 복사하는 것입니다. 그것에 대한 포인터. 실제 복제본을 만들려면보다 창의적이어야하고 값 유형과 참조 유형의 차이 를 사용하여 메모리에 원래 객체의 다른 사본을 만듭니다.

값 유형과 참조 유형의 차이점 을 참조하십시오.

디버깅을 돕는 체크섬

  • 객체를 잘못 복제하면 매우 찾기 어려운 버그가 발생할 수 있습니다. 프로덕션 코드에서는 체크섬을 구현하여 객체가 올바르게 복제되었으며 다른 참조로 손상되지 않았는지 다시 확인하는 경향이 있습니다. 이 체크섬은 해제 모드에서 끌 수 있습니다.
  • 이 방법이 매우 유용하다는 것을 알았습니다. 종종 전체가 아닌 객체의 일부만 복제하려고합니다.

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

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

  • 하나 이상의 스레드가 소유 한 클래스를 수정 한 다음이 클래스의 전체 사본을 ConcurrentQueue로 푸시 할 수 있습니다.
  • 그런 다음 하나 이상의 스레드가 이러한 클래스의 사본을 가져 와서 처리합니다.

이것은 실제로 매우 잘 작동하며 하나 이상의 스레드 (소비자)에서 많은 스레드 (생산자)를 분리 할 수 ​​있습니다.

중첩 된 구조체를 사용하면 중첩 된 클래스를 직렬화/역 직렬화하는 것보다 35 배 빠르며 머신에서 사용 가능한 모든 스레드를 활용할 수 있습니다.

최신 정보

분명히 ExpressMapper는 위와 같은 수동 코딩보다 빠르지는 않지만 빠릅니다. 그들이 프로파일 러와 어떻게 비교되는지보아야 할 수도 있습니다.

9
Contango

리플렉션을 통해 구현 된 것을 보았습니다. 기본적으로 객체의 멤버를 반복하고 적절히 새로운 객체에 복사하는 메서드가있었습니다. 참조 유형이나 컬렉션에 도달했을 때 재귀 호출을 자체적으로 수행했다고 생각합니다. 반사는 비싸지 만 꽤 잘 돌아갔다.

7
xr280xr

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

https://github.com/kalisohn/CloneBehave

또한 너겟 패키지로도 제공됩니다 : https://www.nuget.org/packages/Clone.Behave/1.0.0

예를 들면 다음과 같습니다. 다음 코드는 deepClone Address이지만 _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
7
kalisohn

일반적으로 ICloneable 인터페이스를 구현하고 직접 복제를 구현합니다. C # 개체에는 모든 프리미티브에 도움이되는 얕은 복사본을 수행하는 기본 제공 MemberwiseClone 메서드가 있습니다.

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

7
HappyDude

전체 복사본 구현 방법은 다음과 같습니다.

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

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

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

이 방법은 나를 위해 문제를 해결했습니다.

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

6
JerryGoyal

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

복사 할 항목이 더 많은 경우 추가하십시오.

5
LuckyLikey

코드 생성기

우리는 수작업 구현에서부터 구현에 이르기까지 직렬화에서 많은 아이디어를 보았으며 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 체크를 가지고 있지만, 더 나은 이해를 위해 그들을 버렸습니다.

5
Toxantron

Serialization/Deserialization을 중계하지 않고 빠르고 쉽게 해결할 수있는 솔루션입니다.

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

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

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

EDIT:가 필요합니다.

    using System.Linq;
    using System.Reflection;

그게 내가 그것을 사용한 방법이야.

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

나는 당신이 이것을 시도 할 수 있다고 생각합니다.

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

이 차례를 따라라:

  • 읽기 전용 Self 속성을 사용하여 ISelf<T>를 정의하면 T을 반환하고 ICloneable<out T>ISelf<T>에서 파생되며 T Clone() 메서드를 포함합니다.
  • 그런 다음 전달 된 유형에 protected virtual generic VirtualClone 형변환 CloneBase을 구현하는 MemberwiseClone 유형을 정의하십시오.
  • 각 파생 된 유형은 기본 복제 메소드를 호출 한 다음 부모 VirtualClone 메소드가 아직 처리하지 않은 파생 유형의 해당 부분을 올바르게 복제하기 위해 수행해야하는 모든 작업을 수행하여 VirtualClone을 구현해야합니다.

최대한의 상속 기능을 위해 공용 클론 기능을 노출하는 클래스는 sealed이어야하지만 그렇지 않으면 복제가없는 경우를 제외하고는 기본 클래스에서 파생됩니다. 명시 적으로 복제 가능한 유형의 변수를 전달하는 대신 ICloneable<theNonCloneableType> 유형의 매개 변수를 가져옵니다. 이렇게하면 Foo의 복제 가능한 파생물이 DerivedFoo의 복제 가능한 파생어와 함께 작동 할 것으로 예상되는 루틴을 허용 할뿐만 아니라 Foo의 복제 불가능한 파생물도 만들 수 있습니다.

4
supercat

나는 '[Serializable]'과 '[DataContract]'와 함께 작동하는 허용 된 버전의 버전을 만들었습니다. 필자가 작성한 지 오래되었지만, 올바르게 기억하면 [DataContract]에는 다른 serializer가 필요했습니다.

System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml 이 필요합니다.

public static class ObjectCopier
{

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

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

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


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

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

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


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

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

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


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

} 
3
Jeroen Ritmeijer

Object Tree가 Serialize 일 경우 다음과 같은 것을 사용할 수도 있습니다.

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

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

클래스가 커지면 복제 된 필드 만 계속 존재하며 일련 화 된 필드도 있어야합니다.

3
LuckyLikey

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

이 함수를 클래스에 추가하면됩니다.

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

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

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

yourClass newLine = oldLine.DeepCopy();

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

3
Chtiwi Malek

좋아,이 게시물에 반사와 몇 가지 분명한 예제가 있지만, 반사가 일반적으로 천천히, 당신이 제대로 캐시하기 전까지는.

제대로 캐시하면 1000000 개를 4,6s만큼 깊게 복제합니다 (감시자가 측정).

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

3
Roma Borodov

이 질문에 대한 거의 모든 답변이 불만족 스럽거나 분명히 내 상황에서 작동하지 않기 때문에 전적으로 반영으로 구현되고 여기에있는 모든 요구 사항을 해결 한 AnyClone 을 작성했습니다. 복잡한 구조의 복잡한 시나리오에서 직렬화가 작동하지 못했습니다. IClonable은 이상적이지 않습니다. 사실 필요하지 않습니다.

표준 무시 특성은 [IgnoreDataMember], [NonSerialized]를 사용하여 지원됩니다. 복잡한 컬렉션, 설정자없는 속성, 읽기 전용 필드 등을 지원합니다.

나는 다른 사람들이 저와 같은 문제에 부딪쳤을 때 도움이되기를 바랍니다.

2
Michael Brown

Marc Gravells protobuf-net을 serializer로 사용할 때 복사 할 객체가 [Serializable]로 지정되지 않으므로 직렬화 할 수없고 복제 메소드가 예외를 throw하므로 허용 된 대답에 약간의 수정이 필요합니다.
protobuf-net에서 작동하도록 수정했습니다.

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

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

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

이것은 [ProtoContract] 속성의 존재를 검사하고 protobufs 자신의 포맷터를 사용하여 객체를 직렬화합니다.

1
Basti M

"not ISerializable "형식을 지원하는 C # Extension.

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

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

용법

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

IClonable 인터페이스로 얼마나 많은 노력을 기울일 수 있는지 믿을 수 없습니다. 특히 클래스 계층이 무거운 경우에는 더욱 그렇습니다. 또한 MemberwiseClone은 어떻게 든 이상하게 작동합니다. 일반적인 List 유형의 구조조차 정확히 복제하지는 않습니다.

물론 직렬화에 대한 가장 흥미로운 딜레마는 참조를 다시 직렬화하는 것입니다. 자녀 - 부모 관계가있는 클래스 계층 구조. 나는 바이너리 시리얼 라이저가이 경우에 당신을 도울 수 있을지 의심 스럽다. (재귀 루프 + 스택 오버 플로우로 끝날 것입니다.).

나는 어쨌든 여기에 제안 된 솔루션을 좋아했다 : 어떻게 당신이 .NET에서 개체의 깊은 복사본을합니까 (C # 구체적으로)?

그러나 - 그것은 목록을 지원하지 않았고, 지원을 추가했으며, 또한 재 양육을 고려했습니다. 해당 필드 나 속성의 이름을 "parent"로 지정해야하는 parenting only 규칙에 대해서는 DeepClone에서 무시됩니다. 역 참조에 대한 자신 만의 규칙을 결정할 수도 있습니다. 예를 들어 "왼쪽/오른쪽"일 수있는 트리 계층 구조 등입니다.

다음은 테스트 코드를 포함한 전체 코드 스 니펫입니다.

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

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

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

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


            Debug.WriteLine("ok");

        }
    }

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

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

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


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

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

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

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

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

            Type type = obj.GetType();

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

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

                return newlist;
            } //if

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

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

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

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

                    object fieldValue = field.GetValue(obj);

                    if (fieldValue == null)
                        continue;

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

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

                return null;
            }
        } //DeepClone
    }

}
1
TarmoPikaro

또 다른 JSON.NET 대답. 이 버전은 ISerializable을 구현하지 않는 클래스에서 작동합니다.

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

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

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

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

매퍼는 전체 복사본을 수행합니다. Foreach 멤버는 새로운 개체를 만들고 모든 값을 할당합니다. 그것은 원시적이지 않은 각 멤버에 대해 재귀 적으로 작동합니다.

나는 당신에게 가장 빠른, 현재 적극적으로 개발 된 것들 중 하나를 제안합니다. 나는 UltraMapper https://github.com/maurosampietro/UltraMapper를 제안한다

Nuget 패키지 : https://www.nuget.org/packages/UltraMapper/

1
Mauro Sampietro

딥 복제는 state 복사에 관한 것입니다. .net의 경우 statefields를 의미합니다.

계층 구조가 있다고 가정 해 봅시다.

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

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

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

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

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

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

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

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

복제가 가능합니다 :

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

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

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

        return result;
    }
}

데모 1 :

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

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

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

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

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

결과:

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

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

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

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

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

모든 새 객체는 random 필드에 대해 임의의 값을 갖지만 cloneimage과 정확히 일치합니다.

데모 2 :

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

// ...

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

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

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

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

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

결과:

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

이벤트 백업 필드도 복사되고 클라이언트는 복제 이벤트에도 등록됩니다.

1
Ted Mucuzany

제네릭 접근법은 모두 기술적으로 유효하지만 실제 사본을 필요로하는 경우가 거의 없으므로 필자는 메모를 추가하기를 원합니다. 따라서 실제 비즈니스 응용 프로그램에서 일반적인 완전 복사를 사용하는 것에 강력히 반대 할 것입니다. 객체가 복사되고 명시 적으로 수정 된 장소. 잃어 버리기 쉽습니다.

대부분의 실제 상황에서도 데이터 액세스 프레임 워크에 결합되어있을뿐만 아니라 실제로 복사 된 비즈니스 개체가 거의 100 %가되지 않아야하므로 복사 프로세스를 최대한 세부적으로 제어하려고합니다. 예를 들어, 객체 참조를 식별하기 위해 ORM에서 사용하는 referenceId를 사용하면 전체 IDC도이 ID를 복사하므로 개체가 다른 경우 데이터 저장소에 제출하자마자 불만을 토로합니다. 어쨌든 복사 한 후에이 속성을 수동으로 수정해야하며 개체가 변경되면 일반 딥 복사를 사용하는 모든 위치에서 개체를 조정해야합니다.

ICloneable로 @cregox 대답에 확장, 실제로 깊은 사본 무엇입니까? 원래 객체와 동일하지만 다른 메모리 공간을 차지하는 힙의 새롭게 할당 된 객체는 일반 객체를 사용하는 것보다 새로운 객체를 만드는 것이 왜 좋은가요?

개인적으로 내 도메인 객체에 대한 정적 팩토리 메소드의 아이디어를 사용합니다.

예:

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

        protected Client()
        {
        }

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

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

        public string Address { get; set; }

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

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

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

누군가가 어떻게하면 복제 프로세스에 대한 완전한 제어권을 유지하면서 객체 인스턴스화를 구조화 할 수 있는지 찾고 있는데, 그것은 개인적으로 매우 성공적이었던 솔루션입니다. 보호 된 생성자도 그렇게하므로 다른 개발자는 객체 내부의 구성 논리를 캡슐화하는 객체 인스턴스화의 깔끔한 단일 지점을 제공하는 팩토리 메서드를 사용해야합니다. 또한 메소드를 오버로드하고 필요할 경우 여러 장소에 대한 여러 복제 로직을 가질 수 있습니다.

0
Piotr Jerzy Mamenas

면책 조항 : 나는 언급 된 패키지의 저자입니다.

2019 년이 질문에 대한 최고 답변이 여전히 직렬화 또는 반영을 사용하는 방법에 놀랐습니다.

직렬화가 제한적이며 (속성, 특정 생성자 등이 필요) 매우 느립니다.

BinaryFormatter에는 Serializable 속성이 필요하고 JsonConverter에는 매개 변수가없는 생성자 또는 속성이 필요하며 읽기 전용 필드 또는 인터페이스를 잘 처리하지 않으며 둘 다 필요보다 10-30 배 느립니다.

표현식 트리

대신 Expression Trees 또는 Reflection.Emit 를 사용하여 복제 코드를 한 번만 생성 한 다음 느린 반영 대신 컴파일 된 코드를 사용할 수 있습니다 또는 직렬화.

문제를 직접 겪고 만족스러운 해결책을 찾지 못했을 때, 나는 그렇게하는 패키지를 만들기로 결정했고 모든 유형에서 작동하며 거의 사용자 정의 코드만큼 빠릅니다.

GitHub에서 프로젝트를 찾을 수 있습니다 : https://github.com/marcelltoth/ObjectCloner

용법

NuGet에서 설치할 수 있습니다. ObjectCloner 패키지를 가져 와서 다음과 같이 사용하십시오.

var clone = ObjectCloner.DeepClone(original);

또는 확장자로 객체 유형을 오염시키는 것이 마음에 들지 않으면 ObjectCloner.Extensions도 얻고 다음을 작성하십시오.

var clone = original.DeepClone();

공연

클래스 계층 복제의 간단한 벤치 마크는 리플렉션을 사용하는 것보다 ~ 3 배, Newtonsoft.Json 직렬화보다 ~ 12 배, 제안 된 BinaryFormatter보다 ~ 36 배 빠른 성능을 보여주었습니다.

0
Marcell Toth

_System.Text.Json_ 사용 :

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

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

새로운 API는 _Span<T>_를 사용하고 있습니다. 이것은 빠르며 일부 벤치 마크를 수행하는 것이 좋을 것입니다.

참고 : Json.NET에서와 같이 _ObjectCreationHandling.Replace_은 기본적으로 컬렉션 값을 대체하기 때문에 필요하지 않습니다. 모든 것이 새로운 공식 API로 대체 될 것이므로 이제 Json.NET에 대해 잊어야합니다.

이것이 개인 필드에서 작동하는지 확실하지 않습니다.

0
Konrad

Emit이라는 새로운 방법을 찾았습니다.

Emit을 사용하여 일리노이를 앱에 추가하고 실행할 수 있습니다. 그러나 나는 내가 그 대답을 쓰는 것을 완벽하게하고 싶다는 좋은 방법이 없다고 생각합니다.

Emit은 공식 문서가이드

코드를 읽으려면 IL을 배워야합니다. 클래스의 속성을 복사 할 수있는 코드를 작성합니다.

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

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

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

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

코드는 전체 복사본이 될 수 있지만 속성을 복사 할 수 있습니다. 당신이 일리노이를 위해 그것을 바꿀 수있는 깊은 사본을 만들고 싶다면, 내가 할 수없는 너무 어려워.

0
lindexi

클로닝을 해결하기위한 빠르고 쉽고 효과적인 Nuget 패키지

모든 답변을 읽은 후 아무도이 훌륭한 패키지를 언급하지 못했습니다.

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

Readme에 대해 좀 더 자세히 설명하자면, 우리가 직장에서 그것을 선택한 이유는 다음과 같습니다.

면책 조항-요구 사항 :

  • . NET 4.0 이상 또는 .NET Standard 1.3 (.NET Core)
  • 완전한 신뢰 권한 집합 또는 리플렉션 권한 (MemberAccess) 이 필요합니다.
  • 깊거나 얕은 복사가 가능합니다
  • 딥 클로닝에서는 모든 객체 그래프가 유지됩니다.
  • 결과 복제가 엄청나게 빠르므로 런타임에 코드 생성을 사용합니다.
  • 내부 구조로 복사 된 객체, 메소드 또는 ctor가 없습니다.
  • 어떻게 든 클래스를 표시 할 필요가 없습니다 (직렬화 가능 속성 또는 인터페이스 구현 등)
  • 복제 할 객체 유형을 지정할 필요가 없습니다. 객체는 인터페이스 또는 추상 객체로 캐스트 할 수 있습니다 (예 : int 배열을 추상 배열 또는 IEnumerable로 복제 할 수 있으며 오류없이 null을 복제 할 수도 있음)
  • 복제 된 개체는 자신이 복제 된 것으로 판단 할 수있는 능력이 없습니다 (매우 구체적인 방법은 제외)

사용법은 이렇습니다 :

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

기본적으로 자동 복사 생성자를 호출해야하는 메소드 내부를 재 작성하는 방법은?

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

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

나에게 효과가있는 것 같다.

0
will_m

이렇게하면 객체의 모든 읽기 가능 및 쓰기 가능 속성이 다른 객체에 복사됩니다.

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

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

                return trg;
            }
        }

이것이 이것을 사용하는 방법입니다.

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

또는 모든 것을 복사하려면 :

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