C#泛型

本文详细介绍了C#中的泛型,包括泛型简介、可空类型Nullable的使用、泛型方法和类的定义及应用,以及泛型集合如List和Dictionary的使用。同时,探讨了IComparable和IComparer接口在比较两个对象值时的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

C#泛型简介

在前面《C#集合》一节中介绍了集合,集合中的项允许是 object 型的值,因此可以存放任意类型的值。
例如,在 ArrayList 中以 double 类型存入学生考试成绩,但存入值时并没有做验证,存入了其他数据类型的值,代码如下。

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


//在输出集合中的元素时,如果使用 double 类型来遍历集合中的元素,代码如下。
foreach (double d in arrayList)
{
    Console.WriteLine(d);
}

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

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

C#可空类型:Nullable

对于引用类型的变量来说,如果未对其赋值,在默认情况下是 Null 值,对于值类型的变量,如果未赋值,整型变量的默认值为 0。

但通过 0 判断该变量是否赋值了是不太准确的。

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

定义可空类型变量的语法形式如下。

System.Nullable<T> 变量名;

其中,Nullable所在的命名空间 System 在 C# 类文件中默认是直接引入的,因此可以省略 System,直接使用 Nullable 即可;T 代表任意类型,例如定义一个存放 int 类型值的变量,代码如下。

Nullable<int> a;

这样,可以将变量 a 的值设置为 Null。即:

Nullable<int> a = Null;

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

int? a

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

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

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 的值为空!");
        }
    }  
}
//可空类型允许将值类型变量的值设置为 Null,并可以通过 HasValue 属性判断其是否为 Null 值。

C#泛型方法的定义及使用

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

如果没有泛型,每次方法中的参数类型都是固定的,不能随意更改。

在使用泛型后,方法中的数据类型则有指定的泛型来约束,即可以根据提供的泛型来传递不同类型的参数。

【实例】创建泛型方法,实现对两个数的求和运算。

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);
    }
}

C#泛型类的定义及使用

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

具体的定义形式如下。

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

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

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

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#泛型集合定义及使用

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

由于在集合中能存放任意类型的值,在取值时经常会遇到数据类型转换异常的情况,因此推荐在定义集合时使用泛型集合。

前面《C# ArrayList》与《C# Hashtable》中已经介绍了非泛型集合中的 ArrayList、Hashtable。

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

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

//将学生信息定义为一个类,并在该类中定义学号、姓名、年龄属性。
//在泛型集合 List<T> 中添加学生信息类的对象,并遍历该集合。

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);
        }
    }
}
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 + ":" + name + ":" + age;
    }
}

//在该泛型集合中存放的是 Student 类的对象,当从集合中取岀元素时并不需要将集合中元素的类型转换为 Student 类的类型,而是直接遍历集合中的元素即可,这也是泛型集合的一个特点。

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

//将在实例 1 中所创建学生信息类的对象作为 Dictionary<K,V> 集合中的 value 值部分,key 值部分使用学生信息类中的学号,这样能很容易地通过学号查询学生的信息。实现的代码如下。 
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("您查找的学号不存在!");
        }
    }
}

根据输入的学号直接从 Dictionary<int,Student> 泛型集合中查询出所对应的学生信息,并且在输出学生信息时不需要进行类型转换,直接输出其对应的 Student 类的对象值即可。

C# IComparable、IComparer接口:比较两个对象的值

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

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

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

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

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

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

【实例 1】在上一节《C#泛型集合》中实例 1 的基础上将学生信息按照年龄从大到小输出。

//如果不使用比较器,由于集合中的元素是 Student 类型的,不能直接排序,需要按照 Student 学生信息类中的年龄属性排序,因此代码比较烦琐。
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 + ":" + name + ":" + 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);
        }
    }
}

//在默认情况下,Sort 方法是将集合中的元素从小到大输出的, 由于在 Student 类中重写了 CompareTo 方法,因此会按照预先定义好的排序规则对学生信息排序。

 //需要说明的是,在 CompareTo 方法中返回值大于 0 则表示第一个对象的值大于第二个对象的值,返回值小于 0 则表示第一个对象的值小于第二个对象的值,返回值等于 0 则表示两个对象的值相等。

对于实例 1 中的操作也可以使用 IComparer 接口来实现,IComparer 接口中的方法如下表所示。

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

在使用 IComparer 接口中的 Compare 方法时,需要单独定义一个类来实现该比较方法。

【实例 2】将实例 1 用 IComparer 接口实现。

//先定义一个比较器的类,再实现对集合中元素的排序
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);
        }
    }
}

从上面两个实例可以看出,不论使用 IComparer 接口还是 IComparable 接口都能自定义在集合中使用 Sort 方法时的排序。

提示:不仅在泛型集合中允许使用比较器,在非泛型集合中也允许使用比较器,并且可以使用非泛型接口的比较器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值