数组


1.同一类型和不同类型的多个对象

    需要使用同一类型的多个对象则使用集合和数组。

    Array类为数组元素排序和过滤提供了多个方法,使用枚举器,可以迭代数组所有元素。

2.简单数组

数组的声明

因为数组是引用类型,所以只要声明之后就会分配内存,以保存数组所有元素。使用new运算符,指定数组中的元素类型和数量来初始化数组的变量。

数组的声明:

int[] myArray; 
数组的初始化
1.先定义后声明并指定数量
int[] myArray;
myArray =new int[4]; 2.声明并指定数量后直接初始化
int[] myArray=new int[4]{4,7,11,5}; 3.声明后直接初始化
int[] myArray=new int[]{4,7,11,5,8,7}; 4.直接定义初始化
int[] myArray={4,7,11,5,8,7};
访问数组元素
通过索引访问

myArray[]访问的是数组的第一个元素。
myArray[myArray.Length-]访问的是数组最后一个元素。
数组的第一个索引是从0开始的 循环中使用索引访问
for(int i=;i<myArray.Length;i++)
{
Console.WriteLine(myArray[i]) ;
}
foreach(var item in myArray)
{
Console.WriteLine(item) ;
}
使用引用类型

如果数组的元素是引用类型,则必须给数组每个元素分配内存,若使用了没有分配内存的元素。则会出现空指针的异常。

使用从0开始的索引器,可以为数组的每个元素分配内存:

myPersons[] = new Person { FirstName = "Ayrton", LastName = "Senna" };
myPersons[] = new Person { FirstName = "Michael", LastName = "Schumacher" };

3.多维数组

用一个整数做索引的是一维数组,用多个整数做索引的是多维数组。

二维数组:

int[,] twodim = new int[, ];
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;
twodim[, ] = ;

二维数组中的twodim[3,3],twodim[2,3],twodim[3,2]会发生运行时异常,索引最大值不能大于等于声明的数量,且不能小于0;

如果只赋了一部分索引的值。使用了没有赋值的索引会发生空指针的异常。比如说twodim[2,2]没有赋值的话,却使用了它则会报错。

int[,] twodim={
{,,},
{,,},
{,,}
};

二维数组的初始化器有点不同。大括号里面包含了多组小括号对。第一对小括号,代码索引0,小括号中的第一个值就是twodim[0,0]了

三维数组:

int[, ,] threedim = {
{ { , }, { , } },
{ { , }, { , } },
{ { , }, { , } }
};
Console.WriteLine(threedim[0, 1, 1]);

这个三维数组的threedim[0,0,0]等于1;threedim[2,0,2]等于10

4.锯齿数组

锯齿数组大小对应一个矩形。每一行都有不同的大小。

声明锯齿数组时,依次放置左右中括号。左括号为行数,右括号为空。

 int[][] jagged = new int[][];
jagged[] = new int[] { , };
jagged[] = new int[] { , , , , , };
jagged[] = new int[] { , , };

  迭代器数组中所有元素的代码可以放在嵌套的for循环中,外层的for循环中迭代每一行,

在内层的for循环中迭代一行中的每个元素:

for (int row = ; row < jagged.Length; row++)
{
for (int element = ;
element < jagged[row].Length; element++)
{
Console.WriteLine(
"row: {0}, element: {1}, value: {2}",
row, element, jagged[row][element]);
}
}

  该迭代结果显示了所有的行和每一行的各个元素:

row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:
row: , element: , value:

5.Array类

创建数组

Array类是抽象的,因此不能通过构造函数初始化。可以使用静态方法CreateInstance()方法创建数组。方法中需要传入数组的类型,创建好之后使用SetValue和GetValue来给这个数组添加和修改元素值。

Array intArray1 = Array.CreateInstance(typeof(int), );
for (int i = ; i < ; i++)
{
intArray1.SetValue(, i);
} for (int i = ; i < ; i++)
{
Console.WriteLine(intArray1.GetValue(i));
}

还可以将数组强制转换为int数组。

int[] intArray2=(int[])intArray1
复制数组

因为数组是引用类型,所以将一个数组变量赋值给另一个数组变量,就得到2个引用地址相同的数组变量。从而复制数组。以下代码使用了克隆方法Clone方法进行了数组复制。

int[] intArray1={,};
int[] intArray2=(int[])intArray1.Clone();

如果数组中包含引用类型。则不复制元素,复制引用。则更改克隆后的对象会更改被克隆对象的值。

Person[] beatles = {
new Person { FirstName="John", LastName="Lennon" },
new Person { FirstName="Paul", LastName="McCartney" }
}; Person[] beatlesClone = (Person[])beatles.Clone();

除了Clone方法还可以使用Copy复制方法。复制数组的浅副本。只复制数据。

排序

Array类使用Quick算法对数组元素进行排序。需要使用Array类的Sort方法,因此需要实现IComparable接口。

 string[] names = {
"Christina Aguilera",
"Shakira",
"Beyonce",
"Gwen Stefani"
}; Array.Sort(names); foreach (string name in names)
{
Console.WriteLine(name);
}

该应用程序的输出是排好序的数组:

Beyonce
Christina Aguilera
Gwen Stefani
Shakira

 public class Person : IComparable<Person>
{
public string FirstName { get; set; } public string LastName { get; set; } public override string ToString()
{
return String.Format("{0} {1}",
FirstName, LastName);
} public int CompareTo(Person other)
{
if (other == null) throw new ArgumentNullException("other"); int result = this.LastName.CompareTo(other.LastName);
if (result == )
{
result = this.FirstName.CompareTo(other.FirstName);
} return result;
} }

现在可以按照姓氏对Person对象对应的数组排序

Person[] persons= new  {
new Person { FirstName="Damon", LastName="Hill" },
new Person { FirstName="Niki", LastName="Lauda" },
new Person { FirstName="Ayrton", LastName="Senna"},
new Person { FirstName="Graham", LastName="Hill" }
};
Array.Sort(persons);
foreach (Person p in persons)
{
Console.WriteLine(p);
}

6.数组作为参数

数组可以作为方法参数进行传递,也可以作为方法返回类型,数组是引用类型。因此不用ref进行值传递。

数组协变

数组支持协变。因此数组可以声明为基类。方法参数中定义基类数组传入派生类数组进行传递。

数组协变只用于引用类型

在C#中多次提到协变和逆变,那么协变是什么逆变是什么呢。

“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

“逆变”则是指能够使用派生程度更小的类型。

这个博客有了详细的介绍:http://www.cnblogs.com/qixuejia/p/4383068.html

ArraySegments<T>数组段

需要使用不同方法处理某个大型数组的不同部分。那么可以把数组部分复制到各个方法中。

上图中通过创建数组段,然后将第一个数组ar1拆分了一组到数组段,将第2个数组ar2拆分了一组到数组段。

第一个数组从ar1的开始位置引用了3个元素。第2个数组从ar2的第4个位置(索引值是3)引用了3个元素。最后通过SumOfSegments方法对这2个数组段进行了求和.

需要注意的是,ArraySegments<T>没有复制原数组,但是如果数组段中的数组元素改变了,会影响原数组的元素。

7.枚举

IEnumerator接口

foreach语句

我们编写的foreach语句:

IL生成后的语句:

C#会将foreach语句转换为IEnumerator接口,通过GetEnumerator()获得枚举器,在循环中只要MoveNext方法返回true,用Current属性访问数组或集合元素中的值。

yield语句

用于创建迭代器,yield return返回数组中的一个元素,并移到下一个元素。yield break用于停止迭代。

包含yield语句的属性和方法叫迭代块。编译器会生成一个yield的类型。包含状态机。生成后的yield类型相当于Enumerator类型。

8.元组

数组合并了相同类型的对象,元组合并了不同类型的对象。不同的Tuple类型支持不同数量的元素。

元组一共8个类型,元组最后一个类型是TRest需要传入一个元组。

9.结构比较

数组和元组都实现接口IStructuralEquatable和IStructuralComparable,不仅可以比较引用还可以比较内容。需要显示的实现这些内容。

IStructuralEquatable接口用于比较数组和元素是否有相同内容,IStructuralComparable接口用于排序。

结构比较

数组和元组都实现接口IStructuralEquatable和IStructuralComparable.这两个接口不仅可以比较引用,还可以比较内容.这些接口都是显示实现的,所以在使用时需要把数组和元组强制转换为这个接口.IStructuralEquatable接口用于比较两个元组或数组是否有相同的内容,IStructuralComparable接口用于给元组或数组排序.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace

{

class Program

{

static void Main(string[] args)

{

//创建两个Person项的数组.

//比较运算符!=返回true

//因为这其实是两个变量p1和p2引用的两个不同数组.

//因为array类没有重写带一个参数的Equals()放大,所以用"=="运算符

//比较引用会得到相同的结构,即这两个变量不相同

Person zhangsan = new Person { FirstName = "zhang", LastName = "san" };

Person[] p1 = {

new Person

{

FirstName="lisi"

} ,

zhangsan

};

Person[] p2 = {

new Person

{

FirstName="lisi"

} ,

zhangsan

};

if (p1!=p2)

{

Console.WriteLine("not the same reference"); ;

}

Console.ReadKey();

}

}

public class Person : IEquatable<Person>

{

public int ID { get; set; }

public string FirstName { set; get; }

public string LastName { set; get; }

public override string ToString()

{

return string.Format("{0},{1}{2}", ID, FirstName, LastName); ;

}

public override bool Equals(object obj)

{

if (obj == null)

{

throw new ArgumentNullException("obj");

}

return Equals(obj as Person);

}

public override int GetHashCode()

{

return ID.GetHashCode();

}

public bool Equals(Person other)

{

if (other == null)

{

throw new ArgumentNullException("other");

}

return this.ID == other.ID && this.FirstName == other.FirstName && this.LastName == other.LastName;

}

}

}

使用实现IEquatable接口的Person类.IEquatable接口定义了一个强类型化的Equals()方法,用来比较FirstName和LastName属性的值.

下面看看如何对元组执行相同的操作.这里创建了两个内容相同的元组实例:

var t1 = Tuple.Create<string, int>("zhangsan", 19);

var t2 = Tuple.Create<string, int>("zhangsan", 19);

//因为t1和t2引用了两个不同的对象,所以比较运算符"!="返回true

if (t1 != t2)

{

Console.WriteLine("not the same reference to the tuple");

}

//这会调用object.equals()方法比较元组的每一项,每一项都返回true

if (t1.Equals(t2))

{

Console.WriteLine("the same reference to the tuple");

}

Tuple<>类提供了两个Equals()方法:一个重写了object基类中的Equals()方法,并把object作为参数,第二个由IStructyralEqualityComparer接口定义,并把object和IequalityComparer作为参数.

还可以使用类TupleCOmparer创建一个自定义的UequalityComparer,这个类实现了IEqualityComparer接口的两个方法Equals()和GetHashCode():

public class TupleComparer : IEqualityComparer

{

public bool Equals(object x, object y)

{

return x.Equals(y);

}

public int GetHashCode(object obj)

{

return obj.GetHashCode();

}

}

实现IEqualityCOmparer接口的Equals()方法需要new修饰符或者隐式实现的接口,因为基类object也定义了带两个参数的静态的Equals()方法.

使用TupleComparer,给Tuple<T1,T2>类的Equals()方法传递一个新实例.Tuple类的Equals(0方法为要比较的每一项调用TupleComparer的Equals()方法.所以,对于Tuple<T1,T2>类,要调用两次TupleCOmparer,以检查所有项是否相等.

C#高级编程9-第6章 数组的更多相关文章

  1. C#高级编程9 第17章 使用VS2013-C#特性

    C#高级编程9 第17章 使用VS2013 编辑定位到 如果默认勾选了这项,请去掉勾选,因为勾选之后解决方案的目录会根据当前文件选中. 可以设置项目并行生成数 版本控制软件设置 所有文本编辑器行号显示 ...

  2. 读《C#高级编程》第1章问题

    读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...

  3. C#高级编程9 第18章 部署

    C#高级编程9 第18章 部署 使用 XCopy 进行部署 本主题演示如何通过将应用程序文件从一台计算机复制到另一台计算机来部署应用程序. 1.将项目中生成的程序集复制到目标计算机,生成的程序集位于项 ...

  4. C#高级编程9 第16章 错误和异常

    C#高级编程9 第16章 错误和异常 了解这章可以学会如何处理系统异常以及错误信息. System.Exception类是.NET运行库抛出的异常,可以继承它定义自己的异常类. try块代码包含的代码 ...

  5. C#高级编程笔记之第二章:核心C#

    变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...

  6. C#高级编程9 第14章 内存管理和指针

    C#高级编程9 内存管理和指针 后台内存管理 1) 值数据类型 在处理器的虚拟内存中有一个区域,称为栈,栈存储变量的浅副本数据,通过进入变量的作用域划分区域,通过离开变量的作用域释放. 栈的指针指向栈 ...

  7. 重学《C#高级编程》(泛型与数组)

    前段时间工作比较忙,就没有写随笔了,现在继续. 前两天重新看了泛型和数组两章,简单说下我自己的收获吧 泛型 我们知道数组是一种批量的数据格式,而泛型其实就是一种自定义的批量数据格式,当数组和C#现有的 ...

  8. python高级编程(第12章:优化学习)1

    # -*- coding: utf-8 -*-# python:2.x__author__ = 'Administrator'#由于5,6,7,8,9,10,11主要是在包,测试之类的学习所以这边就不 ...

  9. javascript高级编程3第三章:基本概念 本章内容 语法 数据类型 流控制语句 函数

    3.1 语法 ECMAScript的语法大量借鉴了C及其他类C语言的语法. 3.1.1 区分大小写 3.1.2 标识符 所谓标识符,就是值变量.函数.属性的名字,或者函数的参数.标识符可以是按照下列格 ...

  10. 《UNIX环境高级编程》第七章进程环境

    7.2 main函数 1.C程序总是从main函数开始执行的,原型:int main(int argc,char *argv[]);argc是命令行参数的个数argc是指向参数的各个指针所构成的数组2 ...

随机推荐

  1. Javascript中的Callback方法浅析

    什么是callback?  回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数.回调函数不是由该函数 ...

  2. python面向对象(六)之元类

    元类 1. 类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍然成立: In [13]: class ObjectCreator(object): . ...

  3. Service(一):认识service、绑定Service

    Activity是与用户打交道的,而Service是在后台运行的. 这个程序介绍了下如何启动和停止一个Service,以及在后台打印消息,我添加了一些注释. 在activity_main中将布局改为线 ...

  4. ROSCon 2017通知 Announcing ROSCon 2017: September 21st and 22nd in Vancouver

    ROSCon 2017通知:9月21日和22日在温哥华 我们很高兴地宣布,2017年ROSCon将在举行9月21-22日,2017年温哥华会议中心在加拿大温哥华.2017年IROS将在同一地点9月24 ...

  5. Ubuntu 搭建ELK

    一.简介 官网地址:https://www.elastic.co/cn/ 官网权威指南:https://www.elastic.co/guide/cn/elasticsearch/guide/curr ...

  6. return to dl_resolve无需leak内存实现利用

    之前在drop看过一篇文章,是西电的Bigtang师傅写的,这里来学习一下姿势做一些笔记. 0x01 基础知识 Linux ELF文件存在两个很重要的表,一个是got表(.got.plt)一个是plt ...

  7. js中的Object.seal()与Object.freeze()

    关键字:seal, freeze, property descriptor. 1.Object.seal() 参考文档(2)中这样描述: The Object.seal() method seals ...

  8. sublime text配置fcitx输入法

    sublime text配置fcitx输入法 环境:Fedora20 输入法:fcitx sublime text:version 3 安装fcitx输入法 这个看前面教程 编译sublime-imf ...

  9. 如何在k8s集群里快速运行一个镜像?

    在docker里,快速run一个镜像,很简单的. k8s的世界,与之类似. 但要注意一下,如果镜像本身没有提供command命令,这个容器由于前台输出完成,很快就退出了. 所以,遇到这种镜像,就最好自 ...

  10. day7 socket网络编程基础

    Socket Socket是什么? 下面来看一下网络的传输过程: 上面图片显示了网络传输的基本过程,传输是通过底层实现的,有很多底层,我们写传输过程的时候,要知道所有的过程那就太复杂了,socket为 ...