PlayerPrefs + 反射 = 一个简单的自定义类数据类型存储读取管理器

这是一个Unity脚本,通过PlayerPrefsManager类实现了自定义数据类型的保存和读取。该类利用反射遍历对象字段,根据字段类型调用PlayerPrefs的相应方法进行数据存储和加载,支持基本类型、List和Dictionary等复杂数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PlayerPrefsManager

本文章只提供一种思路,思路来自于b站/泰课在线唐老狮unity课程。

using System;
using System.Collections;
using System.Reflection;
using UnityEngine;


/*
Editor:Runto
Version:1.0
The last time for modification:2021.2.13
Time for Creation:2021.2.13
*/
namespace demo
{
    /// <summary>
    /// PlayerPrefs管理类,主要负责自定义类数据保存,和读取。
    /// </summary>
    public class PlayerPrefsManager
    {
        private PlayerPrefsManager instance = new PlayerPrefsManager();

        public PlayerPrefsManager Instance => instance;
        //私有化构造函数以防止调用者自行创建管理类。
        private PlayerPrefsManager()
       {
           
       }
        /// <summary>
        /// PlayerPrefs自定义数据类型数据保存封装方法。
        /// </summary>
        /// <param name="data">自定义数据类型</param>
        /// <param name="keyName">自定义键名</param>
        public void SaveData(object data,string keyName)
        {
            //根据传进来的数据类型获取其type,以方便进而访问其所有字段。
            Type type = data.GetType();
            //获得当前自定义类型的所有字段。
            FieldInfo[] fieldInfos = type.GetFields();
            //拼接keyName用的临时变量,目的是为了保证数据保存的唯一性,防止数据覆盖。
            string key = "";
            //遍历字段数组
            foreach (var VARIABLE in fieldInfos)
            {
                //字符串拼接
                key = keyName + "_" + VARIABLE.FieldType.Name + "_" + VARIABLE.Name;
                //调用SaveValue方法来进一步判断字段类型,并存储
                SaveValue(VARIABLE.GetValue(data),key);
            }

        }
        /// <summary>
        /// 用于进一步判断传进来的字段类型,并存储
        /// </summary>
        /// <param name="value">传入的字段类型</param>
        /// <param name="keyName">完成拼接后的自定义键名</param>
       private void SaveValue(object value,string keyName)
       {
           //获得传入字段类型的type。
           Type type = value.GetType();
           //判断type的类型,并使用PlayerPrefs的set方法来存储。
           if (type == typeof(int))
           {
               PlayerPrefs.SetInt(keyName,(int)value);
           }
           else if (type == typeof(float))
           {
               PlayerPrefs.SetFloat(keyName,(float)value);
           }
           else if (type == typeof(string))
           {
               PlayerPrefs.SetString(keyName,value.ToString());
           }
           //如果type为List,这里使用IsAssignableFrom来判断父子关系,List继承自IList。
           else if (typeof(IList).IsAssignableFrom(type))
           {
               //将类型转为IList。
               IList tempList = value as IList;
               //存储List长度,存储长度是为了读取时知道读取多少个数据。
               PlayerPrefs.SetInt(keyName + "_CountNum",tempList.Count);
               //创立临时变量index,此变量用于拼接在keyName后来保证键唯一。
               int index = 0;
               foreach (var VARIABLE in tempList)
               {
                   //对于List的元素递归调用SaveValue方法来判断类型。
                   SaveValue(VARIABLE,keyName + index);
                   index++;
               }
           }
           //如果type为Dictionary类型,这里使用IsAssignableFrom来判断父子关系,Dictionary继承自IDictionary。
           else if (typeof(IDictionary).IsAssignableFrom(type))
           {
               //将类型转为IDictionary。
               IDictionary tempDictionary = value as IDictionary;
               //存储Dictionary长度,存储长度是为了读取时知道读取多少个数据。
               PlayerPrefs.SetInt(keyName + "_CountNum",tempDictionary.Count);
               //拼接变量index。
               int index = 0;
               //遍历字典的键,并递归调用SaveValue来进一步判断存储元素类型。
               foreach (var key in tempDictionary.Keys)
               {
                   //判断键的类型并存储。
                   SaveValue(key, keyName + "_key" + index);
                   //判断值的类型并存储。
                   SaveValue(tempDictionary[key],keyName + "_value" + index);
                   index++;
               }
           }
           //如果不是以上类型,则断定该字段类型为自定义类型,重复调用SaveData来重头判断。
           else
           {
               SaveData(value,keyName);
           }
       }
        /// <summary>
        /// 读取keyName的存储数据,我们会载内部新建一个对象,并将数值赋值到这个对象上返回。
        /// </summary>
        /// <param name="type">需要读取数据的数据类型</param>
        /// <param name="keyName">键名</param>
        /// <returns>返回一个指定类型的数据填充完毕的对象</returns>
       public object LoadData(Type type,string keyName)
       {
           //根据传进来的type实例化一个对象。
           object typrInstance = Activator.CreateInstance(type);
           //获得当前传入type的所有字段。
           FieldInfo[] fieldInfos = type.GetFields();
           //临时变量用于拼接指定键名。
           string key = "";
           //存储单个FieldInfo的临时变量,可以没有。
           FieldInfo fieldInfo;
           for (int i = 0; i < fieldInfos.Length;i++)
           {
               //为临时FieldInfo变量赋值。
               fieldInfo = fieldInfos[i];
               //拼接指定键值。
               key = keyName + "_" + fieldInfo.FieldType.Name + "_" + fieldInfo.Name;
               //调用当前FieldInfo的赋值方法。,参数部分1. 传入创建的对象 2. 调用LoadValue进一步判断字段类型并返回
               fieldInfo.SetValue(typrInstance,LoadValue(fieldInfo.FieldType, key));
           }
           return typrInstance;
       }
        /// <summary>
        /// LoadValue负责进一步判断字段类型并返回需要的数值。
        /// </summary>
        /// <param name="type">字段类型</param>
        /// <param name="keyName">当前字段的指定键值</param>
        /// <returns>返回当前字段应当存入的数据</returns>
       private object LoadValue(Type type,string keyName)
       {
           //判断type的类型,并使用PlayerPrefs的get方法来获取。
           if (type == typeof(int))
           {
               return PlayerPrefs.GetInt(keyName,0);
           }
           else if (type == typeof(float))
           {
               return PlayerPrefs.GetFloat(keyName,0);
           }
           else if (type == typeof(string))
           {
               return PlayerPrefs.GetString(keyName,"");
           }
           //如果当前字段类型为List类型,我们需要返回一个List对象。
           else if (typeof(IList).IsAssignableFrom(type))
           {
               //为了返回一个List对象,我们就要先创建一个对象。
               IList tempList = Activator.CreateInstance(type) as IList;
               //获得当前List的长度。
               int listCount = PlayerPrefs.GetInt(keyName + "_CountNum");
               for (int i = 0; i < listCount; i++)
               {
                   //遍历添加元素,其中元素的类型使用GetGenericArguments来获取。
                   //并使用LoadValue来进一步确定元素的类型。
                   //需要注意这里的 + i 对应SaveValue中的 + index。
                   tempList.Add(LoadValue(type.GetGenericArguments()[0], keyName + i));
               }
               return tempList;
           }
           //如果当前字段类型为Dictionary类型,我们需要返回一个Dictionary对象。
           else if (typeof(IDictionary).IsAssignableFrom(type))
           {
               //为了返回一个Dictionary对象,我们就要先创建一个对象。
               IDictionary tempDictionary = Activator.CreateInstance(type) as IDictionary;
               //获得当前Dictionary的长度。
               int dicCount = PlayerPrefs.GetInt(keyName + "_CountNum");
               //获取字典的泛型参数列表<>。
               Type[] genericArguments = type.GetGenericArguments();
               for (int i = 0; i < dicCount; i++)
               {
                   //遍历添加元素。
                   tempDictionary.Add(
                       LoadValue(genericArguments[0],keyName + i),
                       LoadValue(genericArguments[1],keyName + i)
                   );
               }
               return tempDictionary;
           }
           //如果当前字段类型为自定义类型,则重新调用LoadData来判断。
           else
           {
               return LoadData(type,keyName);
           }
       }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值