频道栏目
首页 > 资讯 > C# > 正文

把c#中的Array说透

10-07-11        来源:[db:作者]  
收藏   我要投稿

1. 数组大局观

数组是一个引用类型,也就是意味着数组的内存分配在托管堆上,并且我们在栈上维护的是他的指针而并非真正的数组。

接下来我们分析下数组的元素,其中的元素无外乎是引用类型和值类型。

当数组中的元素是值类型时,,不同于int i;这样的代码。数组会根据数组的大小自动把元素的值初始化为他的默认值。例如:

static void Main(string[] args)
{
    int[] intArray = new int[3];
    foreach(int i in intArray)
    {
        Console.WriteLine(i);
    }
    DateTime[] dtArray = new DateTime[3];
    foreach (DateTime i in dtArray)
    {
        Console.WriteLine(i);
    }
}

结果如下:

image

当数组中的元素是引用类型时,实际上数组中的元素是一个指向对象实际内存空间的指针,占用4Bytes的空间。

2. 谈谈零基数组

从学C语言时起,相信老师就会对我们讲,数组的第一个索引是0,而不是1。但是在C#中,我们可以去构造一个非零基数组,在这一节,我们就来把这个说透。

在常规意义上,我们初始化一个数组,都默认是零基数组,这也使得数组成为了字符串后再一个初始化时特殊的类型。正如我们知道的一样,初始化一个字符串时,对应的IL指令是newstr,同样,初始化一个零基数组对应的IL指令是newarr。

当我们希望构造一个非零基数组时,我们可以以下的语句来做到:

static void Main(string[] args)
{
    Array intArr = Array.CreateInstance(typeof(Int32), new int[] { 5 }, new int[] { 1 });
    Console.WriteLine(intArr.GetValue(1).ToString());
    Console.WriteLine(intArr.GetValue(0).ToString());
}

 

得到的测试结果便如下:

image

于是便证明,我们初始化了一个非零基数组。此外,延伸一下,我们还应该通过这个记住以下两个方法:

static void Main(string[] args)
{
    Array intArr = Array.CreateInstance(typeof(Int32), new int[] { 5 }, new int[] { 1 });
    Console.WriteLine(intArr.GetLowerBound(0));
    Console.WriteLine(intArr.GetUpperBound(0));
}

 

得到的测试结果如下:

image

3. 谈谈效率问题

相信会有好多阴谋论者说,C#是个类型安全的语言,也就是意味着我循环时每次访问一次数组的元素,那么就要检查一次该索引是否会造成数组越界,于是就造成了一定的性能损失。那么在这里,我们就把这个问题说透。

我们在这里把数组分成零基数组,非零基数组,多维数组,交错数组四种情况来分别讨论这个问题。

零基数组是.NET中提倡使用的类型,并且初始化时提供了特殊的IL指令newarr则充分说明了他在.NET中的特殊性,自然.NET Framework也会为其提供很大的优化待遇。在循环访问数组时,如这样的代码:

static void Main(string[] args)
{
    int[] intArr = new int[5];
    for (int i = 0; i < 4; i++)
    { 
        //Some Method
    }
}

 

JIT编译器只会在循环开始之前检查一次4和intArr.GetUpperBound的大小关系,之后便不会对其进行干预。也就是说JIT编译器只对其检查一次安全,因此带来的性能损失是非常小的。

而对于非零基数组,我们来比较这样两段代码:

static void Main(string[] args)
{
    Array intArr = Array.CreateInstance(typeof(Int32), new int[] { 5 }, new int[] { 1 });
    Console.WriteLine(intArr.GetValue(1).ToString());
    Console.WriteLine(intArr.Length);
    //
    int[] intArr1 = new int[5];
    Console.WriteLine(intArr1[1]);
    Console.WriteLine(intArr1.Length);
}

 

其实两者创建的几乎是相同的数组,调用的也几乎是一样的方法,但是我们看下IL却会发现两者有着惊人的不同,首先是非零基数组的IL:

image

接下来是零基数组的:

image

我们可以发现,对于非零基数组中的大部分操作,.NET Framework都提供了对应的IL指令,我们也可以理解为.NET Framework为其提供了特殊的优化。

相关TAG标签
上一篇:.Net Framework: 字符串的驻留(String Interning)
下一篇:c# 反射机制
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站