'CLONE'에 해당되는 글 1건

  1. 2016.08.08 C# 객체의 Deep Copy / Deep Clone 구현 방법
개발팁2016. 8. 8. 08:21

C# 의 데이터에는 Value Type 와 Reference Type 이 존재한다.

 

Value Type 은 그 자체가 값인 경우이다.
Reference Type 은 별도의 메모리 공간에 인스탄스가 생성이 되고,
메모리의 위치만을 값으로 가지는 경우다.

 

배열이나 클래스의 객체는 Reference Type 으로, 실제값은 별도로 저장이된다.

 

Class 안에 멤버가 모두 Value Type 이라면, 단순히 Clone 을 통해서, 멤버 값이 전부 새로운 값으로 복사가 된다.
그러나, 한 Class 안에 Value Type, 배열, 클래스 객체가 공존한다면,
이 객체를 Clone 을 하게 되면, Value Type 은 정상적으로 복사가 되지만, 나머지 값은 원래 객체의 주소값이 복사가 된다.

 

원래 객체 / 클론 객체가 모두 동일한 메모리 위치를 가리키기 때문에, 한쪽에서 멤버객체의 값을 변경한다면,
다른곳에서는 의도치 않게 값이 변경되는 결과를 보게 된다.

 

Deep Clone 이라는 것은 Value / Reference Type 모두 새로운 값으로 복사를 하는 것이다.

 

 

2가지 방법이 있는데, 복사하려는 객체가 Serializable 객체라면,
// 2. Serializable 객체에 대한  Deep Clone 을 이용하여,
MemoryStream 을 통해 Serialize 하여 복사가 된다.

 

그렇지 않은 Class 는,
// 1. Deep Clone 구현 을 이용하여, 복사를 하면 된다.

Class 안의 멤버는 Value Type 과 Reference Type 이 있고,
Reference Type 객체 안에는 또다시, Value Type 과 Reference Type 의 멤버가 있고,
그 안에 있는 Reference 에는 또다시 Value Type 과 Reference Type 있다.
이런식으로 계층간에 여러 단계의 객체가 존재할 수가 있다.
이를 모두 복사해야 하기 때문에, Circular 또는 Recursive 를 통해서 값을 복사를 해야 한다.

 

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.Serialization.bformatters.Binary;
using System.IO;

namespace Hungry.Developer
{



    // Copyright 헝그리개발자(https://bemeal2.tistory.com)
    // 소스는 자유롭게 사용가능합니다. Copyright 는 삭제하지 마세요.

    // C# 객체를 복사해주는 클래스
    public class ObjectCopy
    {
        // 1. Deep Clone 구현
        public static T DeepClone<T>(T obj)
        {
            if (obj == null)
                throw new ArgumentNullException("Object cannot be null.");


            return (T)Process(obj, new Dictionary<object, object>() { });
        }



        private static object Process(object obj, Dictionary<object, object> circular)
        {
            if (obj == null)
                return null;



            Type type = obj.GetType();


            if (type.IsValueType || type == typeof(string))
            {
                return obj;
            }



            if (type.IsArray)
            {
                if (circular.ContainsKey(obj))
                    return circular[obj];



                string typeNoArray = type.FullName.Replace("[]", string.Empty);
                Type elementType = Type.GetType(typeNoArray + ", " + type.Assembly.FullName);
                var array = obj as Array;
                Array arrCopied = Array.CreateInstance(elementType, array.Length);

                circular[obj] = arrCopied;


                for (int i = 0; i < array.Length; i++)
                {
                    object element = array.GetValue(i);
                    object objCopy = null;



                    if (element != null && circular.ContainsKey(element))
                        objCopy = circular[element];
                    else
                        objCopy = Process(element, circular);



                    arrCopied.SetValue(objCopy, i);
                }



                return Convert.ChangeType(arrCopied, obj.GetType());
            }



            if (type.IsClass)
            {
                if (circular.ContainsKey(obj))
                    return circular[obj];



                object objValue = Activator.CreateInstance(obj.GetType());
                circular[obj] = objValue;
                FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);



                foreach (FieldInfo field in fields)
                {
                    object fieldValue = field.GetValue(obj);

                    if (fieldValue == null)
                        continue;



                    object objCopy = circular.ContainsKey(fieldValue) ? circular[fieldValue] : Process(fieldValue, circular);
                    field.SetValue(objValue, objCopy);
                }

                return objValue;
            }
            else
                throw new ArgumentException("Unknown type");
        }
 

        // 2. Serializable 객체에 대한  Deep Clone
        public static T SerializableDeepClone<T>(T obj)
        {
            using (var ms = new MemoryStream())
            {
                var bformatter = new Binarybformatter();
                bformatter.Serialize(ms, obj);
                ms.Position = 0;

                return (T) bformatter.Deserialize(ms);
            }
        }



    }

}


 

 

 

Posted by 헝개