C# 数组(5) 持续更新
同一类型和不同类型的多个对象
使用同一类型的多个对象,使用集合和数组。
使用不同类型的多个对象,使用Tuple(元组)。
初始化数组
int[] myArray = new int[4];
myArray 存放在栈中,而 myArray 的内容 放在 托管堆。
声明数组后,必须为数组分配内存,以保存数组的所有的元素。数组是引用类型,必须给它分配堆上的内存。
如果不知道数组包括多少元素,可以使用集合。
还可以用数组初始化器。
int[] myArray = new int[4]{1,2,3,4};
访问数组
myArray[0] = 12; myArray[1] = 24;
如果使用错误的索引值,会抛出 IndexOutOfRangeException 错误。
遍历数组
int[] myArray = new int[4]{1,2,3,4}; for (int i = 0; i < myArray.Length; i++) { Console.WriteLine("{0}",myArray[i]); }
int[] myArray = new int[4]{1,2,3,4}; foreach (int val in myArray) { Console.WriteLine("{0}",val); }
foreach 语句利用了本章 IEnumerable 和 IEnumerator 接口。
使用引用类型
Person[] myPersons = new Person[2]; myPersons[0] = new Person() {FirstName = "FirstName1", LastName = "LastName1"}; myPersons[1] = new Person() { FirstName = "FirstName2", LastName = "LastName2" };
Person[] myPersons = { new Person {FirstName = "FirstName1", LastName = "LastName1"}, new Person {FirstName = "FirstName2", LastName = "LastName2"} };
多维数组
int[,] mInts = new int[2,2]; mInts[0, 0] = 1; mInts[0, 1] = 2; mInts[1, 0] = 3; mInts[1, 1] = 4;
int[,] mInts = { {0, 1}, {0, 2}, {1, 1}, {2, 2} };
当然还可以声明三维数组
int[,,] mInts = new int[2,2,2]; mInts[0, 0, 0] = 0; mInts[0, 0, 1] = 1; mInts[0, 1, 0] = 2; mInts[0, 1, 1] = 3; mInts[1, 0, 0] = 4; mInts[1, 0, 1] = 5; mInts[1, 1, 0] = 6; mInts[1, 1, 1] = 7;
int[,,] mInts = { {{0, 1}, {2, 3}}, {{3, 4}, {5, 6}}, {{7, 8}, {9, 10}} };
锯齿数组
int[][] jagged = new int[3][];
在初始化锯齿数组是,只在第1对方括号中设置该数组包含的行数。定义各行中元素个数的第2个方括号设置为空,让数组每行包含不同元素个数,之后指定行中元素个数。
int[][] jagged = new int[3][]; jagged[0] = new int[2] {1, 2}; jagged[1] = new int[3] {3, 4, 5}; jagged[2] = new int[4] {6, 7, 8, 9};
Array类
方括号声明数组, 实际上派生一个自抽象基类Array新类。foreach语句迭代数组,实际就是调用 Array中的 GetEnumerator()。
Array.LongLength 包含数组的元素个数超出了整数的取值范围,就可以使用LongLength属性来获得元素的个数。
Array.Rank 获得数组的维数。
创建数组
Array intArray1 = Array.CreateInstance(typeof(int),5); for (int i = 0; i < 5; i++) { intArray1.SetValue(2 + i,i); } for (int i = 0; i < 5; i++) { Console.WriteLine(intArray1.GetValue(i)); } int[] intArray2 = (int[]) intArray1;
创建多维数组
int[] lengths = {2, 3}; int[] lowerBounds = {1, 10}; Array racers = Array.CreateInstance(typeof(Person),lengths,lowerBounds);
复制数组
数组实现ICloneable接口,然后调用Clone返回浅副本。
int[] intArray1 = {1, 2, 3}; int[] intArray2 = (int[])intArray1.Clone();
copy必须传递足够元素的数组。
有时也需要深度克隆,但克隆的时候,要仔细思考一下,是否真的需要克隆。
排序
int[] intArray1 = {99, 6, 3}; Array.Sort(intArray1); foreach (var i in intArray1) { Console.WriteLine(i); }
如果数组是自定义类,那么类就要实现Icomparable接口
public class Person: IComparable<Person> { public string FirstName { get; set; } public string LastName { get; set; } public int CompareTo(Person other) { if (other == null) return 1; int result = String.CompareOrdinal(this.LastName, other.LastName); if (result == 0) { result = String.CompareOrdinal(this.FirstName, other.FirstName); } return result; } public override string ToString() { return String.Format("{0} {1}",FirstName,LastName); } }
对象相等,方法返回0。小于 返回 小于0的值 ,大于返回 大于0的值。
如果不能修改类,还可以新类里 实现 IComparer 或 IComparer<T> 实现接口。
public enum PersonCompareType { FirstName, LastName } public class PersonComparer : IComparer<Person> { private PersonCompareType compareType; public PersonComparer(PersonCompareType compareType) { this.compareType = compareType; } #region IComparer<Person> Members public int Compare(Person x, Person y) { if (x == null) throw new ArgumentNullException("x"); if (y == null) throw new ArgumentNullException("y"); switch (compareType) { case PersonCompareType.FirstName: return String.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal); case PersonCompareType.LastName: return String.Compare(x.LastName, y.LastName, StringComparison.Ordinal); default: throw new ArgumentException( "unexpected compare type"); } } #endregion }
Array.Sort(persons, new PersonComparer(PersonCompareType.FirstName)); foreach (Person p in persons) { Console.WriteLine(p); }
数组协变
数值支持协变。这表示数组可以声明为基类,其派生类型的元素可以赋予数组元组。
static void TestArray(object[] objects)
这里就可以传入 Person[] 参数进去了。
注意,数组协变只能用于引用类型,不能用于值类型。另外如果object[],被赋予了 Person[] 。就不能赋值其他类型的值了,否则运行时,会出现 ArrayTypeMismatchException 的异常。
ArraySegment<T>
ArraySegment表示数组的一部分。
static void Main(string[] args) { int[] ar1 = {0, 1, 2, 3, 4, 5, 6}; int[] ar2 = { 0, 1, 2, 3, 4, 5, 6 }; ArraySegment<int>[] setmentInts = new ArraySegment<int>[2] { new ArraySegment<int>(ar1,0,3), new ArraySegment<int>(ar2,3,3), }; int sum = SumOfSegments(setmentInts); Console.WriteLine(sum); } 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]; //} foreach (var value in segment) { sum += value; } } return sum; }
数组段不复制原数组的元素,但原数组可以通过ArraySegment<T>访问。如果数组段的元素改变了,这些变换就会反映到原数组中。
枚举
数组或集合实现带 GetEumerator方法的IEumerable 接口。 GetEumberator 方法返回一个实现Ieumerable 接口的枚举。然后 foreach语句就可以使用Ieumerable接口迭代集合。
IEnumerator接口
foreach使用IEnumerator接口的方法和属性,迭代集合中的所有方法。为此,IEnumerator定义了Current属性,来返回光标所在的元素,该接口的MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法返回false。
这个接口的泛型版本IEnumerator<T> 派生字接口 IDispose ,因此定义了 Dispose 方法,来清空枚举器占用的资源。
IEnumerator 接口还定义了 Reset() 方法,以与 COM 交互操作。
foreach语句
C#的 foreach 语句 不会解析为 IL代码中的 foreach语句。 C#编译器会把 foreach语句转换为 IEnumerator接口的方法和属性。
foreach (var value in ar1) { Console.WriteLine(value); }
解析为
int[] ar1 = {0, 1, 2, 3, 4, 5, 6}; int[] ar2 = { 0, 1, 2, 3, 4, 5, 6 }; IEnumerator enumerator = ar1.GetEnumerator(); while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current); }
yield
yield可以创建枚举器,yield return语句返回集合一个元素,并移动到下一个元素上。yield break 可停止迭代。
public class SimpleConllection : IEnumerable<string> { public IEnumerator<string> GetEnumerator() { yield return "Hello"; yield return "World"; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } static void Main(string[] args) { SimpleConllection simpleConllection = new SimpleConllection(); foreach (string s in simpleConllection) { Console.WriteLine(s); } }
包含yield语句的方法或属性称为迭代快,迭代块必须声明返回IEnumerator 和 IEnumerable 接口,或者这些接口的泛型版本。这个块可以包含多条 yield return 语句 或 yield break 语句。但不能包含 return 语句。
使用迭代块,编译器会生成 yield 类型,其中包含一个状态机,代码如下
public class SimpleConllection { public IEnumerator GetEnumerator() { return new Enumerator(0); } public class Enumerator : IEnumerator<string>, IEnumerator, IDisposable { private int state; private string current; public Enumerator(int state) { this.state = state; } bool System.Collections.IEnumerator.MoveNext() { switch (state) { case 0: current = "Hello"; state = 1; return true; case 1: current = "World"; state = 2; return true; case 2: break; } return false; } public void Dispose() { } public void Reset() { } public string Current { get; private set; } object IEnumerator.Current { get { return current; } } } }
yield 语句 会生成一个枚举器,而不仅仅生成一个包含的项的列表。这个枚举器通过 foreach 语句调用。foreach中访问每一项是,就会访问枚举器。这样迭代大数据时,无须一次把所有的数据读入内存。
元组
数组存储相同类型的对象,元组存放不同类型的对象。.net 4.0 中 定义了 8个泛型Tuple类和一个静态Tuple类。用于创建Tuple。
http://blog.csdn.net/limlimlim/article/details/7835926
private static Tuple<int, int> Divide(int dividend, int divisor) { return Tuple.Create<int, int>(dividend, divisor); } static void Main(string[] args) { Tuple<int, int> result = Divide(13, 2); Console.WriteLine("result of divison:{0}," + "reminder:{1}", result.Item1, result.Item2); }
当然 元组 还可以套用 元组。
http://blog.csdn.net/limlimlim/article/details/7835926
数组和元组 都实现了 IStructuralComparable,IStructuralEquatable 接口。
IStructuralEquatable 用于比较两个元组或数组是否有相同的内容。
IStructuralEquatable 用于元组或数组排序。
Tuple 也提供了 Equals