第7章 数组和元组

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()也可以创建浅表副本,但必须传递阶数相同且有足够元素的已有数组。
  • 排序:

    • 数组使用自定义类型应实现接口有两种方式:
      • 实现IComparable接口,重写CompareTo()方法 ==> 就是在告诉大家,我实现了这个接口,所有我的实例都是可以比较的,并且比较的规则是按照我实现的IComparable中的方法CompareTo来进行的。
      • 实现IComparer接口或IComparer<T>接口,重写Compare()方法。该接口独立于要比较的类,需要两个要比较的参数。

                           注明:详细可参考 原文:https://blog.csdn.net/ios99999/article/details/77800819

    •  举例说明实现以上两种接口的自定义类型数组使用Sort()排序方法
      • 实现IComparable接口
//自定义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:用于给元组或数组排序
原文地址:https://www.cnblogs.com/zhangjbravo/p/9719378.html