[.net]数组
在C语言中,数组是比较简单,也使用比较多的一种基础的数据结构。常用的有一维数组,二维数组等。但是在C#中,使用最多的是List,Dictionary等一些集合类,因为用他们来操作同类型的数据,比数组更加方便。当然,C#的数组Array也通过实现一些接口,提供了访问和操作数据的一些便捷方法。而在C语言中,都是比较不容易实现或者使用不方便。这也就是C#作为一门面向对象的语言的好处,虽然由此带来的性能损失,尤其在做算法相关的问题的时候。但退一步讲,在如今硬件资源比较丰富的情况下,日常场景中,这种性能损失还是可以接受的。
接下来就聊聊C#种数组的那些事儿。
1.数组基本概念:
学习C#数组,首先需要明确一点,C#的数组是引用类型,即使数据基类型是值类型。作为引用类型,自然就会有类型对象指针,同步索引块,开销字段(overhead)等。
2.C#数组分类:
C#支持多维数组,也支持交错数组。并且他们都可以是非0基的。所谓非0基,是指数组第一个元素的索引不是从0开始。相应的,.Net CLR本身支持非0基数组,但是CLS规范规定所有数组都应该是0基数组,主要是为了兼容性和跨平台性,毕竟.Net家族除了C#还有VB,F#,C++(托管的)等语言。
概念解释:
一维0基数组:它又称为SZ数组(Single-dimension,Zero-based)或者向量。它的性能是最好的,因为编译器可以为他生成特殊的IL指令,实现JIT编译时候的优化,诸如以下指令(newarr,ldelem,ldelema,ldlen,stelem)。
使用如下:
int[] nums=new int[];
多维数组:又称矩形数组。从行列的角度来考虑的话,它的每一行的列数都必须时相等的。
使用如下:
int[,] nums=new int[,];
交错数组:用行列的角度来考虑的画,它的每一行的列数可以不相同。
int[][] nums=new int[][];
for(int i=;i<;i++)
{
nums[i]=new int[];//交错数组,各数组长度可不一样
}
交错数组这个定义方式,非常类似于C语言中的动态多维数组,在C语言中,我们常用以下方法来开辟动态的二维数组
int i;
int** nums=(int**) malloc(sizeof(int*)*);
for(i=;i<;i++)
{
nums[i]=(int*)malloc(sizeof(int)*);//这儿10是个实例,更具需求设置
}
二者的定义的确有相似的地方,但应该注意到他们在本质上时不同的。
3.数组索引:
数组的索引是数组的一个很核心的概念,因为我们对数组的访问一般都是通过索引来实现的,但对索引的不当处理,可能导致程序终止。
在C语言中,数组的索引控制都需要程序员来实现。如果发生索引超出数组范围,导致越界访问内存数据,甚至修改内存数据,这些都是不可察觉的,但无疑它为系统的奔溃留下了伏笔;
在C#中,由于CLR要保证数据的安全,CLR在运行代码的时候,如果检测到数组索引超出范围,会立即抛出System.IndexOutOfRangeException异常,从而终止错误的继续。当然也可以使用unsafe关键字,来忽略CLR的这种默认行为。
C#支持非0基数组,意味着数组可以的索引可以从非0的数开始,可以使用Arrary.CreatInstance()方法来创建下限非零数组。
4.数组元素初始化:
C#中为数组的初试化提供了一些甜蜜的语法糖。通常我们用以下几种方式来初始化一个数组。
int[] age=new int[]{,,};//大括号中的数据项称为数组初始化器(arrar,initializer)
var age=new int[]{,,}
var age=new [] {,,};//隐式类型推断
C#的隐式类型推断为创建一个数组提供了良好的支持。
5.数组转型:
类型转换是编程种很常见的一件事儿,我们把数组的类型转换又称为数组协变性(array convariance),在数组的转型的时候,需要满足以下几点条件:
①数组维数相同
②基类型存在隐式或显式类型转化
需要注意以下几点:
CLR不允许值类型数组转型为其他任何类型数组,但可以通过Array.Copy()变通实现。
Array.Copy():支持装箱,拆箱,以及加宽基元类型(比如int到double的转变),但它是浅拷贝,即如果数组基类型是引用类型,则只复制其引用。
System.Buffer.BliockCopy() 支持基元类型,但不具有转型能力
System.Array.ConstrainnedCopy() 不支持装箱、拆箱和向下类型转化(父类到子类的转换),但它是可靠的,数据安全的,要么成功复制一个数组,要么不会修改目标数组的任何数据
6.数组接口:
数组实现了一些常用的接口,相当于给数组插上了访问的翅膀。
System.Array数组基类实现了ICollection,IEnumerable,IList的非泛型版本,因为多维数组和非0基数组的原因,没有实现泛型版本(原因我也不太清楚)
CLR单独为一维0基数组实现了泛型版本接口,包括其基类型,但是System.ValueType和Object除外
7.数组做参数:
在使用中,不可避免的要把数组作为一个参数来使用,那么在使用的时候,需要注意以下几点
①作为方法实参时传递的是引用;
②如果需要返回一个数组类型的字段,建议使用Array.Copy(),返回一个从该字段复制的数组,这么做是为了保证OOP的开放封闭原则,因为通常字段是私有的,不能被外部方法修改的和使用的;
③对于返回数组的方法,如果数组元素个数为0,仍不建议返回一个null,而是应该返回一个空的数组,这么做的目的在于对于方法返回参数的使用这来说,免去了null判断,防止产生空引用异常的错误。
8.数组原理:
实际在CLR内部只支持两种类型数组:一维0基数组 和 下限未知的一维或多维数组;
一维0基数组:可以使用特殊的IL指令(newarr,ldelem,ldelema,ldlen,stelem),让编译器产生优化代码。比如索引检查发生的时刻提前,循环判断的提前的。
下限未知的一维或多维数组:每次数组访问前验证索引有效性
一些使用C#数组的建议:
①用交错数组代替矩形数组
②Unsafe关键字访问数组时可关闭索引上下限检查,支持常见的值类型和值类型结构(在fixed语句中)
③可以在线程栈上分配数组,利用stackalloc语句,类似于C语言alloca语句 ,条件是必须为一维0基数组,数组基类型为值类型
内容均来自于Jeffrey Richter的《CLR Via C#》第四版一书,以及个人总结和心得。学习深入了解C#,建议从这本书开始学习。
[.net]数组的更多相关文章
- javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈
Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...
- 探究javascript对象和数组的异同,及函数变量缓存技巧
javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...
- 编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议75~78)
建议75:集合中的元素必须做到compareTo和equals同步 实现了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必须实现的,它与equals方法 ...
- 了解PHP中的Array数组和foreach
1. 了解数组 PHP 中的数组实际上是一个有序映射.映射是一种把 values 关联到 keys 的类型.详细的解释可参见:PHP.net中的Array数组 . 2.例子:一般的数组 这里,我 ...
- JavaScript权威指南 - 数组
JavaScript数组是一种特殊类型的对象. JavaScript数组元素可以为任意类型,最大容纳232-1个元素. JavaScript数组是动态的,有新元素添加时,自动更新length属性. J ...
- JavaScript常见的五种数组去重的方式
▓▓▓▓▓▓ 大致介绍 JavaScript的数组去重问题在许多面试中都会遇到,现在做个总结 先来建立一个数组 var arr = [1,2,3,3,2,'我','我',34,'我的',NaN,NaN ...
- js:给定两个数组,如何判断他们的相对应下标的元素类型是一样的
题目: 给Array对象原型上添加一个sameStructureAs方法,该方法接收一个任意类型的参数,要求返回当前数组与传入参数数组(假定是)相对应下标的元素类型是否一致. 假设已经写好了Array ...
- javascript数组查重方法总结
文章参考地址:http://blog.csdn.net/chengxuyuan20100425/article/details/8497277 题目 对下列数组去重: var arr = ['aa', ...
- 掌握javascript中的最基础数据结构-----数组
这是一篇<数据结构与算法javascript描述>的读书笔记.主要梳理了关于数组的知识.部分内容及源码来自原作. 书中第一章介绍了如何配置javascript运行环境:javascript ...
- 小兔JS教程(四)-- 彻底攻略JS数组
在开始本章之前,先给出上一节的答案,参考答案地址: http://www.xiaotublog.com/demo.html?path=homework/03/index2 1.JS数组的三大特性 在J ...
随机推荐
- swift - tableview 滚动到指定位置
滚动一定要在 tableView.reloadData()之后进行 1. 默认 plain 模式 办法1. tableView.contentOffset.y = 0 办法2 tableView. ...
- Mac 终端使用 - 加密 1. MD5 2.Base64
1.MD5 打开终端 输入 :echo -n "加密内容" |md5 md5加密网站https://md5jiami.51240.com/ md5解密网站http://www.cm ...
- OpenGL.ProjectiveTextureMapping
1. 简介 https://developer.nvidia.com/content/projective-texture-mapping
- BZOJ 1345[BOI]序列问题 - 贪心 + 单调栈
题解 真的没有想到是单调栈啊. 回想起被单调栈支配的恐惧 最优情况一定是小的数去合并 尽量多的数,所以可以维护一个递减的单调栈. 如果加入的数比栈首小, 就直接推入栈. 如果加入的数大于等于栈首, 必 ...
- VS2010 MFC 使用GDI+给图片添加汉字
1.配置GDI+ VS2010自带GDI+,直接使用. (1)首先要添加头文件和库 #pragma comment( lib, "gdiplus.lib" ) #include & ...
- Devexpress VCL Build v2014 vol 14.2.6 发布
终于支持XE8 了.需要这么长时间吗? New Major Features in 14.2 What's New in VCL Products 14.2 Feature Highlights To ...
- 非常实用的windows运行打开服务命令
1.注册表-->regedit.exe 2.本地服务设置-->services.msc 3.远程桌面连接-->mstsc 4.检查windows版本-->winver 5.组策 ...
- 如何在Linux中统计一个进程的线程数(转)
方法一: /proc proc 伪文件系统,它驻留在 /proc 目录,这是最简单的方法来查看任何活动进程的线程数. /proc 目录以可读文本文件形式输出,提供现有进程和系统硬件相关的信息如 CPU ...
- ansible-playbook 主机变量1
hosts 配置后可以支持指定 端口,密码等其他变量 [root@10_1_162_39 host_vars]# ll total -rw-r--r-- root root May : hosts - ...
- JAVA开发常用计算机命令
系统常用命令 win+r > control (可进入控制面板,管理工具,服务) win+r > cmd > systeminfo (x86-based 指32位系统,x86-64 ...