大家都遇到有这种需求吧,类似一个网格的结构体,一般都会使用二维数组来实现。类似以下的代码:
int[][] array = new int[2][] { new int[] {0,11,12 }, new int[] {1,2,3,4,5 }};
for (int i = 0; i < array.Length; i++) {
foreach (int j in array[i]) {
Debug.Log ("i " + i + " j " + j);
}
}
这里二维数组在使用会带来一些不方便,或者说是问题:
1. 只能通过索引,进行某一行元素的遍历,不支持某以列的从上到下的遍历,同时遍历方向也是固定的,不能从反向进行遍历。
2. 内存会有一定多余,无法间隔定义元素,比如以上代码中,有两行,第一行三个数字,不能在0和11间插入几个“空”,只能使用0等进行占位,如果只是数字还好一些,如果元素是一个复杂的结构,就会多出很多无用的空间。特别是在设计一些稀疏表数据时,会消耗很大的内存资源。
3. 最致命的一点是,数组无法后期进行补充数据项,只能一开始就定义表的分布。当然如果使用单个List,索引的管理就会很繁琐。
4. 不支持一些高级功能,比如表合并、子表、持久化等功能。
本文的目的是提供一个类似二维数组的操作的封装类,并解决以上问题。还是先上类图:
主要思路是使用Dictionary作为元素的基础存储结构,通过行、列组合成为索引值也就是字典的Key,元素作为Value进行保存,这样可以进行空间的有效利用,不会出现“空”块,访问速度也快。
主要类说明:
IZGrid定义表的基础行为,包括增/删/改/查等操作
IZGridIterator用于定义索引器,通过[i, j]和[i]的形式,进行所有元素和行的遍历,同时也支持Foreach进行遍历
IZRowIterator用定义行或者列元素的遍历,同样支持Foreach
IZGridIndexable用于定义支持可以获取到i, j(行、列)的元素模板类,自定义的元素类可以从其继承就可以访问元素所在的行、列属性了。
共通技术点说明:
1. IEnumerable接口的使用以上前三个接口是都是从有IEnumerable继承,以获取遍历功能的支持,关IEnumerable的使用方法,其中Unity有其特别的地方,可以参考以下文章
https://blog.csdn.net/qq_30695651/article/details/79105332
有关IEnumerable的其它C#语法的说明
https://blog.csdn.net/chengmin1989/article/details/63255908
IEnumerable还有一个带模板的定义IEnumerable
2. 接口的显式实现与隐式实现,通俗的来讲,“显示接口实现”就是使用接口名称作为方法名的前缀;而传统的实现方式称之为:“隐式接口实现”。主要的区别是隐式接口实现,类和接口都可访问接口中方法。显式接口实现,只能通过接口访问。我的使用经验是隐式实现可以用来隐藏掉一些方法,又保证接口层的正常使用。本例中统一原则:支持Template参数的方法或者属性才使用public公有的显式实现,否则使用隐式实现。有关基础知识的介绍参考如下:
https://www.cnblogs.com/ben-zhang/archive/2012/12/18/2823455.html
3.有关索引器的实现
索引器跟属性很像,只不过他有一个this关键字紧跟着是[int index],来实现类似数组访问的封装。
4. internal关键字的使用
这里主要是使用它对一些迭代器的构造函数进行修饰,以保证其不会被在库外面构造使用。
有关索引器的使用:
ZGrid grid = new ZGrid (10, 10);
//数据索引器的使用
//赋值操作
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
grid[i, j] = j * 10 + i;
}
}
Debug.Log ("grid size is " + grid.Width);
//遍历所有的数据项
foreach (var i in grid.Datas) {
//Debug.Log ("item is " + i);
}
//行、列索引器的使用
foreach (var i in grid.Rows) {
foreach (var d in i) {
//Debug.Log ("row is " + d);
}
}
子表的使用
//获取子网格
var subGrid = grid.SubGrid(5, 5, 3, 3);
foreach (var i in subGrid) {
Debug.Log ("Sub item is " + i);
}
支持C# Ling操作
//Ling操作
var items = subGrid.Datas.Where (a => a > 60).ToList ();
foreach (var i in items) {
Debug.Log ("Select item is " + i);
}
Inderable的支持
ZGrid dataGrid = new ZGrid (3, 3);
dataGrid[2, 2] = new TestData ();
dataGrid[2, 2].data = 10;
Debug.Log ("dataGrid[2, 2] col = " + dataGrid [2, 2].Col);
可以获取到元素所对应的行、列信息,元素类的定义如下:
public class TestData : ZGridIndexable{
public int data;
}
代码库与Demo代码位置 :
https://github.com/bennychao/ZGrid
未完成功能:
目前列的遍历未实现完。
后续可以通过Editor在Unity里进行可视化的配置表结构。
持久化功能未现实完。
其它说明:
typeof(Animal).IsAssignFrom(typeof(Dog)) 他返回true的条件是 Dog类直接或间接的实现了Animal类;继承也可以
typeof(Dog).IsSubClassOf(typeof(Animal)) 他返回true的条件是Dog类是Animal的子类