第一次发论坛,随手写了点,写的稀烂,敬请谅解
前置知识
该Excel转换二进制数据插件是基于唐老狮数据持久化二进制的实践项目的小改进。
没看过的同学先学习这个 ——> 数据持久化二进制
改进后的插件
改进了什么?
自动读取
最大的改进是为BinaryDataManager
拓展了自动读取方法
在第一次调用BinaryDataManager
的Instance
时,就会自动的调用一个LoadAllTable
方法,该方法可以读取转换过的二进制数据的表格名,通过表格名获取类型来遍历执行LoadTable
方法
具体如何实现可以写在下面(嗯,其实我只是想来推销我刚学到的东西的)
为数据结构类和数据容器类添加基类
在通过Excel表格生成数据容器类和数据结构类时,分别让它们继承DataContainer
和DataStructure
,这两个类本质上没有实现什么,但是通过里氏替换原则,我们可以更方便的管理数据结构类和数据容器类,降低装箱拆箱风险,同时可以添加泛型约束让IDE为我们做类型检查。
// BinaryDataManager部分内部代码
public class BinaryDataManager
{
// 装载各个数据容器的字典,TValue参数从object改进为自定义的DataContainer基类
public Dictionary<string, DataContainer> tableDict = new Dictionary<string, DataContainer>();
// 读取表格二进制数据方法
public void LoadTable<TContainer, TDataClass>() where TContainer : DataContainer where TDataClass : DataStructure
{
//代码内容略,和唐老狮原版的内容一致
//唯一的改进是约束了泛型参数的填入,使其更加安全
}
// 获取表格数据方法
public T GetTable<T>() where T : DataContainer
{
//代码内容略,和唐老狮原版的内容一致
//唯一的改进是约束了泛型参数的填入,使其更加安全
}
}
//数据容器类基类
public abstract class DataContainer { }
//数据结构类基类
public abstract class DataStructure { }
还有其他的一点改变,例如修改各种名字,取消了对.xls文件的读取(不知道为什么读不了,干脆砍了)
自动读取的实现
- 在通过Excel表格生成二进制数据时,将成功生成了二进制数据的表格名字记录下来,将这些表格名也生成一个二进制文件,供接下来
BinaryDataManager
读取,
- 之后开始运行,场景上的某个对象第一次调用
BinaryDataManager
的GetTable
方法时,先读取上一步记录了生成二进制数据的表格名的文件,获取到所有的表格名。
- 通过得到的表格名,我们进一步的获取到表格名对应的数据结构类类型,数据容器类类型。
- 通过得到的两个类型,调用
BinaryDataManager
的GetTable
方法读取数据,遍历所有被记录的表格名,读取所有生成的二进制数据。
前两点都不是什么问题,涉及到的知识都基本就限于我们学过的二进制持久化知识
实现这个思路的最大难点是三四步,也就是:如何通过表格名或者说是类型名来获取类型,以及如何通过类型变量调用
通过类型名字符串获取类型
我们需要使用Type.GetType()
方法,参数传入类型名字符串,字符串后面最好再加上该类型定义所在的程序集
示例:
// typeName是类型的名字,而", Assembly-CSharp"是加上类型的定义所在的程序集
// 该方法返回的是字符串转化出来类型变量
Type dataClassType = Type.GetType(typeName + ", Assembly-CSharp");
通过类型变量,利用反射,调用泛型方法
仅仅得到类型变量,我们是无法直接将他们作为泛型参数调用泛型方法的,这样我们要如何调用泛型方法呢
我们需要通过反射的知识,通过MethodInfo.MakeGenericMethod()
构建一个已经传入了泛型参数的MethodInfo
,再Invoke
,就可以实现类型变量调用泛型方法。
可能怎么说难以理解,让我们通过下面的例子来理解他:
public class BinaryDataManager
{
private static BinaryDataManager instance;
public static BinaryDataManager Instance
{
get
{
// 如果是第一次调用,先将实例化单例对象,再让该自动单例执行读取所有二进制数据表格的方法
if (instance == null)
{
instance = new BinaryDataManager();
instance.LoadAllTable();
}
return instance;
}
}
public void LoadAllTable()
{
//假设之前我们通过读取文件得到了数据结构类型名的字符串
string tableName = "PlayerInfo";
//通过字符串转化得到类型变量
Type dataClassType = Type.GetType(tableName + ", Assembly-CSharp");
Type dataContainerType = Type.GetType(tableName + "Container" + ", Assembly-CSharp");
//现在我们要通过这两个类型变量调用下面的LoadTable泛型方法
//先获取到BinaryDataManager的LoadTable的MethodInfo
MethodInfo method = typeof(BinaryDataManager).GetMethod("LoadTable");
//通过MakeGenericMethod()方法,传入两个类型变量,构建一个传入了泛型参数的MethodInfo
MethodInfo genericMethod = method.MakeGenericMethod(dataContainerType, dataClassType);
//在执行这个最终已经得到类型参数的MethodInfo,执行方法的对象是instance也就是本类的单例对象,第二个参数传入null(因为本来就是无参方法)
genericMethod.Invoke(instance, null);
}
public void LoadTable<TContainer, TDataClass>() where TContainer : DataContainer where TDataClass : DataStructure
{
//代码内容略
}
}
最终拓展的代码如下:
// BinaryDataManager部分内部代码
public class BinaryDataManager
{
private static BinaryDataManager instance;
public static BinaryDataManager Instance
{
get
{
// 如果是第一次调用,先将实例化单例对象,再让该自动单例执行读取所有二进制数据表格的方法
if (instance == null)
{
instance = new BinaryDataManager();
instance.LoadAllTable();
}
return instance;
}
}
// 根据上次Excel生成二进制数据时记录的表格名列表文件,加载所有的表格内容
public void LoadAllTable()
{
// 读取上次Excel生成二进制数据时记录的表格名列表文件,若文件不存在则直接返回
// DATA_BINREY_PATH 是存储Excel转化为二进制数据文件的路径
// ALL_EXCELTABLE_BINARYDATA_LIST_FILENAME 是记录了生成二进制数据的表格名的文件的文件名
if (!File.Exists(DATA_BINREY_PATH + ALL_EXCELTABLE_BINARYDATA_LIST_FILENAME))
return;
using FileStream fileStream = File.Open(DATA_BINREY_PATH + ALL_EXCELTABLE_BINARYDATA_LIST_FILENAME,
FileMode.Open,
FileAccess.Read);
// 先读取记录了多少条数据
byte[] bytes = new byte[4];
int index = fileStream.Read(bytes, 0, 4);
int count = BitConverter.ToInt32(bytes, 0);
// 读取出来的数据数如果大于0,就开始循环读取表格名,再通过表格名加载二进制数据
if (count <= 0) return;
int strBytesLength;
byte[] strBytes;
string tableName;
for (int i = 0; i < count; i++)
{
// 先读取字符串字节数组长度,再根据长度读取字符串字节数组,再将其转化为列表名
index = fileStream.Read(bytes, 0, 4);
strBytesLength = BitConverter.ToInt32(bytes, 0);
strBytes = new byte[strBytesLength];
index = fileStream.Read(strBytes, 0, strBytesLength);
tableName = Encoding.UTF8.GetString(strBytes);
// 根据列表名获取类型变量,参数为列表名加上默认程序集
Type dataClassType = Type.GetType(tableName + ", Assembly-CSharp");
Type dataContainerType = Type.GetType(tableName + DataContainer.ALL_DataContainerClass_SuffixName + ", Assembly-CSharp");
// 通过反射,将类型变量作为泛型参数传入到该单例对象的LoadTable<>()方法内
typeof(BinaryDataManager).GetMethod("LoadTable").MakeGenericMethod(dataContainerType, dataClassType).Invoke(instance, null);
}
}
}
完