您的位置:首页 > 其它

ZGrid方格容器类的简单介绍

2018-06-14 08:59 218 查看

需求

大家都遇到有这种需求吧,类似一个网格的结构体,一般都会使用二维数组来实现。类似以下的代码:

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<T>,一般应该是同时实现这两个接口,这样可以保证可以使用IEnumerable接口进行统一的访问。比如,C#自带的List类就是同时实现了以上两个接口。当然这样的实现代码上会比较的繁琐。

2. 接口的显式实现与隐式实现,通俗的来讲,“显示接口实现”就是使用接口名称作为方法名的前缀;而传统的实现方式称之为:“隐式接口实现”。主要的区别是隐式接口实现,类和接口都可访问接口中方法。显式接口实现,只能通过接口访问。我的使用经验是隐式实现可以用来隐藏掉一些方法,又保证接口层的正常使用。本例中统一原则:支持Template参数的方法或者属性才使用public公有的显式实现,否则使用隐式实现。有关基础知识的介绍参考如下:

https://www.cnblogs.com/ben-zhang/archive/2012/12/18/2823455.html

3.有关索引器的实现

索引器跟属性很像,只不过他有一个this关键字紧跟着是[int index],来实现类似数组访问的封装。

4. internal关键字的使用

这里主要是使用它对一些迭代器的构造函数进行修饰,以保证其不会被在库外面构造使用。

Demo代码

有关索引器的使用:

ZGrid<int> grid = new ZGrid<int> (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<TestData> dataGrid = new ZGrid<TestData> (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<TestData>{
public int data;
}
代码库与Demo代码位置 :

https://github.com/bennychao/ZGrid

未完成功能:

目前列的遍历未实现完。

后续可以通过Editor在Unity里进行可视化的配置表结构。

持久化功能未现实完。


其它说明:

C# Type.IsAssignableFrom与IsSubClassOf的使用区别

typeof(Animal).IsAssignFrom(typeof(Dog))  他返回true的条件是 Dog类直接或间接的实现了Animal类;继承也可以typeof(Dog).IsSubClassOf(typeof(Animal))  他返回true的条件是Dog类是Animal的子类


阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: