2016년 10월 23일 일요일

[C#, Reflection] 직렬화를 자동화하기 (Automatical Serialization)

도입


직렬화는 클래스를 저장하고 불러오기에 편리한 도구지만

많은 프로퍼티와 많은 클래스들을 직렬화(Serialization)하려면

직렬화할 멤버 하나당 2줄의 코드를 쓸 필요가 있습니다

또한 코드를 일일이 쓰다보니 잘못 써서 버그가 생길 염려도 있지요

그렇기에 리플렉션을 이용하여 자동화하는 것이 필요합니다




SerializationHelper

using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace CodeGenerater.Example
{
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public sealed class SerializationTargetAttribute : Attribute
    {
    }
    public static class SerializationHelper
    {
        public static void Serialize(object Instance, SerializationInfo Info)
        {
            foreach (dynamic each in GetTarget(Instance))
                if (each is FieldInfo)
                    Info.AddValue(each.Name, each.GetValue(Instance), each.FieldType);
                else if (each is PropertyInfo)
                    Info.AddValue(each.Name, each.GetValue(Instance), each.PropertyType);
        }
        public static void Deserialize(object Instance, SerializationInfo Info)
        {
            foreach (dynamic each in GetTarget(Instance))
                if (each is FieldInfo)
                    each.SetValue(Instance, Info.GetValue(each.Name, each.FieldType));
                else if (each is PropertyInfo)
                    each.SetValue(Instance, Info.GetValue(each.Name, each.PropertyType));
        }
        static IEnumerable<MemberInfo> GetTarget(object Instance)
        {
            return from q in Instance.GetType().GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                   where q.GetCustomAttribute<SerializationTargetAttribute>() != null
                   select q;
        }
    }
}
cs

직렬화를 자동화하기 위한 클래스입니다

Serialize, Deserialize 함수에 있는 dynamic 키워드는 4.0버전부터 가능하므로

만약 그 이전의 버전을 사용하고 계실경우 아래와 같이 쓰시면 됩니다

public static void Serialize(object Instance, SerializationInfo Info)
{
    foreach (var each in GetTarget(Instance))
        if (each is FieldInfo)
        {
            FieldInfo F = each as FieldInfo;
            Info.AddValue(F.Name, F.GetValue(Instance), F.FieldType);
        }
        else if (each is PropertyInfo)
        {
            PropertyInfo P = each as PropertyInfo;
            Info.AddValue(P.Name, P.GetValue(Instance), P.PropertyType);
        }
}
cs





Example

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace CodeGenerater.Example
{
    [Serializable]
    class Program : ISerializable
    {
        #region Serializaion
        public void GetObjectData(SerializationInfo Info, StreamingContext Context)
        {
            SerializationHelper.Serialize(this, Info);
        }
        Program(SerializationInfo Info, StreamingContext Conmtext)
        {
            SerializationHelper.Deserialize(this, Info);
        }
        #endregion
        #region Constructor
        public Program()
        {
        }
        #endregion
        #region Field
        [SerializationTarget]
        int PrivateField;
        #endregion
        #region Property
        [SerializationTarget]
        public int PublicProperty
        {
            set;
            get;
        }
        [SerializationTarget]
        public int HalfPublicProperty
        {
            private set;
            get;
        }
        [SerializationTarget]
        int PrivateProperty
        {
            set;
            get;
        }
        #endregion
        #region Method
        static void Main(string[] args)
        {
            var T = new Program();
            int R_PrivateProperty, R_HalfPublicProperty, R_PublicProperty, R_PrivateField;
        
            #region Initialize
            Random R = new Random();
            
            T.PrivateProperty = R_PrivateProperty = R.Next();
            T.HalfPublicProperty = R_HalfPublicProperty = R.Next();
            T.PublicProperty = R_PublicProperty = R.Next();
            T.PrivateField = R_PrivateField = R.Next();
            #endregion
            using (MemoryStream Stream = new MemoryStream())
            {
                #region Serialization
                BinaryFormatter F = new BinaryFormatter();
                F.Serialize(Stream, T);
                #endregion
                #region Init
                T = null;
                Stream.Position = 0;
                #endregion
                #region Deserialization
                T = (Program)F.Deserialize(Stream);
                #endregion
                #region Check
                bool TestSuccess =
                    T.PrivateProperty == R_PrivateProperty &&
                    T.HalfPublicProperty == R_HalfPublicProperty &&
                    T.PublicProperty == R_PublicProperty &&
                    T.PrivateField == R_PrivateField;
                if (TestSuccess)
                    Console.WriteLine("Success");
                else
                    Console.WriteLine("Fail");
                #endregion
            }
        }
        #endregion
    }
}
cs

간단하게 작동여부를 확인할 수 있는 예제입니다





주의할 점

프로퍼티를 직렬화할때 Setter와 Getter가 한정자와 상관없이 모두 존재해야합니다

Setter와 Getter 중 하나가 존재하지 않으면 쓰거나 읽을때 오류가 납니다





Archive File

댓글 없음:

댓글 쓰기