02、C#基础语法(二)

基础语法(二)


数组

数组从字面上理解就是存放一组数,但在 C# 语言中数组存放的并不一定是数字,也可以是其他数据类型。

声明数组时,方括号[]必须跟在类型后面,而不是标识符后面。

int[] table;

一维数组

一维数组在数组中最常用,即将一组值存放到一个数组中,并为其定义一个名称,通过数组中元素的位置来存取值。

数据类型[] 数组名;

数组的初始化有如下几种方式:

//初始化数组中的元素
数据类型[] 数组名 = new 数据类型[长度];
数据类型[] 数组名 = {值 1, 值 2, ...}
数据类型[] 数组名 = new 数据类型[长度]{值 1,值 2,...}

在定义数组时定义的数据类型代表了数组中每个元素的数据类型。

由于在数组中存放了多个元素,在存取数组中的元素时要使用下标来存取,类似于取字符串中的字符。

class Program {
    static void Main(string[] args) {
        //定义 int 类型的数组
        int[] a = {1,2,3};
        //输岀数组中的一个元素
        Console.WriteLine(a[0]);
        //输出数组中的最后一个元素
        Console.WriteLine(a[a.Length-1]);
    }
}

获取数组的长度使用的是数组的Length属性,数组的下标仍然从 0 开始,数组中的最后一个元素是数组的长度减 1。

创建一个字符串类型的数组,并存入 5 个值,然后将数组中下标是偶数的元素输出。

class Program {
    static void Main(string[] args) {
        string[] strs = {"aaa", "bbb", "ccc", "ddd", "eee"};
        for (int i = 0; i < strs.Length; i++) {
            if (i % 2 == 0) {
                Console.WriteLine(strs[i]);
            }
        }
    }
}

创建 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];
        for (int i = 0; i < a.Length; i++) {
            if (a[i] > max) {
                max = a[i];
            }
        }
        Console.WriteLine("数组中最大值为:{0}", max);
    }
}

多维数组

在 C# 语言里多维数组中比较常用的是二维数组。

定义多维数组的语法形式如下:

数据类型[ , , ...] 数组名;

多维数组的初始化方式如下:

数据类型[ , , ...] 数组名 = new 数据类型[m,n,...] {{ , , ...},{ , , ...}};

存取数组中的值也是将下标用“,”隔开。

定义一个存放学生成绩的二维数组,并将该数组中每个学生的成绩输出。

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 开始的。

锯齿数组

C# 语言中不仅支持上面给出的多维数组,也支持锯齿型数组,即在多维数组中的每一维中所存放值的个数不同。

锯齿型数组也被称为数组中的数组。定义锯齿型数组的语法形式如下。

数据类型[][] 数组名 = new 数据类型[数组长度][];
数组名[0] = new 数据类型[数组长度];

在这里,数据类型指的是整个数组中元素的类型,在定义锯齿型数组时必须要指定维度。

创建一个锯齿型数组,第一维数组的长度是 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();
        }
    }
}

锯齿型数组中的值也可以通过循环语句来赋值,与输岀语句类似。

foreach语句

foreach 循环用于列举出集合中所有的元素,foreach 语句中的表达式由关键字 in 隔开的两个项组成。

foreach 语句经常与数组一起使用,在 C# 语言中提供了 foreach 语句遍历数组中的元素,具体的语法形式 如下:

foreach(数据类型 变量名 in 数组名) {
//语句块;
}

这里变量名的数据类型必须与数组的数据类型相兼容。

foreach 语句仅能用于数组、字符串或集合类数据类型。

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 += point;
        }
        avg = sum / points.Length;
        Console.WriteLine("总成绩为:{0}", sum);
        Console.WriteLine("平均成绩为:{0}", avg);
    }
}

Split切割

Split 方法用于按照指定的字符串来拆分原有字符串,并返回拆分后得到的字符串数组。

从控制台输入一个字符串,然后计算该字符串中包含的逗号的个数。

class Program {
    static void Main(string[] args) {
        Console.WriteLine("请输入一个字符串:");
        string str = Console.ReadLine();
        string[] strs = str.Split(",");
        foreach (var s in strs) {
            Console.Write(s + " ");
        }
    }
}

在使用 Split 方法时,需要注意该方法中传递的参数 (condition, StringSplitOptions.None)。

第一个参数:拆分的条件数组,可以在该数组中存放多个字符串作为拆分的条件。
第二个参数:StringSplitOptions.None 是拆分的选项,表示如果在拆分时遇到空字符也拆分出一个元素。

枚举类型

枚举类型和结构体类型都是特殊的值类型,应用也比较广泛。

枚举类型在定义时使用 enum 关键字表示,枚举类型的定义与类成员的定义是一样的,或者直接定义在命名空间中。

访问修饰符 enum 变量名 : 数据类型 {
值l,
值2,
}

修饰符:与类成员的访问修饰符一样,省略访问修饰符也是代表使用 private 修饰符的。

数据类型:指枚举中值的数据类型。只能是整数类型,包括 byte、short、int、long 等。

值:在枚举类型中显示的值。但实际上每个值都被自动赋予了一个整数类型值,并且值是递增加 1 的,默认是从 0 开始。

class Program {
    public enum Season : int {
        春天,
        夏天,
        秋天,
        冬天
    }
    static void Main(string[] args) {
        Console.WriteLine(Season.春天 + ":" + (int)Season.春天);
        Console.WriteLine(Season.夏天 + ":" + (int)Season.夏天);
        Console.WriteLine(Season.秋天 + ":" + (int)Season.秋天);
        Console.WriteLine(Season.冬天 + ":" + (int)Season.冬天);
    }
}

结构体类型

在结构体中能定义字段、属性、方法等成员。定义的语法形式如下:

访问修饰符 struct 结构体名称 {
// 结构体成员
}

访问修饰符:通常使用 public 或者省略不写,如果省略不写,代表使用 private 来修饰。

结构体名称:命名规则通常和变量的命名规则相同,即从第二个单词开始每个单词的首字母大写。

结构体成员:包括字段、属性、方法以及后面要介绍的事件等。

在结构体中也能编写构造器,但必须带参数,并且必须为结构体中的字段赋初值。

class Program {
    public struct student {
        private string name;
        private int age;

        public student(string name, int age) {
            this.name = name;
            this.age = age;
        }

        public void printStudent() {
            Console.WriteLine("姓名:{0}", name);
            Console.WriteLine("年龄:{0}", age);
        }
    }
    
    static void Main(string[] args) {
        student student = new student("李四", 25);
        student.printStudent();
    }
}

结构体与类有些类似,但其定义却有很大区别,具体如下表所示:

结构体
允许不使用new对其实例化 必须使用new实例化
没有默认构造方法 有默认构造方法
不能继承类 能继承类
没有析构方法 有析构方法
不允许使用abstract、protected以及sealed修饰 允许使用abstract、protected以及sealed修饰

集合

集合与数组比较类似,都用于存放一组值,但集合中提供了特定的方法能直接操作集合中的数据,并提供了不同的集合类来实现特定的功能。

集合类或与集合相关的接口命名空间都是 System.Collection,该命名空间中提供的常用接口如下表所示。

接口名称 作用
IEnumerable 用于迭代集合中的项,该接口是一种声明式的接口
IEnumerator 用于迭代集合中的项,该接口是一种实现式的接口
ICollection .NET 提供的标准集合接口,所有的集合类都会直接或间接地实现这个接口
IList 继承自 IEnumerable 和 ICollection 接口,用于提供集合的项列表,并允许访问、查找集合中的项
IDictionary 继承自 IEnumerable 和 ICollection 接口,与 IList 接口提供的功能类似,但集 合中的项是以键值对的形式存取的
IDictionaryEnumerator 用于迭代 IDictionary 接口类型的集合

针对上表中的接口有一些常用的接口实现类,如下表所示:

类名称 实现接口 特点
ArrayList ICollection、IList、IEnumerable、ICloneable 集合中元素的个数是可变的,提供添加、删除等方法
Queue ICollection、IEnumerable、ICloneable 集合实现了先进先出的机制,即元素将在集合的尾部添加、在集合的头部移除
Stack ICollection、IEnumerable、ICloneable 集合实现了先进后出的机制,即元素将在集合的尾部添加、在集合的尾部移除
Hashtable IDictionary、ICollection、IEnumerable、 ICloneable 等接口 集合中的元素是以键值对的形式存放的,是 DictionaryEntry 类型的
SortedList IDictionary、ICollection、IEnumerable、 ICloneable 等接口 与 Hashtable 集合类似,集合中的元素以键值对的形式存放,不同的是该集合会按照 key 值自动对集合中的元素排序

ArrayList

ArrayList 类(动态数组)是一个最常用的集合类,与数组的操作方法也是最类似的。

ArrayList 代表了可被单独索引的对象的有序集合。它基本上可以替代一个数组。

构造方法 作用
ArrayList() 创建 ArrayList 的实例,集合的容量是默认初始容量
ArrayList(ICollection c) 创建 ArrayList 的实例,该实例包含从指定实例中复制的元素,并且初始容量与复制的元素个数相同
ArrayList(int capacity) 创建 ArrayList 的实例,并设置其初始容量

下面分别使用 ArrayList 类的构造器创建 ArrayList 实例,代码如下。

ArrayList listl=new ArrayList();
ArrayList list2=new ArrayList(listl);
ArrayList list3=new ArrayList(20);

在创建 ArrayList 类的实例后,集合中还未存放值。

在 C# 语言中提供了集合初始化器,允许在创建集合实例时向集合中添加元素,代码如下:

ArrayList list = new ArrayList(){1,2,3,4};

集合与数组一样也能使用 foreach 语句遍历元素。

class Program {
    static void Main(string[] args) {
        ArrayList list = new ArrayList(){1, 2, 3, 4};
        foreach (var v in list) {
            Console.WriteLine(v);
        }
    }
}

在 ArrayList 类中提供了很多属性和方法供开发人员调用,以便简化更多的操作。

ArrayList 类中常用的属性和方法如下表所示。

属性或方法 作用
int Add(object value) 向集合中添加 object 类型的元素,返回元素在集合中的下标
void AddRange(ICollection c) 向集合中添加另一个集合 c
Capacity 属性,用于获取或设置集合中可以包含的元素个数
void Clear() 从集合中移除所有元素
bool Contains(object item) 判断集合中是否含有 item 元素,若含有该元素则返回 True, 否则返回 False
void CopyTo(Array array) 从目标数组 array 的第 0 个位置开始,将整个集合中的元素复制到类型兼容的数组 array 中
void CopyTo(Array array,int arraylndex) 从目标数组 array 的指定索引 arraylndex 处,将整个集合中的元素赋值到类型兼容的数组 array 中
void CopyTo(int index,Array array,int arrayIndex,int count) 从目标数组 array 的指定索引 arrayindex 处,将集合中从指定索引 index 开始的 count 个元素复制到类型兼容的数组 array 中
Count 属性,用于获取集合中实际含有的元素个数
int IndexOf(object value) 返回 value 值在集合中第一次出现的位置
int IndexOf(object value,int startIndex) 返回 value 值在集合的 startindex 位置开始第一次出现的位置
int IndexOf(object value,int startIndex,int count) 返回 value 值在集合的 startindex 位置开始 count 个元素中第一次出现的位置
int LastIndexOf(object value) 返回 value 值在集合中最后一次出现的位置
int LastIndexOf(object value,int startIndex) 返回 value 值在集合的 startindex 位置开始最后一次出现的位置
int LastIndexOf(object value,int startIndex,int count) 入元素 value值在集合的 startindex 位置开始 count 个元素中最后一次出现的位置
void Insert(int index,object value) 返回 value 向集合中的指定索引 index 处插
void InsertRange(int index,ICollection c) 向集合中的指定索引 index 处插入一个集合
void Remove(object obj) 将指定兀素 obj 从集合中移除
void RemoveAt(int index) 移除集合中指定位置 index 处的元素
void RemoveRange(int index,int count) 移除集合中从指定位置 index 处的 count 个元素
void Reverse() 将集合中的元素顺序反转
void Reverse(int index,int count) 将集合中从指定位置 index 处的 count 个元素反转
void Sort() 将集合中的元素排序,默认从小到大排序
void Sort(IComparer comparer) 将集合中的元素按照比较器 comparer 的方式排序
void Sort(int index,int count,IComparer comparer) 将集合中的元素从指定位置 index 处的 count 个元素按照比较器 comparer 的方式排序
void TrimToSize() 将集合的大小设置为集合中元素的实际个数

查找集合中的元素,使用 IndexOf 或者 LastlndexOf 都可以,代码如下。

class Program {
    static void Main(string[] args) {
        ArrayList list = new ArrayList() { "aaa", "bbb", "abc", 123, 456 };
        int index = list.IndexOf("abc");
        if (index != -1) {
            Console.WriteLine("集合中存在 abc 元素!");
        } else {
            Console.WriteLine("集合中不存在 abc 元素!");
        }
    }
}

将集合中下标为偶数的元素添加到另一个集合中:

class Program {
    static void Main(string[] args) {
        ArrayList list = new ArrayList() { "aaa", "bbb", "abc", 123, 456 };
        ArrayList newList = new ArrayList();
        for(int i = 0; i < list.Count; i = i + 2) {
            newList.Add(list[i]);
        }
        foreach(var v in newList) {
            Console.WriteLine(v);
        }
    }
}

在集合中的第一个元素后面添加元素,使用 Insert 方法每次只能添加一个元素,但使用 InsertRange 方法能直接将一个集合插入到另一个集合中。

class Program {
    static void Main(string[] args) {
        ArrayList list = new ArrayList() { "aaa", "bbb", "abc", 123, 456 };
        ArrayList insertList = new ArrayList() { "A", "B", "C" };
        list.InsertRange(1, insertList);
        foreach(var v in list) {
            Console.WriteLine(v);
        }
    }
}

使用 Sort 方法对集合中的元素排序,则需要将集合中的元素转换为同一类型才能比较,否则会出现无法比较的异常。

class Program {
    static void Main(string[] args) {
        ArrayList list = new ArrayList() { "aaa", "bbb", "abc" };
        list.Sort();
        foreach(var v in list) {
            Console.WriteLine(v);
        }
    }
}

默认是按照自然排序的顺序,如果需要比较字符串的值则需要使用CompareTo方法。

字符串1.CompareTo(字符串2);

在 ArrayList 类中常用的属性和方法表中列出的 Sort 方法中,Sort 方法能传递 IComparer 类型的参数。

1)定义比较器,用于比较两个字符串

class MyCompare : IComparer {
    public int Compare(object? x, object? y) {
        string str1 = x.ToString();
        string str2 = y.ToString();
        return str1.CompareTo(str2);
    }
}

2)在sort方法中使用自定义的比较器进行比较:

class Program {
    static void Main(string[] args) {
        ArrayList list = new ArrayList() { "a", "b", "c", 1, 2 };
        MyCompare myCompare = new MyCompare();//创建自定义比较器实例
        list.Sort(myCompare);
        foreach(var v in list) {
            Console.WriteLine(v);
        }
    }
}

Queue

Queue (队列) 是常见的数据结构之一,队列是一种先进先出的结构。

Queue 类提供了 4 个构造方法,如下表所示:

构造方法 作用
Queue() 创建 Queue 的实例,集合的容量是默认初始容量 32 个元素,使用默认的增长因子
Queue(ICollection col) 创建 Queue 的实例,该实例包含从指定实例中复制的元素,并且初始容量与复制的元素个数、增长因子相同
Queue(int capacity) 创建 Queue 的实例,并设置其指定的元素个数,默认增长因子
Queue(int capacity, float growFactor) 创建 Queue 的实例,并设置其指定的元素个数和增长因子

增长因子是指当需要扩大容量时,以当前的容量(capacity)值乘以增长因子(growFactor)的值来自动增加容量。

下面使用上表中的构造方法来创建 Queue 的实例,代码如下:

//第 1 中构造器
Queue queueq1 = new Queue();
//第 2 中构造器
Queue queueq2 = new Queue(queue1);
//第 3 中构造器
Queue queueq3 = new Queue(30);
//第 4 中构造器
Queue queueq4 = new Queue(30, 2);

注意:Queue 类不能在创建实例时直接添加值。

Queue类中常用的属性和方法如下表所示:

属性或方法 作用
Count 属性,获取 Queue 实例中包含的元素个数
void Clear() 清除 Queue 实例中的元素
bool Contains(object obj) 判断 Queue 实例中是否含有 obj 元素
void CopyTo(Array array, int index) 将 array 数组从指定索引处的元素开始复制到 Queue 实例中
object Dequeue() 移除并返回位于 Queue 实例开始处的对象
void Enqueue(object obj) 将对象添加到 Queue 实例的结尾处
object Peek() 返回位于 Queue 实例开始处的对象但不将其移除
object[] ToArray() 将 Queue 实例中的元素复制到新数组
void TrimToSize() 将容量设置为 Queue 实例中元素的实际数目
IEnumerator GetEnumerator() 返回循环访问 Queue 实例的枚举数

下面通过实例来演示 Queue 类的使用:

1)创建 Queue 类的实例,模拟排队购电影票的操作。

class Program {
    static void Main(string[] args) {
        Queue queue = new Queue();
        queue.Enqueue("小张");
        queue.Enqueue("小李");
        queue.Enqueue("小刘");
        Console.WriteLine("购票开始");
        // 当队列中没有人时,购票结束
        while (queue.Count != 0) {
            Console.WriteLine(queue.Dequeue() + "已购票。");
        }
        Console.WriteLine("购票结束...");
    }
}

2)向 Queue 类的实例中添加 3 个值,在不移除队列中元素的前提下将队列中的元素依次输出。

class Program {
    static void Main(string[] args) {
        Queue queue = new Queue();
        queue.Enqueue("aaa");
        queue.Enqueue("bbb");
        queue.Enqueue("ccc");
        object[] obj = queue.ToArray();
        foreach(var v in obj) {
            Console.WriteLine(v);
        }
    }
}

除了使用 ToArray() 方法以外,还可以使用 GetEnumerator() 方法来遍历,实现的代码如下:

class Program {
    static void Main(string[] args) {
        Queue queue = new Queue();
        queue.Enqueue("aaa");
        queue.Enqueue("bbb");
        queue.Enqueue("ccc");
        IEnumerator enumerator = queue.GetEnumerator();
        while (enumerator.MoveNext()) {
            Console.WriteLine(enumerator.Current);
        }
    }
}

Stack

Stack (栈)是常见的数据结构之一,栈是一种先进后出的结构,即元素从栈的尾部插入,从栈的尾部移除。

Stack 类提供了 3 种构造方法,如下表所示。

构造方法 作用
Stack() 使用初始容量创建 Stack 的对象
Stack(ICollection col) 创建 Stack 的实例,该实例包含从指定实例中复制的元素,并且初始容量与复制的元素个数、增长因子相同
Stack(int capacity) 创建 Stack 的实例,并设置其初始容量

Stack 类中的常用属性和方法如下表所示

属性或方法 作用
Push(object obj) 向栈中添加元素,也称入栈
object Peek() 用于获取栈顶元素的值,但不移除栈顶元素的值
object Pop() 用于移除栈顶元素的值,并移除栈顶元素
Clear() 从 Stack 中移除所有的元素
Contains(object obj) 判断某个元素是否在 Stack 中
object[] ToArray() 复制 Stack 到一个新的数组中

下面通过实例来演示 Stack 类的使用。

1) 创建一个栈(Stack),模拟餐馆盘子的存取。

class Program {
    static void Main(string[] args) {
        Stack stack = new Stack();
        //向栈中存放元素
        stack.Push("1 号盘子");
        stack.Push("2 号盘子");
        stack.Push("3 号盘子");
        stack.Push("4 号盘子");
        stack.Push("5 号盘子");
        Console.WriteLine("取出盘子:");
        //判断栈中是否有元素
        while(stack.Count != 0) {
            //取出栈中的元素
            Console.WriteLine(stack.Pop());
        }
    }
}

执行结果如下:

取出盘子:
5 号盘子
4 号盘子
3 号盘子
2 号盘子
1 号盘子

从上面的执行效果可以看出,通过 Stack 类提供的 Pop 方法可以依次从栈顶取出栈中的每一个元素。

Hashtable

Hashtable 类实现了 IDictionary 接口,集合中的值都是以键值对的形式存取的。

C# 中的 Hashtable 称为哈希表,也称为散列表,在该集合中使用键值对(key/value)的形式存放值。

Hashtable 类提供的构造方法有很多,最常用的是不含参数的构造方法:

Hashtable hashtable = new Hashtable ();

Hashtable 类中常用的属性和方法如下表所示:

属性或方法 作用
Count 集合中存放的元素的实际个数
void Add(object key,object value) 向集合中添加元素
void Remove(object key) 根据指定的 key 值移除对应的集合元素
void Clear() 清空集合
ContainsKey (object key) 判断集合中是否包含指定 key 值的元素
ContainsValue(object value) 判断集合中是否包含指定 value 值的元素

1)使用 Hashtable 集合实现图书信息的添加、查找以及遍历的操作

class Program {
    static void Main(string[] args) {
        Hashtable hashtable = new Hashtable();
        hashtable.Add(1, "计算机基础");
        hashtable.Add(2, "C#高级编程");
        hashtable.Add(3, "数据库应用");
        Console.WriteLine("请输入图书编号");
        int id = int.Parse(Console.ReadLine());
        bool flag = hashtable.ContainsKey(id);
        if (flag) {
            Console.WriteLine("您查找的图书名称为:{0}", hashtable[id].ToString());   
        } else {
            Console.WriteLine("您查找的图书编号不存在!");
        }
        Console.WriteLine("所有图书信息如下:");
        foreach (DictionaryEntry dictionaryEntry in hashtable) {
            int key = (int)dictionaryEntry.Key;
            string value = dictionaryEntry.Value.ToString();
            Console.WriteLine("图书编号:{0},图书名称:{1}", key, value);
        }
    }
}

SortedList

SortedList 类实现了 IDictionary 接口 ,集合中的值都是以键值对的形式存取的。

C# SortedList 称为有序列表,按照 key 值对集合中的元素排序。

使用 SortedList 实现挂号信息的添加、查找以及遍历操作。

class Program {
    static void Main(string[] args) {
        SortedList sortedList = new SortedList();
        sortedList.Add(1, "小张");
        sortedList.Add(2, "小李");
        sortedList.Add(3, "小刘");
        Console.WriteLine("请输入挂号编号:");
        int id = int.Parse(Console.ReadLine());
        bool flag = sortedList.ContainsKey(id);
        if (flag) {
            string name = sortedList[id].ToString();
            Console.WriteLine("查找的患者姓名为:{0}", name);
        } else {
            Console.WriteLine("查找的挂号编号不存在!");
        }
        
        Console.WriteLine("所有的挂号信息如下:");
        foreach (DictionaryEntry dictionaryEntry in sortedList) {
            int key = (int) dictionaryEntry.Key;
            string value = dictionaryEntry.Value.ToString();
            Console.WriteLine("挂号编号:{0}, 姓名:{1}", id, value);
        }
    }
}

泛型

泛型是 C#2.0 推出的新语法,不是语法糖,而是 2.0 由框架升级提供的功能。

泛型是在 System.Collections.Generic 命名空间中的,用于约束类或方法中的参数类型。

泛型的应用非常广泛,包括方法、类以及集合等。

在 ArrayList 中以 double 类型存入学生考试成绩,但存入值时并没有做验证,存入了其他数据类型的值,代码如下。

ArrayList arrayList=new ArrayList();
arrayList.Add(100);
arrayList.Add("abc");
arrayList.Add(85.5);

在输出集合中的元素时,如果使用 double 类型来遍历集合中的元素,代码如下。

foreach (int d in arrayList){
    Console.WriteLine(d);
}

执行上面的代码,由于在集合中存放的并不全是 double 类型的值,因此会出现 System.InvalidCastException 异常,即指定的转换无效。

为了避免类似的情况产生,将集合中元素的类型都指定为 double 类型,不能在集合中输入其他类型的值,这种设置方式即为泛型的一种应用。

Nullable

在 C# 语言中提供了一种泛型类型(即可空类型 (System.Nullable))来解决值类型的变量在未赋值的情况下允许为 Null 的情况。

System.Nullable<T> 变量名;

例如定义一个int类型的变量:

Nullable<int> a;

除了使用上面的方法定义可空类型变量以外,还可以通过如下语句定义一个 int 类型的可空类型变量。

int? a

从上面的定义可以看出,int? 等同于Nullable<int>

此外,在使用可空类型时也可以通过 HasValue 属性判断变量值是否为 Null 值。

创建一个 int 的可空类型变量和 double 的可空类型变量,并使用 HasValue 属性判断其值是否为空。

class Program {
    static void Main(string[] args) {
        int? i = null;
        double? d = 3.14;
        if (i.HasValue) {
            Console.WriteLine("i 的值为{0}", i);
        }else {
            Console.WriteLine("i 的值为空!");
        }

        if (d.HasValue) {
            Console.WriteLine("d 的值为{0}", d);
        } else {
            Console.WriteLine("d 的值为空!");
        }
    }
}

泛型方法

在 C# 语言中泛型方法是指通过泛型来约束方法中的参数类型,也可以理解为对数据类型设置了参数。

定义泛型方法需要在方法名和参数列表之间加上<>,并在其中使用 T 来代表参数类型。

class Program {
    static void Main(string[] args) {
        add<double>(3.3, 4);
        add<int>(3, 4);
    }

    private static void add<T>(T a, T b) {
        double sum = double.Parse(a.ToString()) + double.Parse(b.ToString());
        Console.WriteLine(sum);
    }
}

从上面的执行效果可以看出,在调用 Add 方法时能指定不同的参数类型执行加法运算。

泛型类

C# 语言中泛型类的定义与泛型方法类似,是在泛型类的名称后面加上,当然,也可以定义多个类型。

class 类名<T1,T2,…> {
//类的成员
}

这样,在类的成员中即可使用 T1、T2 等类型来定义。

定义泛型类,并在泛型类中定义数组,提供添加和显示数组中全部元素的 方法。

public class MyTest<T> {
    private T[] items = new T[3];
    private int index = 0;

    public void add(T t) {
        if (index < 3) {
            items[index] = t;
            index++;
        } else {
            Console.WriteLine("数组已满!");
        }
    }

    public void show() {
        foreach (T t in items) {
            Console.WriteLine(t);
        }
    }
}

在 Main 方法中调用 MyTest 类中的方法,代码如下:

class Program {
    static void Main(string[] args) {
        MyTest<int> test = new MyTest<int>();
        test.add(10);
        test.add(20);
        test.add(30);
        test.show();
    }
}

根据泛型类中指定的数据类型创建数组,并实现了对数组元素的添加和显示。

泛型集合

C# 语言中泛型集合是泛型中最常见的应用,主要用于约束集合中存放的元素。

非泛型集合中的 ArrayList、Hashtable 在泛型集合中分别使用 List 和 Dictionary<K,V> 来表示,其他泛型集合均与非泛型集合一致。

下面以 List 和 Dictionary<K,V> 为例介绍泛型集合的使用。

1)使用泛型集合 List 实现对学生信息的添加和遍历。

class Program {
    static void Main(string[] args) {
        List<Student> list = new List<Student>();
        list.Add(new Student(1, "小明", 28));
        list.Add(new Student(2, "小李", 21));
        list.Add(new Student(3, "小赵", 26));
        foreach (Student student in list) {
            Console.WriteLine(student);
        }
    }
}

class Student {
    private int id;
    private string name;
    private int age;

    public Student(int id, string name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int Id {
        get => id;
        set => id = value;
    }

    public string Name {
        get => name;
        set => name = value;
    }

    public int Age {
        get => age;
        set => age = value;
    }

    public override string ToString() {
        return id + ":" + name + ":" + age;
    }
}

2)使用泛型集合 Dictionary<K,V> 实现学生信息的添加,并能够按照学号查询学生信息。

class Program {
    static void Main(string[] args) {
        Dictionary<int, Student> dictionary = new Dictionary<int, Student>();
        List<Student> list = new List<Student>();
        list.Add(new Student(1, "小明", 28));
        list.Add(new Student(2, "小李", 21));
        list.Add(new Student(3, "小赵", 26));
        dictionary.Add(stu1.id, stu1);
        dictionary.Add(stu2.id, stu2);
        dictionary.Add(stu3.id, stu3);
        Console.WriteLine("请输入学号:");
        int id = int.Parse(Console.ReadLine());
        if (dictionary.ContainsKey(id)) {
            Console.WriteLine("学生信息为:{0}", dictionary[id]);
        } else {
            Console.WriteLine("您查找的学号不存在!");
        }
    }
}

IComparable & IComparer

在 C# 语言中提供了 IComparer 和 IComparable 接口比较集合中的对象值,主要用于对集合中的元素排序。

IComparer 接口用于在一个单独的类中实现,用于比较任意两个对象。
IComparable 接口用于在要比较的对象的类中实现,可以比较任意两个对象。

在比较器中还提供了泛型接口的表示形式,即 IComparer 和 IComparable 的形式。

IComparer

对于 IComparer 接口,方法如下表所示。

方法 作用
CompareTo(T obj) 比较两个对象值

如果需要对集合中的元素排序,通常使用 CompareTo 方法实现,下面通过实例来演示 CompareTo 方法的使用。

使用 CompareTo 方法实现比较简单。在 Student 类中添加 CompareTo 方法,代码如下:

class Program {
    static void Main(string[] args) {
        List<Student> list = new List<Student>();
        list.Add(new Student(1, "小明", 28));
        list.Add(new Student(2, "小李", 21));
        list.Add(new Student(3, "小赵", 26));
        foreach (Student student in list) {
            Console.WriteLine(student);
        }
    }
}

class Student :IComparable<Student>{
    private int id;
    private string name;
    private int age;

    public Student(int id, string name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int Id {
        get => id;
        set => id = value;
    }

    public string Name {
        get => name;
        set => name = value;
    }

    public int Age {
        get => age;
        set => age = value;
    }

    public int CompareTo(Student? other) {
        if (this.age > other.age) {
            return 1;
        }
        return -1;
    }

    public override string ToString() {
        return id + ":" + name + ":" + age;
    }
}

运行上面的程序,输出的结果会按照年龄进行排序:

1:小明:20
2:小李:25
3:小赵:26

**IComparable **

上面的示例也可以使用 IComparer 接口来实现,IComparer 接口中的方法如下表所示

方法 作用
Compare(T obj1,T obj2) 比较两个对象值

下面通过实例演示 IComparer 接口的使用:

1)先定义一个比较器的类,再实现对集合中元素的排序

class MyCompare : IComparer<Student> {
    public int Compare(Student? x, Student? y) {
        if (x.Age > y.Age) {
            return 1;
        }
        return -1;
    }
}

2)在 Main 方法中应用该比较器对集合中的元素排序,代码如下:

class Program {
    static void Main(string[] args) {
        List<Student> list = new List<Student>();
        list.Add(new Student(1, "小明", 28));
        list.Add(new Student(2, "小李", 21));
        list.Add(new Student(3, "小赵", 26));
        list.Sort(new MyCompare());
        foreach (Student student in list) {
            Console.WriteLine(student);
        }
    }
}

论使用 IComparer 接口还是 IComparable 接口都能自定义在集合中使用 Sort 方法时的排序。

原文地址:https://www.cnblogs.com/pengjingya/p/14401333.html