[C#] 리플렉션 없고 자동완성 되는 PresetLoader.cs



비주얼 스튜디오 에서 기본 제공해 주는 *.config 는 상당히 편리한 구석이 있으나 문서 자체가 너무 복잡하게 되어 버릴 경우가 많아서 사용자에게 xml 을 수정 하라는 요구를 하기가 사뭇 꺼려 진다.


하지만 문자열로 xml의 값을 하나 하나 불러 오는것도 인간적인 실수 와 더불어 사용 코드가 그리 깔끔하지 않아 불편함이 있다.

그래서 개선한 버전인 클래스 필드의 변수를 리플렉션 하여 얻어와 값을 인위적으로 넣어 주는 방법이 있었는데, 리플렉션 역시 사용 환경에 따라 제약이 생길 수 있다. (난독화, MONO ,  등등)






그래서 셋팅값의 자동완성도 가능하면서 리플렉션도 사용하지 않는 PresetLoader 를 만들었고 그것의 실체와 사용법은 아래와 같다. 



// // PresetLoader.cs // CallbackTest // // Created by SuperSc on 2015. 1. 8.. // Copyright (c) 2015년 SuperSc. All rights reserved. // using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; namespace PresetLoader { /* * 실제 사용 예시 * new PresetLoader(); string name = PresetLoader.GetValue(PresetLoader.PresetProp.string_name); int age = PresetLoader.GetValue(PresetLoader.PresetProp.int_age); float fv = PresetLoader.GetValue(PresetLoader.PresetProp.float_floatvalue); int[] bodytypes = PresetLoader.GetValue(PresetLoader.PresetProp.ints_bodyvalues); float[] floats = PresetLoader.GetValue(PresetLoader.PresetProp.floats_floatvalues); string[] alias = PresetLoader.GetValue(PresetLoader.PresetProp.strings_alias); * * */ public class PresetLoader { /* * PresetProp Enum 안에 사용할 Preset 값들을 정의 합니다. * * 선언법 : [값 타입](s)_[이름] * 배열타입으로 선언하기 : [값 타입] 뒤에 s 형태로 복수영문을 표현 하면 배열로 인식 합니다. * * * 예시 : * 1) int_age // int 형인 속성 age * 2) ints_numbers // int[] 형인 numbers 배열 * * */ public enum PresetProp { int_age , float_floatvalue, floats_floatvalues, ints_bodyvalues , string_name, strings_alias } /* * 이 메서드를 이용하여 Preset의 값을 찾아 냅니다. * 극 장점은 바로 자동완성이 된다는 점 입니다. * * 사용법 : int[] numbers = PresetLoader.GetValue(PresetLoader.PresetProp.numbers); * */ public static dynamic GetValue(PresetProp name) { PropTypeValue resultItem = default(PropTypeValue); foreach (PropTypeValue item in _proplist) { if (item.prop_enum_object == name) { resultItem = item; break; } } return resultItem.prop_value; } //---------------------------------------------------------------------PresetLoader #region //---------------------------------------------------------------------define PropTypeValue private struct PropTypeValue { public PresetProp prop_enum_object; public bool prop_isArray; public string prop_name; public Type prop_type; public dynamic prop_value; } //---------------------------------------------------------------------Initializer #region private static readonly string PresetFileName = "Preset.xml"; private static List<PropTypeValue> _proplist; private static XElement _preset_xml; public PresetLoader() { _proplist = new List<PropTypeValue>(); foreach (string item in Enum.GetNames(typeof(PresetProp))) { string[] item_split = item.Split('_'); string _raw_type_name = item_split[0]; string _raw_prop_name = item_split[1]; bool _raw_isArray = false; PresetProp _raw_enum = (PresetProp)Enum.Parse(typeof(PresetProp), item); Type _raw_type = DefineTypeFromString(_raw_type_name); if (_raw_type_name.Substring(_raw_type_name.Length - 1) == "s") { _raw_isArray = true; } _proplist.Add(new PropTypeValue() { prop_enum_object = _raw_enum, prop_isArray = _raw_isArray, prop_name = _raw_prop_name, prop_type = _raw_type, prop_value = null }); } PresetXMLProgress(); //결과 화면 표시 Console.WriteLine("=======preset load reslut======="); foreach (var item in _proplist) { Console.WriteLine("PropName(enum) : " + item.prop_enum_object +" name: " + item.prop_name + " type: " + item.prop_type + " value: " + item.prop_value); } } private Type DefineTypeFromString(string value) { Type result = typeof(string); switch (value.ToLower()) { case "string": result = typeof(string); break; case "strings": result = typeof(string[]); break; case "int": result = typeof(int); break; case "ints": result = typeof(int[]); break; case "float": result = typeof(float); break; case "floats": result = typeof(float[]); break; default : throw new Exception("can't definition type"); } return result; } #endregion //---------------------------------------------------------------------Progress #region private void PresetXMLProgress() { MakeXML(); ReadXML(); } /* * XML 파일을 체크 하여 없다면 새로 생성. * 결과를 전역 변수 _preset_xml 로 대입 * */ private void MakeXML() { if (File.Exists(PresetFileName) == true) { _preset_xml = XElement.Parse(File.ReadAllText(PresetFileName)); return; } XElement xml = new XElement("root"); xml.Add( new XComment(string.Format("{0}{1}{2}", Environment.NewLine, " 0 으로 되어 있는 값을 입력하여야 합니다. 이름뒤에s가 붙은 배열 타입은 ',' 로 구분 합니다. \n (예시> <ints>30,20,10</ints> )" , Environment.NewLine) )); foreach (var item in _proplist) { xml.Add(new XElement(item.prop_name, "0")); } xml.Save(PresetFileName); Console.WriteLine("---create new preset xml---"); Console.WriteLine(xml.ToString()); _preset_xml = xml; } /* * XML 을 읽는 도중 누락된 항목이 발견되면 파일을 리셋. * */ private void ReadXML() { try { for (int i = 0; i < _proplist.Count; i++) { dynamic value = ValueParser(_proplist[i], _preset_xml.Element(_proplist[i].prop_name).Value);//Convert.ChangeType(_preset_xml.Element(_proplist[i].prop_name).Value, _proplist[i].prop_type); PropTypeValue newValue = _proplist[i]; newValue.prop_value = value; _proplist[i] = newValue; } } catch(NullReferenceException) { Console.WriteLine("ERR! > XML Parse Error "); File.Delete(PresetFileName); PresetXMLProgress(); } } /* * 속성의 type들을 검사 하여 실제 값(xml의 값)을 파싱 하여 대입. * */ private dynamic ValueParser(PropTypeValue target , string xml_value) { dynamic result = null; if (target.prop_isArray == true) { string[] raw_values = xml_value.Split(','); if (target.prop_type == typeof(int[])) { result = CovertArray<int>(raw_values); } else if (target.prop_type == typeof(System.Single[])) { result = CovertArray<float>(raw_values); } else if (target.prop_type == typeof(string[])) { result = CovertArray<string>(raw_values); } } else { result = Convert.ChangeType(xml_value, target.prop_type); } return result; } /* * 배열 타입의 속성을 위한 템플릿 유틸 메서드. * */ private T[] CovertArray<T>(string[] raw_values) { T[] arr = new T[raw_values.Length]; for (int i = 0; i < raw_values.Length; i++) { arr[i] = (dynamic)Convert.ChangeType(raw_values[i], typeof(T)); } return arr; } #endregion #endregion } }



Yamecoder 야매코더_
C# 2015.01.08 17:27
Powerd by Tistory, designed by criuce
rss