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);
}
}
}
}