C#数组 - C#入门基础

C#中的数组

  • 数组概念
  • 数组是对象
  • 数组的声明
  • 数组的实例化
  • 显式的初始化 数组
    • 数组快捷语法
  • 隐式的初始化 数组
  • 知识点串起来组成例子

 

数组 是 由一个 变量名 表示的 同一组同类型数据元素

数组 一旦创建,大小就固定了;

C# 不像 javascript 一样,C#是不支持动态数组的。

数组从 0 开始,范围:0~n-1  (n是数量)

有两种类型的数组:一维数组 和 多维数组

数组实例是从 System.Array类 继承类型的对象。

 

数组是对象

数组是 引用类型;

数组的元素 的类型 既可以是引用类型也可以是值类型;分别称为:引用类型数组 和 值类型数组。

 

数组的声明

long[] secondArray;
int[,,] b;    // 三维数组,如果没有逗号就是一维数组,一个逗号就是二维数组

错误的写法: 
long b[];     // 错误,中括号在变量名后面,这是 C/C++的写法,C#不适用

 

数组的实例化

 // 一维数组,声明 a 数组、创建 a 数组实例,没有初始化
int[] a = new int[2];
Student[] std = new Student[];

// 三维数组 初始化
int[,,] arr3 = new int[3, 5, 2];

注意:与对象创建表达式不同,数组的初始化没有 圆括号,即使是引用类型数组的初始化

 

默认值:

创建数组实例后,其每个元素都有默认值,string 的为空字符串,int 的为 0 ,bool 的为 false,引用类型的为 null;

 

数组的初始化,显式的初始化 数组

// 不必输入多少维,也就是 :  = new int[1]{...}
int[] intArr = new int[] {1, 2, 4, 11, 84};

int[,] intArr2 = new int[,] {{10,1}, {2, 10}, {11, 9}};

 

数组快捷语法

int[] arr1 = new int[3] {1, 33, 1};
// 等价于
int[] arr1 = {1, 33, 1};


int[,] arr2 = new int[,] {{10,1}, {2, 10}};
// 等价于
int[,] arr2 = {{10,1}, {2, 10}};

 

数组的初始化,隐式的初始化 数组

上面我们一直都在数组的声明开始处显式指定数组类型,但是,和其他局部变量一样,数组可以是隐式类型的:

int[] a = new int[]{1, 2};
// var的隐式推断,因为可以从 {1,2} 推断出数组时int类型
var a = new []{1, 2};

int[,] b = new int[,] {{10,1}, {2, 10}};
// 隐式
var b = new [,] {{10,1}, {2, 10}};

string[] c = new string[] {"hello", ", world", "!"};
// 隐式
var c = new [] {"hello", ", world", "!"};

 

上面知识点综合串起来组成一个例子

// 声明、创建 和 初始化 一个隐式类型的数组
var arr = new int[,] {{0, 1, 2}, {10, 11, 12}};

for(int i = 0; i < 2; i++){
    for(int j = 0; j < 3; j++){
        Console.WriteLine($"[{i},{j} is {arr[i, j]}");
    }
}

 

接下来,是深入数组

  • 交错数组
  • foreach 循环数组
    • 迭代的变量是只读的
    • foreach语句 与 多维数组
    • foreach语句 与 交错数组
  • 数组协变
  • 数组的属性和方法
  • clone 克隆数组
  • 比较各个数组的区别
  • 数组 与 ref返回 和 ref局部变量

 

交错数组

 交错数组,其实就是数组的数组。

与矩形数组不同,交错数组的子数组的元素个数可以不同。

// 二维交错数组
int[][] jagArr = new int[3][];    // 读:jagArr是3个int数组的数组

jagArr[0] = new int[] {11, 34};
jagArr[1] = new int[] {41, 34, 99, 12, 7};
jagArr[2] = new int[] {11, 110, 781};

交错数组不能在一个步骤中完成:即不能一个语句搞定  创建、声明、实例化;

上述中,jagArr 第一个中括号为3,但是第二个中括号不能有数字;

下面是矩形数组 和 交错数组 的 结构:

一维数组:有特定性能优化指令。矩形数组没有,并且不在相同级别进行优化。

一维数组(可被优化)的交错数组 比 矩形数组(不能被优化) 更高效;

矩形数组复杂度低,因为它被作为一个单元而不是数组的数组,交较之 交错数组 复杂度高;

 

foreach 语句循环数组

foreach 语句除了可以循环数组,其实它还可以循环其他集合类型;

foreach 的迭代变量是 临时 的,也是只读的;

int[] arr1 = {10, 11, 12, 13};
foreach(int item in arr1){
    Console.WriteLine($"Item Value: {item}");   // item 这个临时的变量是只读的,无法修改它
}

 

迭代变量是只读的

因为foreach语句迭代的变量是只读的,所以我们不能改变它;

但是,对于 值类型 和 引用类型的数组,使用foreach语句去 迭代 出来的临时变量,是有区别的;

 

对于值类型的数组:

int[] arr = {1, 2, 3};
foreach(int itme in arr){
    item++;        // 编译错误,不得改变变量值
}

 

对于引用类型的数组:

我们仍然不能改变迭代变量,但是迭代变量只是保存了数据的引用,而不是数据本身(如果不懂此处,请学习有关 引用类型 和 值类型 的具体内存指向),因此,虽然不能改变引用,但我们可以通过迭代变量改变数据:

class AClass{
    public int MyField = 0;
}

AClass[] aclass = new AClass[4];

for(int i = 0; i < 4; i++){
    aclass[i] = new AClass();
    aclass[i].MyField= i;
}

foreach(AClass item in aclass){
    item.MyField += 10;        // 改变数据,是可以的
}

foreach(AClass item in aclass){
    Console.WriteLine($"{item.MyField}");
}

 

foreach语句 和 多维数组

在多维数组中,元素的处理次序是最右边的索引号最先递增。当索引从0到长度减1时,开始递增它左边的索引,右边的索引被重置成0。(这句话有点拗口,看下面例子即可)

// 例子:矩形数组
int total = 0;
int[,] arr = {{10, 11}, {12, 13}};

foreach(var element in arr){
    total += element;
    Console.WriteLine($"Element: {element}, Current Total: {total}");
}

// 输出:
Element: 10, Current Total: 10;
Element: 11, Current Total: 21;
Element: 12, Current Total: 33;
Element: 13, Current Total: 46;

 

foreach语句 和 交错数组

交错数组是数组的数组,所以我们必须为交错数组中的每一个维度使用独立的foreach语句。

例:

int total = 0;
int[][] arr = new int[2][];
arr[0] = new int[]{10, 11};
arr[1] = new int[]{12, 13, 14}

foreach(int[] array in arr){
    Console.WriteLine("开始新的数组");
    foreach(int item in array){
        total += item;
        Console.WriteLine($"  Item: {item}, Current Total: {total}");
    }
}

// 输出:
开始新的数组
  Item: 10, Current Total: 10
  Item: 11, Current Total: 21
开始新的数组
  Item: 12, Current Total: 33
  Item: 13, Current Total: 46
  Item: 14, Current Total: 60

 

数组协变

在某些情况下,即使某个对象不是数组的基类型,也可以把它赋值给数组元素。这种情况叫作 数组协变(array covariance)。在下面的情况下可以使用数组协变。

  • 数组是引用类型的数组。
  • 在赋值的对象类型和数组基类型之间有隐式转换或显式转换。

例如,如下代码声明了两个类,A和B,其中B类继承自A类。最后一行展示了把类型B的对象赋值给类型4的数组元素而产生的协变:

class A {...}
class B: A {...}

A[] AArray1 = new A[3];
A[] AArray2 = new A[3];

// 普通:将A类型的对象赋值给A类型的数组
AArray1[0] = new A();
AArray1[1] = new A();
AArray[2] = new A();

// 协变:将B类型的对象赋值给A类型的数组
AArray2[0] = new B();
AArray2[1] = new B();
AArray2[2] = new B();

 

 注意:值类型数组没有协变!!

 

数组继承来的有用成员

因为数组时继承至 System.Array 类的,这就相当于数组继承了里面的一些属性和方法,包括:

 

 例子:

public static void PrintArray(int[] a){
    foreach(var x in a){
        Console.WriteLine($"{x} ");
    }
    Console.WriteLine("");
}

int[] arr = new int[] {15, 20, 5, 25, 10};
PrintArray(arr);

Array.Sort(arr);
PrintArray(arr);

Array.Reverse(arr);
PrintArray(arr);

Console.WriteLine();
Console.WriteLine($"Rank = {arr.Rank}, Length = {arr.Length}");
Console.WriteLine($"GetLength(0) = {arr.GetLength(0)}");
Console.WriteLine($"GetType() = {arr.GetTeype()}");

// 输出:
15 20 5 25 10
5 10 15 20 25
25 20 15 10 5

Rank = 1, Length = 5
GetLength(0)  = 5
GetType() = System.Int32[]

 

Clone() 克隆数组

这是对数组的 复制,也就是说,它只创建了数组本身的克隆。

如果是引用类型数组,它不会复制元素引用的对象。

对于值类型数组和引用类型数组而言,这有不同的结果。

  • 克隆 值类型 数组 会产生两个独立数组。
  • 克隆 引用类型 数组 会产生指向相同对象的两个数组。

Clone方法返回object类型的引用,它必须被强制转换成数组类型。

 

值类型的例子分析:

int[] intArr1 = {1, 2, 3};   // 步骤 1
int[] intArr2 = (int[])intArr1.Clone();        // 步骤 2

intArr2[0] = 100; intArr2[1] = 200; intArr2[2]  300;    // 步骤 3

 

引用类型的例子分析:

class A{
    public int Value = 5;
}

A[] AArray1 = new A[3]{new A(), new A(), new A()};    // 步骤1
A[] AArray2 = (A[])AArray1.Clone();    // 步骤2

AArray2[0].Value = 100;
AArray2[1].Value = 200;
AArray2[2].Value = 300;    // 步骤 3

 

比较各个数组的区别

 

数组 与 ref返回 和 ref局部变量

请先回顾下ref,不清楚这个ref关键字的请先复习下 ref 关键字的概念和用法;

利用ref返回功能,可以把一个引用作为返回值传到方法体外,而利用ref局部变量,你可以在调用域内使用这个引用。例如,下面的代码定义了一个叫作 PointerToHighestPositive 的方法。这个方法接受一个数组作为参数,并且返回对该数组元素的引用,而不是元素中的int 值。然后,在调用域,你可以通过ref局部变量给这个元素赋值。

public static ref int PointerToHighestPositive(int[] numbers)
{
    int highest = 0;
    int indexOfHighest = 0;

    for (int i = 0; i < numbers.Length; i++)
    {
        if (numbers[i] > highest)
        {
            indexOfHighest = i;
            highest = numbers[indexOfHighest];
        }
    }
    return ref numbers[indexOfHighest];
}

static void Main()
{
    int[] scores = { 5, 80 };
    Console.WriteLine($"之前:{scores[0]}, {scores[1]}");
    ref int locationOfHighest = ref PointerToHighestPositive(scores);

    locationOfHighest = 0;
    Console.WriteLine($"之后:{scores[0]}, {scores[1]}");
}

// 输出
之前:5, 80
之后:5, 0
posted @ 2022-02-28 22:39  醉马踏千秋  阅读(1878)  评论(0编辑  收藏  举报