小工具知识网

当前位置: 首页 > 行业知识 > C#泛型:高级静态语言的效率利器

行业知识

C#泛型:高级静态语言的效率利器

2026-11-15 01:24:34 来源:互联网转载

文章目录

    • 引入
    • 类型约束
    • 子类泛型
    • 常用的泛型数据结构
    • ���型委托

      前文提要:

      ()
      • 超快速成,零基础掌握C#开发中最重要的概念
      • 抽丝剥茧,C#面向对象快速上手
      • Winform,最友好的桌面GUI框架
      • 懂了委托,才算真正入门C#

        引入

        所谓泛型,就是创建一个函数,对所有数据类型都生效。最常见的例子就是运算符,毕竟1+1=2,1.0+1.0=2.0,足以看出+是对多种数据类型起作用的。

        但是,如想创建一个函数add(int a, int b),那么输入add(1.0, 1.0)是肯定要报错的,VS直接就给标红了。

        ()

        泛型的出现,就很好地解决了这个尴尬的问题

        T add(T a, T b) {    dynamic d1 = a;    dynamic d2 = b;    return (T)(d1 + d2);}Console.WriteLine(add(1, 1));Console.WriteLine(add(1.0, 1.0));

        上面代码中,T表示某种数据类型,在调用函数add时,根据add后面的加以声明。

        但如果就此就写return a+b显然也是不行的,因为+这种运算符并没有对T进行重载,编辑器并不会允许两种未知的类型相加。

        这个时候就需要用到dynamic,用来让编辑器放弃类型检查,将任何可能发生的错误都留给运行阶段。

        最后,运行结果为

        22

        类型约束

        dynamic用着确实爽,但后果就是责任自负,这玩意要是用在团队协作的场合,简直就是灾难,毕竟并非所有对象都可以驾驭加法。

        所以,C#的泛型,是可以被约束的泛型,关键就是where,将上述代码写为

        T add(T a, T b) where T : struct{    dynamic d1 = a;    dynamic d2 = b;    return (T)(d1 + d2);}

        where T : struct表示T必须是数值类型的一种,所以编译器的类型检查仍会发挥作用,在调用add时,如果T不是数值类型,就会报错。

        C#一共有5种约束方案,列表如下

        类别条件
        structT必须是值类型
        classT必须是引用类型
        new()T必须有无参数的构造函数
        基类名T必须是基类或派生自基类
        接口名T必须是指定接口
        裸类型

        不同类型的约束,或相同类型不同种类的约束,一般是可以混用的,如果不能混用,编译器会提醒。比如struct几乎不能和其他类型混用。如果new()参与了约束,则放在最后。

        子类泛型

        除了函数可以采用泛型,类当然也可以,不仅可以,而且还能继承。

        class MyList{    public T[] a;    public MyList(){}       //无参数的构造函数,用于继承    public MyList(int n){        a = new T[n];    }    public T this[int index]{        get => a[index];        set => a[index] = value;    }}

        MyList相当于是给数组套了一层壳,其构造函数并不存在什么难以理解的地方,唯一有些问题的可能是下面的索引器public T this[int index],这种写法可以实现方括号形式的索引。

        可以测试一下

        var a = new MyList(5);for (int i = 0; i  

        结果就不粘贴了,接下来新建一个子类

        class MyStack : MyList{    public MyStack(int n)    {        a = new T[n];    }    public T Pop()    {        T p = a[a.Length- 1];        a = a[0..(a.Length-1)];        return p;    }}

        然后测试一下

        var a = new MyStack(3);for (int i = 0; i  

        结果为

        210

        常用的泛型数据结构

        C#通过泛型定义了很多数据结构,例如在讲解switch...case时提到的字典

        Dictionary card = new Dictionary{    {1,"A" },    {11, "J" },    {12, "Q" },    {13, "K" }};

        这种的写法,正是泛型的特点,其中U, V就是可以随意声明的变量。如果查看字典的类型参数,可以发现其定义方法是这样的

        public class Dictionary : ICollection, ... where TKey : notnull

        考虑到本节并不是为了将面向对象,所以字典继承的那一大坨类就省略了,关键是where Tkey:notnull,也就是说,字典对键值对的要求只有一个,就是键不得为null。

        除了字典之外,还有一些常见的数据结构采用了泛型,列表如下,没事儿可以练习练习。

        数据结构说明常用方法
        List泛型列表Add, Remove, RemoveAt
        LinkedList双端链表AddFirst, AddLast, RemoveFirst, RemoveLast
        Queue先进先出列表Enqueue, Dequeue
        Stack栈,先进后出Push, Pop

        泛型委托

        委托,是函数的函数;泛型,可以让函数的参数类型更加灵活,二者结合在一起,就是更加灵活的函数的函数,即泛型委托。

        只要学过了泛型和委托,那么对泛型委托将毫无理解上的难度,回想前面定义的运算符委托

        delegate int Op(int a, int b);

        再回想定义泛型时的,那么泛型委托可以非常简单地定义出来

        delegate T Op(T a, T b);

        然后就可以根据委托,建立一个泛型函数

        T add(T a, T b){    dynamic d1 = a;    dynamic d2 = b;    return (T)(d1 + d2);}var addTest = new Op(add);//也可以省略add后的,写成下面的形式//var addTest = new Op(add);Console.WriteLine(addTest(3, 5));

        运行之后控制台出现了8,就是这么简单。

上一篇:服务器怎么维护升级?

下一篇:华硕服务器在哪里看?