1.区别:
- 使用同一类型的多个对象 ==> 集合或数组
- 使用不同类型的多个对象 ==> 元组(Tuple)
2.简单数组:
- 数组初始化:数组是引用类型,必须给它分配堆上的内存,应使用new运算符
- 指定数组大小:int[] myArray = new int[4];
- 使用初始化器赋值:int[] myArray = new int[4] {4, 7, 11, 2};
- 简化一:使用花括号初始化数据,可不指定数组的大小 int[] myArray = new int[] {4, 7, 11, 2};
- 简化二:C#编译器更简化,使用花括号可以同时声明和初始化数组 int[] myArray = {4, 7, 11, 2};
- 使用引用类型:可以声明自定义类型的数组
- 数组元素是引用类型,就必须为每个数组元素分配内存
//自定义Person类 public class Person { public string FirstName {get; set;} public string LastName {get; set;} public override string ToString() => $"{FirstName} {LastName}"; } //定义Person数组 Person[] myPersons = new Person[2]; //为每个数组元素分配内存 myPersons[0] = new Person {FirstName = "Ayrton", LastName="Senna"}; myPersons[1] = new Person {FirstName = "Michael", LastName="Schumacher"};
-
- 使用数组初始化器赋值
Person[] myPersons2 = { new Person {FirstName = "Ayrton", LastName="Senna"}, new Person {FirstName = "Michael", LastName="Schumacher"} };
3.多维数组:用两个或多个整数来索引
- 二维数组:
- 声明:需要在方括号中加上一个逗号;
- 初始化:
- 应指定每一维的大小(也称为阶)==> int[,] twodim = new int[3,3];
- 使用数组索引器
int[,] towdim = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, };
- 三维数组:在花括号中使用两个逗号声明
int[,,] threedim = { { {1, 2}, {3, 4} }, { {5, 6}, {7, 8} }, { {9, 10}, {11, 12} } }; WriteLine(threedim[0, 1, 1]);
4. 锯齿数组:每一行都可以有不同的大小。
- 声明时要依次放置左右括号
- 初始化时只对第1对方框中设置数组行数。
- 定义每行时再指定元素个数
int[][] jagged = new int[3][]; jagged[0] = new int[2] {1, 2}; jagged[1] = new int[6] {3, 4, 5, 6, 7, 8}; jagged[2] = new int[3] {9, 10, 11};
5. Array类:用方括号声明数组是C#中使用Array类的表示法
- Length属性或foreach语句迭代数组,使用了Array类中的GetEnumerator()方法
- LongLength属性:数组元素个数超出整数的取值范围,使用此属性获得元素个数
- Rank属性:获得数组的维数
- 创建数组:
- CreateInstance()创建是事先不知道元素类型的数组
- SetValue()设置对应元素的值
- GetValue()读取对应元素的值
//创建类型为int、大小为5的一维数组 Array intArray1 = Array.CreateInstatnce(typeof(int), 5); //强制转换成声明为int[]的数组 int[] intArray2 = (int[])intArray1; //创建类型为Person,大小为2*3个元素的二维数组 //且第一维基于1,第二维基于10 int[] lengths = {2, 3}; int[] lowerBounds = {1, 10}; Array racers = Array.CreateInstance(typeof(Person), lengths, lowerBounds);
- 复制数组:
- 实现ICloneable接口,使用Clone()方法创建数组的浅表副本,即创建一个新数组。
- 数组元素是值类型,值复制所有值。
- 数组元素是引用类型,则不复制元素,只复制引用。
- Array.Copy()也可以创建浅表副本,但必须传递阶数相同且有足够元素的已有数组。
- 实现ICloneable接口,使用Clone()方法创建数组的浅表副本,即创建一个新数组。
- 排序:
-
- 数组使用自定义类型应实现接口有两种方式:
- 实现IComparable接口,重写CompareTo()方法 ==> 就是在告诉大家,我实现了这个接口,所有我的实例都是可以比较的,并且比较的规则是按照我实现的IComparable中的方法CompareTo来进行的。
- 实现IComparer接口或IComparer<T>接口,重写Compare()方法。该接口独立于要比较的类,需要两个要比较的参数。
- 数组使用自定义类型应实现接口有两种方式:
注明:详细可参考 原文:https://blog.csdn.net/ios99999/article/details/77800819
-
- 举例说明实现以上两种接口的自定义类型数组使用Sort()排序方法
- 实现IComparable接口
- 举例说明实现以上两种接口的自定义类型数组使用Sort()排序方法
//自定义Person类 public class Person : IComparable<Person> { public string FirstName { get; set; } public string LastName { get; set; } public override string ToString() => $"{FirstName} {LastName}"; public int CompareTo(Person other) { if (other == null) throw new ArgumentNullException("other"); int result = LastName.CompareTo(other.LastName); if (result == 0) { result = FirstName.CompareTo(other.FirstName); } return result; } } //数组排序 Person[] persons = { 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) { WriteLine(p); } //排序结果 //Damon Hill //Graham Hill //Niki Lauda //Ayrton Senna
-
-
- 实现IComparer<T>接口
-
//自定义PersonComparer类 //定义了排序选项 public enum PersonCompareType { FirstName, LastName } public class PersonComparer : IComparer<Person> { private PersonCompareType _compareType; public PersonComparer(PersonCompareType compareType) { _compareType = compareType; //确定排序方式 } #region IComparer<Person> Members // 可根据排序方式灵活进行排序 public int Compare(Person x, Person y) { if (x == null && y == null) return 0; if (x == null) return 1; if (y == null) return -1; switch (_compareType) { case PersonCompareType.FirstName: return string.Compare(x.FirstName, y.FirstName); case PersonCompareType.LastName: return string.Compare(x.LastName, y.LastName); default: throw new ArgumentException( "unexpected compare type"); } } #endregion } //自定义类型数组排序,根据FirstName排序 Array.Sort(persons, new PersonComparer(PersonCompareType.FirstName)); foreach (Person p in persons) { WriteLine(p); } //排序结果 //Ayrton Senna //Damon Hill //Graham Hill //Niki Lauda
6. 数组作为参数:数组可以作为参数传递给方法,也可以从方法返回
- 数组支持协变:即可以声明一个基类参数(如object[]类型),给它传递一个派生类型(如Person[])。数组协变只能用于引用类型。
- 部分数组:结构ArraySegment<T>表示数组的一段。可以将整个数组的某些部分传递给不同的方法。参数包括数组,偏移量和该方法使用的元素数
//计算数组段定义的所有整数之和 static int SumOfSegments(ArraySegment<int>[] segments) { int sum = 0; foreach (var segment in segments) { for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) { sum += segment.Array[i]; } } return sum; } //使用方法 //计算数组ar1从第1个元素开始,引用3个元素; //计算数组ar2从第3个元素开始,引用3个元素; int[] ar1 = { 1, 4, 5, 11, 13, 18 }; int[] ar2 = { 3, 4, 5, 18, 21, 27, 33 }; var segments = new ArraySegment<int>[2] { new ArraySegment<int>(ar1, 0, 3), new ArraySegment<int>(ar2, 3, 3) }; // sum = 1 + 4 + 5 + 18 + 21 + 27 var sum = SumOfSegments(segments); WriteLine($"sum of all segments: {sum}");
7. 枚举:使用foreach语句实现迭代,是由于其使用了枚举器
- 一个类型是否支持foreach遍历,必须满足下面两个条件:
- 这个类实现IEnumerable接口
- 这个类有一个public的GetEnumerator的实例方法,并且返回类型中(IEnumerator)具有public 的bool MoveNext()方法和public的Current属性
- IEnumerable接口与IEnumerator接口区别:
- IEnumerable 可迭代的,可以说是一个声明式的接口,但没有具体如何实现迭代器(iterator),仅有可返回IEnumerator 类型的方法
- IEnumerator 迭代器,通过实现该接口就可以作为一个迭代器(iterator),包含了具体的需要实现的方法
注明: 详细可参考 原文:https://blog.csdn.net/i1tws/article/details/51511134?utm_source=copy
- yield语句:便于创建枚举器,其必须声明为返回IEnumerator或IEnumerable接口
- yield return:返回集合的一个元素
- yield break:可停止迭代
- 可以使用yield return语句,以不同方式迭代集合的类
- yield return返回枚举器
8.元组:用于合并不同类型的对象。
- .NET Framework定义了8个泛型Tuple类和1个静态Tuple类。
- 元组用静态Tuple类的静态Create()方法创建。
9.结构比较:数组和元组都实现接口IStructuralEquatable和IStructuralComparable
- IStructuralEquatable:比较两个元组或数组是否有相同的内容
- IStructuralComparable:用于给元组或数组排序