zoukankan      html  css  js  c++  java
  • 基于DataTable, Json的额外序列化数据

      最近很多功能都涉及到用户设置相关的东西, 比如一个沙盘, 希望在无操作后3秒钟就自动进行相机自动旋转的操作, 代码很简单:

        public class XXX : MonoBehaviour
        {
            public float cameraRotateSpeed = 2f;
            public float waitTime = 3f;
    
            private float m_startRunningTime = 0.0f;
    
            void Awake(){ ResetStartRunningTime(); }
    
            private void ResetStartRunningTime()
            {
                m_startRunningTime = Time.realtimeSinceStartup + waitTime;
            }
    
            private void Update()
            {
                if(Time.realtimeSinceStartup > m_startRunningTime)
                {
                    var rotateAngle = cameraRotateSpeed * Time.deltaTime;
                    // ...
                }
            }
        }

      可是用户如果想自己能设置这个功能的变量时, 我们可以怎么做呢? 

    1. 搞个快捷键? 各种功能都有用户需求的话, 快捷键没那么多. 几十个快捷键没人能记住.

    2. 做个网页后台进行设置, 然后运行时从远程获取变量? 太麻烦, 而且依赖后台, 而且还是异步的.

    3. 在本地写个json文件配置表从里面读取? 对开发不友好, 维护麻烦.

    4. 做个UI面板运行时打开进行设置? 也是过于麻烦. 并且也需要存储数据.

      并且对于一个已经开发到一定程度的工程来说, 额外添加的这个需求不能在开发层面要求过多, 且不能影响原有功能的设计.

      结果还是从本地文件读取结果最靠谱, 假设上面的代码是单例或者使用时唯一,  那么就可以简单的做个容器来存放基本数据就行了:

    Dictionary<string, Common.DataTable> 

      string 就是成员变量名

      Common.DataTable 就是变量

      基本上是通过反射来获取和设置变量的了, 不过在写接口的时候, 希望能有硬连接而不是软连接:

        public class Person
        {
            public string Name { get; set; }
        }
        
        void Test(){
            var name = nameof(Person.Name);
        }

      这样即使代码修改了也会报错, 不过工程用的还是C#4的语法, 只能通过表达式的方式来得到, 因为有Linq扩展可以写成lambda的方式 :

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Linq.Expressions;
    using System.Reflection;
    
    namespace RuntimeData
    {
        using InternalModule.Common;
    
        internal class CompilerSerializedData
        {
            public string filePath { get; private set; }
            private bool inited = false;
    
            private Dictionary<string, Common.DataTable> m_datas = null;
    
            public CompilerSerializedData(string loadPath)
            {
                filePath = loadPath;
            }
    
            #region Main Funcs
            public Common.DataTable GetValue<T, TMem>(T host, Expression<System.Func<T, TMem>> key) where TMem : System.IConvertible
            {
                CheckInited();
                Common.DataTable data = 0;
                var memberExpression = key.Body as MemberExpression;
                if(memberExpression != null && memberExpression.Member != null)
                {
                    var memberInfo = memberExpression.Member;
                    if(m_datas.TryGetValue(memberInfo.Name, out data) == false)
                    {
                        data.SetData(ReflectionHelper.GetValueFromMemberInfo<TMem>(host, memberInfo));
                        m_datas[memberInfo.Name] = data;
                    }
                }
                return data;
            }
            public bool SetValue<T, TMem>(T host, Expression<System.Func<T, TMem>> key, TMem value) where TMem : System.IConvertible
            {
                CheckInited();
                var memberExpression = key.Body as MemberExpression;
                if(memberExpression != null && memberExpression.Member != null)
                {
                    var memberInfo = memberExpression.Member;
                    Common.DataTable data = 0;
                    data.SetData(value);
                    m_datas[memberInfo.Name] = data;
                    ReflectionHelper.SetValue(memberInfo, host, value);
                    return true;
                }
                return false;
            }
        
            public void SaveToFile() { Common.JsonHelper.SaveJsonToFile(m_datas, filePath); }
            #endregion
    
            #region Main Funcs -- Static Support
            public Common.DataTable GetValue<TValue>(string name, System.Func<TValue> defaultValueFunc) where TValue : System.IConvertible
            {
                CheckInited();
                Common.DataTable data = 0;
                if(m_datas.TryGetValue(name, out data) == false)
                {
                    data.SetData(defaultValueFunc.Invoke());
                    m_datas[name] = data;
                }
                return data;
            }
            public bool TryGetValue(string name, out Common.DataTable dataTable)
            {
                CheckInited();
                if(m_datas.TryGetValue(name, out dataTable))
                {
                    return true;
                }
                return false;
            }
            public void SetValue<TValue>(MemberInfo memberInfo, TValue value) where TValue : System.IConvertible
            {
                CheckInited();
                Common.DataTable data = 0;
                if(m_datas.TryGetValue(memberInfo.Name, out data) == false)
                {
                    data.SetData(value);
                    m_datas[memberInfo.Name] = data;
                }
            }
            public bool SetValue<T, TMem>(Expression<System.Func<TMem>> key, TMem value) where TMem : System.IConvertible
            {
                CheckInited();
                var memberExpression = key.Body as MemberExpression;
                if(memberExpression != null && memberExpression.Member != null)
                {
                    var memberInfo = memberExpression.Member;
                    Common.DataTable data = 0;
                    data.SetData(value);
                    m_datas[memberInfo.Name] = data;
                    ReflectionHelper.SetValue(memberInfo, default(T), value);
                    return true;
                }
                return false;
            }
            #endregion
    
            #region Help Funcs
            private void CheckInited()
            {
                if(false == inited)
                {
                    inited = true;
                    m_datas = Common.JsonHelper.LoadFromFile<Dictionary<string, Common.DataTable>>(filePath);
                    if(m_datas == null)
                    {
                        m_datas = new Dictionary<string, Common.DataTable>();
                    }
                }
            }
            #endregion
        }
    
        public static class CompilerSerializedDatas
        {
            private static readonly Dictionary<System.Type, CompilerSerializedData> dictionary = new Dictionary<System.Type, CompilerSerializedData>();
            public static Data.Common.PreCacheData<string> SavePathFolder = new Data.Common.PreCacheData<string>(() => { return Application.streamingAssetsPath + "/CompilerSerializedDatas"; });
    
            #region Main Funcs
            public static Common.DataTable GetCompilerSerializedValue<T, TMem>(this T instance, Expression<System.Func<T, TMem>> key) where TMem : System.IConvertible
            {
                var data = RequireCompilerSerializedData(typeof(T));
                Common.DataTable dataTable = data.GetValue(instance, key);
                return dataTable;
            }
    
            public static bool SetCompilerSerializedValue<T, TMem>(this T instance, Expression<System.Func<T, TMem>> key, TMem value) where TMem : System.IConvertible
            {
                var data = RequireCompilerSerializedData(typeof(T));
                return data.SetValue(instance, key, value);
            }
    
            public static void Save()
            {
                foreach(var data in dictionary.Values)
                {
                    data.SaveToFile();
                }
            }
            #endregion
    
            #region Main Funcs -- Static Support
            private static Dictionary<string, object> CompiledFunc = new Dictionary<string, object>();
            private static CompilerSerializedData RequireCompilerSerializedData(System.Type type)
            {
                CompilerSerializedData data = null;
                if(dictionary.TryGetValue(type, out data))
                {
                    return data;
                }
                RequireFolder(SavePathFolder.data);
                data = new CompilerSerializedData(string.Concat(SavePathFolder.data, "/", type.Name, ".json"));
                dictionary[type] = data;
                return data;
            }
            public static Common.DataTable GetCompilerSerializedValue<TValue>(Expression<System.Func<TValue>> expFunc) where TValue : System.IConvertible
            {
                var memberExpression = expFunc.Body as MemberExpression;
                if(memberExpression != null && memberExpression.Member != null)
                {
                    var memberInfo = memberExpression.Member;
                    var type = memberInfo.DeclaringType;
                    var data = RequireCompilerSerializedData(type);
                    Common.DataTable retVal = 0;
                    if(data.TryGetValue(memberInfo.Name, out retVal))
                    {
                        return retVal;
                    }
                    else
                    {
                        var value = CallExpression(expFunc);
                        data.SetValue(memberInfo, value);
                        retVal.SetData(value);
                    }
                    return retVal;
                }
                return 0;
            }
            public static bool SetCompilerSerializedValue<T, TValue>(Expression<System.Func<TValue>> expFunc, TValue value) where TValue : System.IConvertible
            {
                var data = RequireCompilerSerializedData(typeof(T));
                return data.SetValue<T, TValue>(expFunc, value);
            }
    
    
            private static bool MemberIsStatic(MemberInfo memberInfo)
            {
                var fieldInfo = memberInfo as FieldInfo;
                if(fieldInfo != null)
                {
                    return (fieldInfo.IsStatic);
                }
                else
                {
                    var propertyInfo = memberInfo as PropertyInfo;
                    if(propertyInfo != null)
                    {
                        return (propertyInfo.GetAccessors(true)[0].IsStatic);
                    }
                }
                return false;
            }
            private static System.Func<TValue> ExpressionToCall<TValue>(Expression<System.Func<TValue>> expFunc) where TValue : System.IConvertible
            {
                var key = expFunc.ToString();
                var call = CompiledFunc.TryGetNullableValue(key) as System.Func<TValue>;
                if(call == null)
                {
                    call = expFunc.Compile();
                    CompiledFunc[key] = call;
                }
                return call;
            }
            private static TValue CallExpression<TValue>(Expression<System.Func<TValue>> expFunc) where TValue : System.IConvertible
            {
                var call = ExpressionToCall(expFunc);
                return call.Invoke();
            }
            #endregion
    
            #region Help Funcs
            private static bool RequireFolder(string folderPath)
            {
                if(System.IO.Directory.Exists(folderPath) == false)
                {
                    var info = System.IO.Directory.CreateDirectory(folderPath);
                    return info.Exists;
                }
                return true;
            }
            #endregion
    
        }
    }

      通过代码注入添加了序列化方法, 最初的代码修改成:

        public class XXX : MonoBehaviour
        {
            public float cameraRotateSpeed = 2f;
            public float waitTime = 3f;
    
            private float m_startRunningTime = 0.0f;
    
            void Awake(){ ResetStartRunningTime(); }
    
            private void ResetStartRunningTime()
            {
                m_startRunningTime = Time.realtimeSinceStartup + (float)this.GetCompilerSerializedValue(self => self.waitTime);;
            }
    
            private void Update()
            {
                if(Time.realtimeSinceStartup > m_startRunningTime)
                {
                    var rotateAngle = (float)this.GetCompilerSerializedValue(self => self.cameraRotateSpeed) * Time.deltaTime;
                    // ...
                }
            }
        }

      这样在第一次运行之后, 就可以自动生成json文件了, 在启动时会自动读取文件设置, 既然是用户配置了, 那就不需要进行Set操作了, 不过以防万一也提供了Set操作:

        Tools.AutoCameraRoundController.instance.SetCompilerSerializedValue(self => self.waitTime, 100.0f);

      

  • 相关阅读:
    [转]利用docker进行java开发小demo
    markdown简介及语法
    Thinking in Java 之classpath理解
    docker官方windows安装
    Thinking in Java笔记之类及对象的初始化
    开发工具之play framework
    图解phpstorm常用快捷键
    Git相关
    Yii 1.1 cookie删不掉
    ajax跨域,这应该是最全的解决方案了
  • 原文地址:https://www.cnblogs.com/tiancaiwrk/p/13204268.html
Copyright © 2011-2022 走看看