9.泛型
9.1 简介
泛型的目的是为了解决用同一个方法来处理传入不同种类型参数的问题。
// 集合中的项允许是object型的值,因此可以存放任意类型的值
ArrayList arrayList=new ArrayList();
arrayList.Add(100);
arrayList.Add("abc");
arrayList.Add(85.5);
foreach (int d in arrayList)
{
Console.WriteLine(d);
}
执行上面的代码,由于在集合中存放的并不全是 double 类型的值,因此会出现System.InvalidCastException
异常,即指定的转换无效。为了避免类似的情况产生,将集合中元素的类型都指定为 double 类型,不能在集合中输入其他类型的值,这种设置方式即为泛型的一种应用。
9.2 Nullable:可空类型
对于引用类型的变量来说,如果未对其赋值,在默认情况下是Null值,对于值类型的变量,如果未赋值,整型变量的默认值为0。
可空类型 System.Nullable<T>
是用来解决值类型的变量在未赋值的情况下允许为Null的情况。
System.Nullable<T> 变量名;
其中,Nullable
所在的命名空间 System
在C#类文件中默认是直接引入的,因此可以省略System
,直接使用Nullable
即可;T
代表任意类型,例如定义一个存放int类型值的变量,代码如下。
Nullable<int> a;
// 值设置为 Null
Nullable<int> a = Null;
// int?等同于Nullable<int>
int? a = Null;
// 三元运算符:如果a不为空则返回其自身的值,否则返回-1
int b = a.HasValue ? a.Value:-1 ;、
// 空合并操作符:如果a不为空则返回其自身的值,否则返回-1
int c = a ?? -1;
using System;
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的值为空!");
}
}
}
// i的值为空!
// d的值为3.14
9.3 泛型方法
泛型方法是指通过泛型来约束方法中的参数类型,即可以根据提供的泛型来传递不同类型的参数。定义泛型方法需要在方法名和参数列表之间加上<>
,并在其中使用T
来代表参数类型。
using System;
class Program
{
static void Main(string[] args)
{
//将T设置为double类型
Add<double>(3.3, 4);
//将T设置为int类型
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);
}
}
// 7.3
// 7
9.4 泛型类
泛型类的定义与泛型方法类似,是在泛型类的名称后面加上<T>
,当然,也可以定义多个类型,即<T1,T2,・・・>
。
class 类名<T1,T2,…>
{
//类的成员
}
using System;
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);
}
}
}
class Program
{
static void Main(string[] args)
{
MyTest<int> test = new MyTest<int>();
test.Add(10);
test.Add(20);
test.Add(30);
test.Show();
}
}
// 10
// 20
// 30
9.5 泛型集合
泛型集合是泛型中最常见的应用,主要用于约束集合中存放的元素。由于在集合中能存放任意类型的值,在取值时经常会遇到数据类型转换异常的情况,因此推荐在定义集合时使用泛型集合。非泛型集合中的ArrayList、Hashtable在泛型集合中分别使用List<T>
和Dictionary<K,V>
来表示,其他泛型集合均与非泛型集合一致。
using System;
using System.Collections.Generic;
class Student
{
//提供有参构造方法,为属性赋值
public Student(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}
//学号
public int id { get; set; }
//姓名
public string name { get; set; }
//年龄
public int age { get; set; }
//重写ToString 方法
public override string ToString()
{
return "编号:" + id + "\t姓名:" + name + "\t年龄:" + age;
}
}
class Program
{
static void Main(string[] args)
{
//定义泛型集合
List<Student> list = new List<Student>();
//向集合中存入3名学员
list.Add(new Student(1, "小明", 20));
list.Add(new Student(2, "小李", 21));
list.Add(new Student(3, "小赵", 22));
//遍历集合中的元素
foreach (Student stu in list)
{
Console.WriteLine(stu);
}
}
}
using System;
using System.Collections.Generic;
class Student
{
//提供有参构造方法,为属性赋值
public Student(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}
//学号
public int id { get; set; }
//姓名
public string name { get; set; }
//年龄
public int age { get; set; }
//重写ToString 方法
public override string ToString()
{
return "编号:" + id + "\t姓名:" + name + "\t年龄:" + age;
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<int, Student> dictionary = new Dictionary<int, Student>();
Student stu1 = new Student(1, "小明", 20);
Student stu2 = new Student(2, "小李", 21);
Student stu3 = new Student(3, "小赵", 22);
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("您查找的学号不存在!");
}
}
}
9.6 IComparable、IComparer接口:比较两个对象的值
IComparer和IComparable接口可以比较集合中的对象值,主要用于对集合中的元素排序。IComparer接口用于在一个单独的类中实现,用于比较任意两个对象;IComparable接口用于在要比较的对象的类中实现,可以比较任意两个对象。
在比较器中还提供了泛型接口的表示形式,即IComparer<T>
和IComparable<T>
的形式。
- 对于
IComparer<T>
接口,方法如下表所示。
方法 | 作用 |
---|---|
CompareTo(T obj) | 比较两个对象值 |
如果需要对集合中的元素排序,通常使用CompareTo
方法实现,下面通过实例来演示CompareTo方法的使用。
using System;
using System.Collections.Generic;
class Student : IComparable<Student>
{
//提供有参构造方法,为属性赋值
public Student(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}
//学号
public int id { get; set; }
//姓名
public string name { get; set; }
//年龄
public int age { get; set; }
//重写ToString 方法
public override string ToString()
{
return "编号:" + id + "\t姓名:" + name + "\t年龄:" + age;
}
//定义比较方法,按照学生的年龄比较
public int CompareTo(Student other)
{
if (this.age > other.age)
{
return -1;
}
return 1;
}
}
class Program
{
static void Main(string[] args)
{
List<Student> list = new List<Student>();
list.Add(new Student(1, "小明", 20));
list.Add(new Student(2, "小李", 21));
list.Add(new Student(3, "小赵", 22));
list.Sort();
foreach (Student stu in list)
{
Console.WriteLine(stu);
}
}
}
// 编号:3 姓名:小赵 年龄:22
// 编号:2 姓名:小李 年龄:21
// 编号:1 姓名:小明 年龄:20
在默认情况下,Sort 方法是将集合中的元素从小到大输出的, 由于在Student类中重写了CompareTo方法,因此会按照预先定义好的排序规则对学生信息排序。需要说明的是,在CompareTo方法中返回值大于0则表示第一个对象的值大于第二个对象的值;返回值小于0则表示第一个对象的值小于第二个对象的值;返回值等于 0 则表示两个对象的值相等。
IComparer<T>
接口中的方法如下表所示。
方法 | 作用 |
---|---|
Compare(T obj1,T obj2) | 比较两个对象值 |
在使用IComparer<T>
接口中的Compare方法时,需要单独定义一个类来实现该比较方法。
using System;
using System.Collections.Generic;
class Student
{
//提供有参构造方法,为属性赋值
public Student(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}
//学号
public int id { get; set; }
//姓名
public string name { get; set; }
//年龄
public int age { get; set; }
//重写ToString 方法
public override string ToString()
{
return "编号:" + id + "\t姓名:" + name + "\t年龄:" + age;
}
//定义比较方法,按照学生的年龄比较
}
class MyCompare : IComparer<Student>
{
//比较方法
public int Compare(Student x, Student y)
{
if (x.age > y.age)
{
return -1;
}
return 1;
}
}
class Program
{
static void Main(string[] args)
{
List<Student> list = new List<Student>();
list.Add(new Student(1, "小明", 20));
list.Add(new Student(2, "小李", 21));
list.Add(new Student(3, "小赵", 22));
//在Sort方法中传递自定义比较器作为参数
list.Sort(new MyCompare());
foreach (Student stu in list)
{
Console.WriteLine(stu);
}
}
}
// 编号:3 姓名:小赵 年龄:22
// 编号:2 姓名:小李 年龄:21
// 编号:1 姓名:小明 年龄:20
提示:不仅在泛型集合中允许使用比较器,在非泛型集合中也允许使用比较器,并且可以使用非泛型接口的比较器。