通過反射將物件轉化為檔案,同時反向將檔案還原為物件(適用配置檔案讀寫)

koloumi發表於2017-06-09

檔案和物件的轉換

在寫程式中經常會使用到配置檔案,而且配置檔案的內容通常還是要賦值給物件,那麼每次如果都給一個特定的類去寫一套讀寫肯定是很不方便,由於工作上的需求所以順便寫了一個通用的只要提供物件即可(ps:要儲存的內容只能是基礎型別,如果物件中有引用型別的將不會被儲存,不過大部分配置檔案的內容應該都是基礎型別就OK了)
    /// <summary>
    /// 把物件轉化為檔案的內容
    /// </summary>
    public static class StreamClassToFile
    {
        /// <summary>
        /// 每個物件的分隔
        /// </summary>
        private static string ClassLog = "`------------------------`" + RowEndLog;
        /// <summary>
        /// 單個物件的標誌
        /// </summary>
        private static string SingleHandLog = "Single\n";
        /// <summary>
        /// 多個物件的標誌
        /// </summary>
        private static string MoreleHandLog = "More\n";
        /// <summary>
        /// 分隔符
        /// </summary>
        private static string CenterLog = "|~|";
        /// <summary>
        /// 行結尾符
        /// </summary>
        private static string RowEndLog = "\n";
        /// <summary>
        /// 欄位開始標識
        /// </summary>
        private static string FieldStartLog = "<|Fields>";
        /// <summary>
        /// 欄位結束標識
        /// </summary>
        private static string FieldEndLog = "<Fields|>";
        /// <summary>
        /// 開始物件標識
        /// </summary>
        private static string ObjectStartLog = "<|Object>";
        /// <summary>
        /// 結束物件標識
        /// </summary>
        private static string ObjectEndLog = "<Object|>";
        /// <summary>
        /// 將物件儲存到檔案
        /// </summary>
        /// <param name="obj"></param>
        public static void ObjectToFile(object obj, string fileName)
        {
            string saveString = SingleHandLog;
            saveString += ObjectToString(obj);
            using (Stream stream = new FileStream(fileName, FileMode.Create))
            {
                byte[] data = Encoding.UTF8.GetBytes(saveString);
                stream.Write(data, 0, data.Length);
            }
        }
        /// <summary>
        /// 將物件轉換為字串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static string ObjectToString(object obj)
        {
            StringBuilder saveString = new StringBuilder();
            Type type = obj.GetType();
            saveString.Append(ObjectStartLog);
            saveString.Append(type.Name + RowEndLog);
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
            ///儲存欄位
            saveString.Append(FieldStartLog + RowEndLog);
            foreach (var item in fields)
            {
                if (IsBasisType(item.FieldType))
                    saveString.Append(item.FieldType.Name + CenterLog + item.Name + CenterLog + item.GetValue(obj).ToString() + CenterLog + RowEndLog);
            }
            saveString.Append(FieldEndLog + RowEndLog);
            saveString.Append(ObjectEndLog);
            return saveString.ToString();
        }
        /// <summary>
        /// 將檔案中的資料讀取並且還原為一個物件(只支援有空建構函式的物件)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static T FileToObject<T>(string fileName)
        {
            StringBuilder readString = new StringBuilder();
            using (StreamReader stream = new StreamReader(fileName, Encoding.UTF8))
            {
                readString.Append(stream.ReadToEnd());
            }
            int logindex = readString.ToString().IndexOf(SingleHandLog);
            if (logindex == -1 || logindex > SingleHandLog.Length)
            {
                return default(T);
            }
            return GetObjectByString<T>(readString.ToString());
        }
        /// <summary>
        /// 根據類字串組裝類
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="readString"></param>
        /// <returns></returns>
        private static T GetObjectByString<T>(string readString)
        {
            try
            { 
                Type type = typeof(T);
                //type.GetConstructor( BindingFlags.Public, new );
                object readobj = Activator.CreateInstance(type);

                FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
                ListData fieldsData = GetFieldValue(readString);///解析所有欄位獲取值

                foreach (var item in fields)
                {
                    if (IsBasisType(item.FieldType))
                    {
                        item.SetValue(readobj, GetValue(fieldsData[item.Name], item));
                    }
                }
                return (T)readobj;
            }
            catch (Exception ex)
            {
                return default(T);
            }
        }
        /// <summary>
        /// 檢測是否是基礎型別,只支援基礎型別
        /// </summary>
        /// <param name="typeName">型別名</param>
        /// <returns>true為是,false為否</returns>
        public static bool IsBasisType(Type type)
        {
            if (type.IsPrimitive || type.Name == "string" || type.Name == "String" || type.Name == "Guid")
                return true;
            else
                return false;
        }
        /// <summary>
        /// 獲取獲取欄位的資料
        /// </summary>
        /// <param name="info">欄位資訊</param>
        /// <param name="file">總檔案流</param>
        /// <returns></returns>
        private static ListData GetFieldValue(string file)
        {
            ListData data = new ListData();
            string fieldstring = file.ToString();
            int startIndex = fieldstring.IndexOf(FieldStartLog);
            int endIndex = fieldstring.IndexOf(FieldEndLog);
            fieldstring = fieldstring.Substring((startIndex + FieldStartLog.Length), (endIndex - startIndex - FieldStartLog.Length));
            string[] fields = fieldstring.Split(RowEndLog.ToCharArray());
            string[] values;
            foreach (var item in fields)
            {
                values = item.Split(CenterLog.ToCharArray());
                if (values.Length <= 1)
                    continue;
                data.Add(values[CenterLog.Length], values[CenterLog.Length * 2]);
            }
            return data;
        }
        /// <summary>
        /// 根據欄位的型別變成對應的欄位的值
        /// </summary>
        /// <param name="value">欄位的值的字串</param>
        /// <param name="type">欄位的型別</param>
        /// <returns></returns>
        private static object GetValue(string value, FieldInfo type)
        {
            try
            {
                TypeCode code = GetEnumTypeByString<TypeCode>(type.FieldType.Name);
                if (code == TypeCode.Empty && type.FieldType.Name == "Guid")
                    return new Guid(value);
                return Convert.ChangeType(value, code);
            }
            catch(Exception ex)
            {
                if(type.Name == "string")
                {
                    return value;
                }
                else
                {
                    return null;
                }
            }
        }
        /// <summary>
        /// 批量讀取
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objs"></param>
        /// <param name="fileName"></param>
        public static List<T> FileToObjects<T>( string fileName)
        {
            StringBuilder readString = new StringBuilder();
            using (StreamReader stream = new StreamReader(fileName, Encoding.UTF8))
            {
                readString.Append(stream.ReadToEnd());
            }
            int logindex = readString.ToString().IndexOf(MoreleHandLog);
            if (logindex == -1 || logindex > MoreleHandLog.Length)
            {
                return default(List<T>);
            }
            List<T> objs = new List<T>();
            ////string[] classtrings = readString.ToString().Split(ClassLog.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            string[] classtrings = Regex.Split(readString.ToString(), ClassLog);
            int index = 0;
            foreach (string item in classtrings)
            {
                //if(index ++ % ClassLog.Length ==0 && index != classtrings.Length)
                if(item.Length != 0)
                    objs.Add(GetObjectByString<T>(item));
            }
            return objs;
        }
        /// <summary>
        /// 將多個物件儲存到檔案
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objs"></param>
        /// <param name="fileName"></param>
        public static void ObjectsToFile<T>(List<T> objs, string fileName)
        {
            StringBuilder saveStrings = new StringBuilder();
            saveStrings.Append(MoreleHandLog);
            foreach (object item in objs)
            {
                saveStrings.Append(ObjectToString(item));
                saveStrings.Append(ClassLog);
            }
            using (Stream stream = new FileStream(fileName, FileMode.Create))
            {
                byte[] data = Encoding.UTF8.GetBytes(saveStrings.ToString());
                stream.Write(data, 0, data.Length);
            }
        }
        /// <summary>
        /// 根據name檢索對應列舉
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public static T GetEnumTypeByString<T>(string name)
        {
            foreach (T item in Enum.GetValues(typeof(T)))
            {
                if(item.ToString() == name)
                {
                    return item;
                }
            }
            return default(T);
        }
        /// <summary>
        /// 線性插值
        /// </summary>
        /// <param name="start">起始值</param>
        /// <param name="end">終止值</param>
        /// <param name="lerp">插值</param>
        /// <returns></returns>
        public static double Lerp(double start, double end, double lerp)
        {
            lerp = lerp < 0 ? 0 : lerp;
            lerp = lerp > 1 ? 1 : lerp;
            return (end + start) * lerp;
        } 
        /// <summary>
        /// 根據T型別的列舉的具體V型別的值獲取對應的列舉值
        /// </summary>
        /// <typeparam name="T">列舉型別</typeparam>
        /// <typeparam name="V">列舉型別繼承的型別</typeparam>
        /// <param name="value">對應的值</param>
        /// <returns>對應的列舉值</returns>
        public static T GetEnumTypeByType<T, V>(V value)
        {
            try
            {
                TypeCode code = GetEnumTypeByString<TypeCode>(value.GetType().Name);
                foreach (T item in Enum.GetValues(typeof(T)))
                {
                    if (Convert.ChangeType(value, code).Equals(value))
                    {
                        return item;
                    }
                }
                return default(T);
            }
            catch (Exception)
            {
                return default(T);
            }

        }
    }

    internal class ListData
    {
        /// <summary>
        /// 資料
        /// </summary>
        private Dictionary<string, string> _data = new Dictionary<string, string>();
        /// <summary>
        /// 根據名稱獲取值
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string this[string name]
        {
            get
            {
                if(_data.Keys.Contains(name))
                {
                    return _data[name];
                }
                return "";
            }
        }
        /// <summary>
        /// 資料
        /// </summary>
        public Dictionary<string, string> Data
        {
            get
            {
                return _data;
            }
        }
        /// <summary>
        /// 新增一條資料
        /// </summary>
        /// <param name="name">屬性名稱</param>
        /// <param name="value">屬性的值</param>
        public void Add(string name, string value)
        {
            _data.Add(name, value);
        }
    }
使用的時候只要呼叫StreamClassToFile.ObjectToFile(物件, 檔名稱包含路徑和名稱);
同時還有ObjectsToFile支援List<T>型別的轉化。
反向的是public static T FileToObject<T>(string fileName)
同樣支援返回List<T>的物件組

相關文章