C#数组详解

C/C#/C++ 1365 0 2021-07-02

C#数组详解
C#数组简介
数组从字面上理解就是存放一组数,但在 C# 语言中数组存放的并不一定是数字,也可以是其他数据类型。
在一个数组中存放的值都是同一数据类型的,并且可以通过循环以及数据操作的方法对数组的值进行运算或操作。
数组中某个指定的元素是通过索引来访问的,从零开始建立索引,即数组索引从零开始。
所有的数组都是由连续的内存位置组成的。最低的地址对应第一个元素,最高的地址对应最后一个元素。
数组的工作方式与在大多数其他流行语言中的工作方式类似。但还有一些差异应引起注意。声明数组时,方括号[]必须跟在类型后面,而不是标识符后面。

C#一维数组
一维数组在数组中最常用,即将一组值存放到一个数组中,并为其定义一个名称,通过数组中元素的位置来存取值。
//定义数组
数据类型[]  数组名;
//初始化数组中的元素
数据类型[]  数组名 = new  数据类型[长度];
数据类型[]  数组名 = {值 1, 值 2, ...}
数据类型[]  数组名 = new  数据类型[长度]{值 1,值 2,...}
在使用数组前必须初始化数据,即为数组赋初值。
在指定数组的长度后,数组中的元素会被系统自动赋予初始值,与类中段的初始化类似,数值类型的值为 0、引用类型的值为 null。
如果在初始化数组中直接对数组赋值了,那么数组中值的个数就是数组的长度。
例如有一个 int 类型的数组,输出数组中的第一个元素和最后一个元素,语句如下。
//定义 int 类型的数组
int[] a = {1,2,3};
//输岀数组中的一个元素
Console.WriteLine(a[0]);
//输出数组中的最后一个元素
Console.WriteLine(a[a.Length-1]);


【实例 】在 Main 方法中创建 int 类型数组,并从控制台输入 5 个值存入该数组中,最后将数组中的最大数输出。
class Program
{
    static void Main(string[] args)
    {
        int[] a = new int[5];
        Console.WriteLine("请输入5个整数:");
        for(int i = 0; i < a.Length; i++)
        {
            a[i] = int.Parse(Console.ReadLine());//将字符串类型转换成整型
        }
        int max = a[0];//这里假设a[0]是最大的
        for(int i = 1; i < a.Length; i++)
        {
            if (a[i] > max)
            {
                max = a[i];
            }
        }
        Console.WriteLine("数组中最大值为:" + max);
    }
}


C#多维数组
在 C# 语言里多维数组中比较常用的是二维数组。
//定义多维数组
数据类型[ , , ...]  数组名;
//创建多维数组并初始化
数据类型[ , , ...]   数组名 = new  数据类型[m,n,...]  {{ , , ...},{ , , ...}};
【实例 1】在 Main 方法中定义一个存放学生成绩的二维数组,并将该数组中每个学生的成绩输出。
class Program
{
    static void Main(string[] args)
    {
        double[,] points = { { 90, 80 }, { 100, 89 }, { 88.5, 86 } };
        for(int i = 0; i < points.GetLength(0); i++)
        {
            Console.WriteLine("第" + (i + 1) + "个学生成绩:");
            for(int j = 0; j < points.GetLength(1); j++)
            {
                Console.Write(points[i, j] + " ");
            }
            Console.WriteLine();
        }
    }
}

在遍历多维数组元素时使用 GetLength(维度) 方法能获取多维数组中每一维的元素,维度也是从 0 开始的,因此在该实例中获取数组中第一维的值时使用的是 points.GetLength(0)。
在 C# 语言中不仅支持上面给出的多维数组,也支持锯齿型数组,即在多维数组中的每一维中所存放值的个数不同。
锯齿型数组也被称为数组中的数组。定义锯齿型数组的语法形式如下。
数据类型[][]  数组名 = new 数据类型[数组长度][];
数组名[0] = new 数据类型[数组长度];
在这里,数据类型指的是整个数组中元素的类型,在定义锯齿型数组时必须要指定维度。
【实例 2】在 Main 方法中创建一个锯齿型数组,第一维数组的长度是 2、第二维数组的长度是 3、第三维数组的长度是 4,并直接向数组中赋值,最后输出数组中的元素。
class Program
{
    static void Main(string[] args)
    {
        int[][] arrays = new int[3][];
        arrays[0] = new int[] { 1, 2 };
        arrays[1] = new int[] { 3, 4, 5 };
        arrays[2] = new int[] { 6, 7, 8, 9 };
        for(int i = 0; i < arrays.Length; i++)
        {
            Console.WriteLine("输出数组中第" + (i + 1) + "行的元素:");
            for(int j=0;j<arrays[i].Length; j++)
            {
                Console.Write(arrays[i][j] + " ");
            }
            Console.WriteLine();
        }
    }
}

锯齿型数组中的值也可以通过循环语句来赋值,与输岀语句类似。
在上面的实例中, arrays 数组中的元素从控制台输入的具体语句如下。
int[][] arrays = new int[3][];
arrays[0] = new int[2];
arrays[1] = new int[3];
arrays[2] = new int[4];
for(int i = 0; i < arrays.Length; i++)
{
    Console.WriteLine("输入数组中第" + (i + 1) + "行的元素:");
    for(int j=0;j<arrays[i].Length; j++)
    {
        arrays[i][j] = int.Parse(Console.ReadLine());
    }
    Console.WriteLine();
}


C# foreach循环用法
in 右边的项是集合名,in 左边的项是变量名,用来存放该集合中的每个元素。
foreach 语句经常与数组一起使用,在 C# 语言中提供了 foreach 语句遍历数组中的元素,具体的语法形式如下。
foreach(数据类型  变量名  in  数组名)
{
    //语句块;
}
这里变量名的数据类型必须与数组的数据类型相兼容。
foreach 语句仅能用于数组、字符串或集合类数据类型。
【实例】在 Main 方法中创建一个 double 类型的数组,并在该数组中存入 5 名学生的考试成绩,计算总成绩和平均成绩。
class Program
{
    static void Main(string[] args)
    {
        double[] points = { 80, 88, 86, 90, 75.5 };
        double sum = 0;
        double avg = 0;
        foreach(double point in points)
        {
            sum = sum + point;
        }
        avg = sum / points.Length;
        Console.WriteLine("总成绩为:" + sum);
        Console.WriteLine("平均成绩为:" + avg);
    }
}

从上面的执行效果可以看出,在使用 foreach 语句时可以免去使用下标的麻烦,这也 给遍历数组中的元素带来很多方便。

C# Split:将字符串拆分为数组
Split 方法用于按照指定的字符串来拆分原有字符串,并返回拆分后得到的字符串数组。
【实例 】在 Main 方法中从控制台输入一个字符串,然后计算该字符串中包含的逗号的个数。
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("请输入一个字符串:");
        string str = Console.ReadLine();
        string[] condition = { "," };
        string[] result = str.Split(condition, StringSplitOptions.None);
        Console.WriteLine("字符串中含有逗号的个数为:" + (result.Length - 1));
    }
}

在使用 Split 方法时,需要注意该方法中传递的参数 (condition, StringSplitOptions.None)。
第一个参数是拆分的条件数组,可以在该数组中存放多个字符串作为拆分的条件。
第二个 参数 StringSplitOptions.None 是拆分的选项,表示如果在拆分时遇到空字符也拆分出一个元素。
如果在拆分时不需要包含空字符串,则可以使用 StringSplitOptions.RemoveEmptyEntries 选项,例如在上例中将 StringSplitOptions.None 更改成 StringSplitOptions.RemoveEmptyEntries。

C#冒泡排序(Sort方法)
冒泡排序在应聘的笔试题目中经常被考到,冒泡排序的原理是将数组元素中相邻两个元素的值进行比较,将较小的数放到前面,每一次交换都将最大的数放到最后,依次交换后最终将数组中的元素从小到大排序。
【实例 1】在 Main 方法中创建一个整型数组,并在该数组中存放 5 个元素,使用冒泡排序算法将数组中的元素从小到大排序。
class Program
{
    static void Main(string[] args)
    {
        int[] a = { 5, 1, 7, 2, 3 };
        for(int i = 0; i < a.Length; i++)
        {
            for(int j = 0; j < a.Length - i - 1; j++)
            {
                if (a[j] > a[j + 1])
                {
                    int temp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = temp;
                }
            }
        }
        Console.WriteLine("升序排序后的结果为:");
        foreach(int b in a)
        {
            Console.Write(b + "");
        }
        Console.WriteLine();
    }
}

System.Array 是所有数组的基类,其提供的属性和方法也可以被用到任何数组中。

数组中常用的方法如下表所示。

编号 方法 描述
1 Clear() 清空数组中的元素
2 Sort() 冒泡排序,从小到大排序数组中的元素
3 Reverse() 将数组中的元素逆序排列
4 IndexOf() 查找数组中是否含有某个元素,返回该元素第一次出现的位置,如果没有与之匹配的元素,则返回 -1
5 LastIndexOf() 查找数组中是否含有某个元素,返回该元素最后一次出现的位置


【实例 2】使用数组中的 Sort 方法完成对数组元素的排序。
class Program
{
    static void Main(string[] args)
    {
        int[] a = { 5, 3, 2, 4, 1 };
        Array.Sort(a);
        Console.WriteLine("排序后的结果为:");
        foreach(int b in a)
        {
            Console.Write(b + " ");
        }
        Console.WriteLine();
    }
}

虽然在数组中并没有提供对其降序排序的方法,但可以先将数组中的元素使用 Sort 排序,再使用 Reverse 方法将数组中的元素逆序,这样就完成了从大到小的排序。

C# enum:枚举类型
枚举类型和结构体类型都是特殊的值类型,应用也比较广泛。
枚举类型与《C#一维数组》一节中介绍的数组比较接近,它可以将一组值存放到一个变量名下,方便调用。
例如,在《C# Split》一节中介绍的拆分方法中的字符串拆分选项 StringSplitOptions 就是一个枚举类型,在该类型中有两个值,分别是 None 和 RemoveEmptyEntries。
枚举类型是一种值类型,定义好的值会存放到栈中。
枚举类型在定义时使用 enum 关键字表示,枚举类型的定义与类成员的定义是一样的,或者直接定义在命名空间中(注意不能直接将枚举类型定义到方法中)。
定义枚举类型的变量的语法形式如下。
访问修饰符  enum  变量名 : 数据类型
{
    值1,
    值2,
}
其中:
1) 访问修饰符
与类成员的访问修饰符一样,省略访问修饰符也是代表使用 private 修饰符的。
2) 数据类型
指枚举中值的数据类型。只能是整数类型,包括 byte、short、int、long 等。
3) 值1、值2、……
在枚举类型中显示的值。但实际上每个值都被自动赋予了一个整数类型值,并且值是递增加 1 的,默认是从 0 开始的,也就是值 1 的值是 0、值 2 的值是 1。
如果不需要系统自动为枚举值指定值,也可以直接为其赋一个整数值。
每个没有指定值的枚举值,它的初始值都是上一个枚举类型的值加 1。
通常设置的枚举值都是不同的,其整数值也是不同的。
【实例 1】创建类 EnumTest,在该类中定义一个枚举类型存放教师职称(助教、讲师、副教授、教授)。在 Main 方法中分别打印出每个枚举值对应的整数值。
class EnumTest
{
    public enum Title : int
    {
        助教,
        讲师,
        副教授,
        教授
    }
}
调用:
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(EnumTest.Title.助教 + ":" + (int)EnumTest.Title.助教);
        Console.WriteLine(EnumTest.Title.讲师 + ":" + (int)EnumTest.Title.讲师);
        Console.WriteLine(EnumTest.Title.副教授 + ":" + (int)EnumTest.Title.副教授);
        Console.WriteLine(EnumTest.Title.教授 + ":" + (int)EnumTest.Title.教授);
    }
}

从上面的代码可以看出,由于枚举类型定义的类与 Main 方法所在的类不同,因此如果要使用该枚举值,需要使用“类名 . 枚举变量名”表示。
获取枚举类型中设置的值使用的语句是“类名 . 枚举变量名 . 枚举值”,在获取枚举类型中的每个枚举值对应的整数值时需要将枚举类型的字符串值强制转换成整型。
如果将助教的值设置为 1,将副教授的值设置为 4,代码如下。
class EnumTest
{
    public enum Title : int
    {
        助教=1,
        讲师,
        副教授=4,
        教授
    }
}

从上面的执行效果可以看出,当助教的值为 1 时,讲师的值为 2,而由于副教授的值被设置为 4,则教授的值为 5。
如果将枚举值的整数值设置为相同,则输出的值也会与前面的枚举值相同。
因此,每个枚举值的整数值都是前一个枚举值的整数值加 1。
在定义枚举类型时,要保证枚举值的唯一性,以免影响枚举类型的应用。

C# struct:结构体类型
结构体与类比较相似,由于它是值类型,在使用时会比使用类存取的速度更快,但灵活性方面没有类好。
结构体从字面上来理解是指定义一种结构,实际上结构体是一种与类的定义非常相似的数据类型,但它是值类型。
结构体的定义位置与枚举类型一样,都是在类中定义或者在命名空间下定义,而不能将其定义到方法中。
在结构体中能定义字段、属性、方法等成员。定义的语法形式如下。
访问修饰符  struct  结构体名称
{
    //结构体成员
}
其中:
1) 访问修饰符
通常使用 public 或者省略不写,如果省略不写,代表使用 private 来修饰。
如果结构体中的内容要被其他类中的成员访问,需要将其访问修饰符设置为 public。
2) 结构体名称
命名规则通常和变量的命名规则相同,即从第二个单词开始每个单词的首字母大写。
3) 结构体成员
包括字段、属性、方法以及后面要介绍的事件等。
在结构体中也能编写构造器,但必须带参数,并且必须为结构体中的字段赋初值。
在调用结构体的成员时,能使用不带参数的构造器,与创建类的对象时类似。
【实例 1】定义名为 student 的结构体,在该结构体中定义学生姓名 (name) 和年龄 (age) 的字段,并分别为字段生成属性,要求年龄必须大于 0。
class Program
{
    static void Main(string[] args)
    {
        student stu = new student();
        stu.Name = "张三";
        stu.Age = -100;
        Console.WriteLine("学生的信息为:");
        Console.WriteLine(stu.Name + ":" + stu.Age);
    }
    struct student
    {
        private string name;
        private int age;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        public int Age
        {
            get
            {
                return age;
            }
            set
            {
                if (value < 0)
                {
                    value = 0;
                }
                else
                {
                    age = value;
                }
            }
        }
    }
}

从上面的执行效果可以看出,由于在结构体中将年龄属性设置为如果小于 0,则值为 0 的条件,因此输出结果中年龄为0。
此外,从调用结构体的代码可以看出,调用结构体和调用类是类似的,是通过构造器来实现的。
当然,在调用结构体时也可以不用构造器。
【实例 2】在结构体中定义带参数的构造器,并在结构体中定义方法输出字段的值。
class Program
{
    static void Main(string[] args)
    {
        student stu = new student("李四",25);
        stu.PrintStudent();
    }
}
public struct student
{
    public student(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
    private string name;
    private int age;
    public void PrintStudent()
    {
        Console.WriteLine("姓名:" + name);
        Console.WriteLine("年龄:" + age);
    }
}

结构体与类有些类似,但其定义却有很大区别,具体如下表所示。
结构体
允许不使用new对其实例化  必须使用new实例化
没有默认构造方法 有默认构造方法
不能继承类  能继承类
没有析构方法 有析构方法
不允许使用abstract、protected以及sealed修饰 允许使用abstract、protected以及sealed修饰

上一篇:C# string字符串、正则表达式的讲解

下一篇:没有了

讨论数量:0

请先登录再发表讨论。 2024-11-24

天涯网魂
3 杠 5 星
TA 的文章
TA 的随言
TA 的资源链