反射知识补充
判断一个类型的对象是否可以让另一个类型为自己分配空间(父类子类):
public class Father
{
}
public class Son : Father
{
}
...
Type ftype = typeof(Father);
Type stype = typeof(Son);
//调用者通过该方法进行判断是否可以通过传入的类型为自己分配空间
if(ftype.IsAssignableFrom(stype))
{
print("可以装");
Father f = Activator.CreateInstance(stype) as Father;
print(f);
}
else
{
print("不能装");
}
通过反射获得泛型类型
List<string>list = new List<string>();
Type listType = list.GetType();
Type[] types = listType.GetGenericArguments();
print(types[0]);
Dictionary<int,string> dic = new Dictionary<int,string>();
Type dicType= dic.GetType();
types = dicType.GetGenericArguments();
print(types[0]);
print(types[1]);
需求分析
项目实践
类的基本信息(单例模式)
public class PlayerPrefsDataMgr
{
private static PlayerPrefsDataMgr instance = new PlayerPrefsDataMgr();
public static PlayerPrefsDataMgr Instance
{
get { return instance; }
}
private PlayerPrefsDataMgr()
{
}
}
存储函数
public void SaveData(object obj, string keyName)//第一个参数为要存储的数据对象,第二个为该对象的唯一key
{
}
读取函数
public object LoadData(Type type, string keyName)
{
//不用object对象传入 而使用 Type传入
//主要目的是节约一行代码(在外部)
//若要传入object进行修改则必须在外部先创建一个object
//现在只用传入一个Type typeof(Player)然后我在内部动态创建一个对象再返回
return null;
}
反射存储数据
结合反射常用数据存储
定义PlayerInfo类以及PlayerPrefsDataMgr的调用
public class PlayerInfo
{
public int age=10;
public string name="啊啊啊";
public float height=1.2f;
public bool sex = true;
}
void Start()
{
PlayerInfo p = new PlayerInfo();
PlayerPrefsDataMgr.Instance.SaveData(p, "Player1");
}
具体两个函数
public void SaveData(object obj, string keyName)
{
Type type = obj.GetType();
FieldInfo[] infos = type.GetFields();
for(int i = 0; i < infos.Length; i++)
{
Debug.Log(infos[i]);
}
string saveKeyName;
FieldInfo info;
for (int i = 0;i < infos.Length; i++)
{
info = infos[i];
info = infos[i];
saveKeyName = keyName + "_" + type.Name + "_" + info.FieldType.Name + "_" + info.Name;
Debug.Log(saveKeyName);
SaveValue(info.GetValue(obj), saveKeyName);
}
}
private void SaveValue(object value, string keyName)
{
//根据数据类型不同用不同API来存储
Type fieldType = value.GetType();
if (fieldType == typeof(int))
{
Debug.Log("存储int" + keyName);
PlayerPrefs.SetInt(keyName, (int)value);
}else if(fieldType == typeof(float))
{
Debug.Log("存储float" + keyName);
PlayerPrefs.SetFloat(keyName, (float)value);
}else if (fieldType == typeof(string))
{
Debug.Log("存储string" + keyName);
PlayerPrefs.SetString(keyName, value.ToString());
}else if( fieldType == typeof(bool))
{
Debug.Log("存储bool" + keyName);
PlayerPrefs.SetInt(keyName, (bool)value ? 1 : 0);
}
}
结合反射List数据类型存储
List继承IList,IList不是泛型,就可以用父子类的判断函数来判断,即判断是不是IList的子类
else if(typeof(IList).IsAssignableFrom(fieldType))
{
IList list = value as IList;
PlayerPrefs.SetInt(keyName,list.Count);
int index = 0;
foreach (object obj in list)
{
SaveValue(obj, keyName+index);
index++;
}
}
结合反射Dictionary数据类型存储
与list相似
else if (typeof(IDictionary).IsAssignableFrom(fieldType))
{
IDictionary dict = value as IDictionary;
PlayerPrefs.SetInt(keyName, dict.Count);
int index = 0;
foreach(object key in dict.Keys)
{
SaveValue(key, keyName+"_key_"+index);
SaveValue(dict[key], keyName + "_value_" + index);
index++;
}
}
结合反射自定义类成员存储
else
{
SaveData(value, keyName);
}
调用上一层函数,SaveData可以再次对该数据类型进行拆分
例如
public class ItemInfo
{
public int id;
public int num;
public ItemInfo()
{
}
public ItemInfo(int id,int num)
{
this.id = id;
this.num = num;
}
}
class ...{
public List<ItemInfo> itemList = new List<ItemInfo>() { new ItemInfo(1, 10), new ItemInfo(2, 20) };
public Dictionary<int ,ItemInfo> ItemDic = new Dictionary<int, ItemInfo>()
{
{3,new ItemInfo(3,30) },
{4,new ItemInfo(4,40) }
};
}
会对这个类中的List、Dictionary再进行细分存储
反射读取数据
结合反射读取常用数据类型
private object LoadValue(Type fieldType, string keyName)
{
//根据字段类型来判断使用哪个API
if(fieldType == typeof(int))
{
return PlayerPrefs.GetInt(keyName,0);
}else if(fieldType == typeof(float))
{
return PlayerPrefs.GetFloat(keyName, 0);
}
else if( fieldType == typeof(string))
{
return PlayerPrefs.GetString(keyName, "");
}
else if(fieldType == typeof(bool))
{
return PlayerPrefs.GetInt(keyName, 0) == 1 ? true : false;
}
return null;
}
结合反射读取List数据类型
要用到取泛型函数类型的那个函数
else if (typeof(IList).IsAssignableFrom(fieldType))
{
int count = PlayerPrefs.GetInt(keyName, 0);
IList list = Activator.CreateInstance(fieldType) as IList;
for(int i = 0; i < count; i++)
{
list.Add(LoadValue(fieldType.GetGenericArguments()[0], keyName + i));
}
return list;
}
结合反射读取Dictionary数据类型
else if (typeof(IDictionary).IsAssignableFrom(fieldType))
{
int count = PlayerPrefs.GetInt(keyName, 0);
IDictionary dic = Activator.CreateInstance(fieldType) as IDictionary;
Type[] ts = fieldType.GetGenericArguments();
for (int i=0; i < count; i++)
{
dic.Add(LoadValue(ts[0],keyName +"_key_"+ i), LoadValue(ts[1], keyName + "_value_" + i));
}
return dic;
}
结合反射读取自定义数据类型
else
{
return LoadData(fieldType, keyName);
}
最终测试
首先删除之前保存的数据
PlayerPrefs.DeleteAll();
写数据:
PlayerInfo p = PlayerPrefsDataMgr.Instance.LoadData(typeof(PlayerInfo), "Player1") as PlayerInfo;
p.age = 14;
p.name = "啊啊啊";
p.height = 100;
p.sex = true;
p.itemList.Add(new ItemInfo(1,99));
p.itemList.Add(new ItemInfo(2, 199));
p.ItemDic.Add(3, new ItemInfo(3, 1));
p.ItemDic.Add(4, new ItemInfo(4, 2));
PlayerPrefsDataMgr.Instance.SaveData(p, "Player1");
第一次执行时LoadData后p中是没有内容的,第一次执行完毕会将数据存储, 第二次执行时就能够读出来。
加密思路
1.找不到:
多层文件夹包裹,将名字辨识度降低,但是对于PlayerPrefs不适用(位置固定改不了)
2.看不懂:
加密Key和Value
3.解不出
防止获得加密规则
生成资源包
右键PlayerPrefsDataMgr点击Export package。