C#中泛型详解

本文详细介绍了C#中的泛型概念,包括类的泛型、方法泛型,以及泛型的使用示例。重点讲解了泛型约束,如引用类型、值类型、new约束、类名约束和接口约束,帮助读者掌握如何在代码中优雅地处理不同数据类型和实现约束.

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

1.泛型概念

假如项目中有3种类型的数据变量:int、double、string,需要创建数据存储类,此时需要创建3个,仅仅因为数据类型不一样,这样代码就会冗余,对于大型项目来说,需要打好底层框架,代码看起来不优雅。因此引入泛型优化代码。
泛型概念:使用占位符T来代表某种类型,编译期间决定具体类型
格式:class myclass //泛型类
使用:myclass mys =new myclass(); //泛型类实例化
原理:编译器在编译的时候,会使用特化的类型替代掉类型占位符,生成具体的calss类代码
代码例子如下:

class Store<MyT>
        {
            private MyT[] arr = new MyT[100];
            public void put(MyT v,int index)
            {
                arr[index] = v;
            }
        }
        static void Main(string[] args)
        {
            //对泛型Store的特化,特化为int类型的类
            Store<int> store=new Store<int>();
        }

概念总结:通过“参数化类型”,在同一份代码上操作多种数据类型
泛型可修饰:类、方法、委托等

2.方法的泛型

上述通过类的泛型为例,讲解泛型格式,以下在方法中添加泛型,通过几个例子解析
例子1:交换2个变量的值
方法Swap注释的部分与非注释部分即为将普通写法替换为泛型的情况

class Store<MyT>
        {
            private MyT[] arr = new MyT[100];
            public void put(MyT v,int index)
            {
                arr[index] = v;
            }
        }
        static void Main(string[] args)
        {
            //对泛型Store的特化,特化为int类型的类
            Store<int> store=new Store<int>();
            Store<float> store2 = new Store<float>();

            //泛型方法的使用
            int m = 0,n = 20;
            Swap<int>(ref m, ref n);
        }

        //1.交换2个变量的值
        //static public void Swap(ref int a,ref int b)
        //{
        //    int temp = a;
        //    a = b; 
        //    b = temp;
        //}
        static public void Swap<T>(ref T a, ref T b)
        {
            T temp = a;
            a = b;
            b = temp;
        }

例子2:两个变量相加
//2.求两个变量的加和

static public T Add<T>(T a,T b)
    {
        return a + b;
    }

此时会报错,原因如下,无法确定a、b是否可为相加的类型,例如a、b为类的实例化,实例化类相加没有意义。
在这里插入图片描述
修改方式:通过dynamic将校验推迟到运行时。即保证程序在编译时,不会报错,运行时如果有问题,则弹出提示框

//2.求两个变量的加和
        static public T Add<T>(T a,T b)
        {
            dynamic da=a;
            dynamic db=b;
            return da + db;
        }

完整代码如下:

class Store<MyT>
        {
            private MyT[] arr = new MyT[100];
            public void put(MyT v,int index)
            {
                arr[index] = v;
            }
        }
        static void Main(string[] args)
        {
            //对泛型Store的特化,特化为int类型的类
            Store<int> store=new Store<int>();
            Store<float> store2 = new Store<float>();

            ////泛型方法的使用:1、交换2个变量的值
            //int m = 0,n = 20;
            //Swap<int>(ref m, ref n);

            //泛型方法的使用:2、变量相加

            Console.WriteLine(Add<int>(1,2));
            Console.WriteLine(Add<float>(2.1f,3.2f));
            Console.ReadLine();
        }

        //1.交换2个变量的值
        //static public void Swap(ref int a,ref int b)
        //{
        //    int temp = a;
        //    a = b; 
        //    b = temp;
        //}
        static public void Swap<T>(ref T a, ref T b)
        {
            T temp = a;
            a = b;
            b = temp;
        }

        //2.求两个变量的加和
        static public T Add<T>(T a,T b)
        {
            dynamic da=a;
            dynamic db=b;
            return da + db;
        }
    }

运行结果如下:
在这里插入图片描述
此时修改代码定义:

class cat
        {
        }

实例化,并屏蔽之前的代码

cat cat = new cat();
cat cat1 = new cat();
Add<cat>(cat, cat1);

运行结果如下,会报错,错误原因为两个类之间时无法相加的,即上面提到的
在这里插入图片描述

3.泛型细节

1.泛型可以同时提供多种类型数据的占位符(类、方法均有效)
2.泛型类可以被继承,子类可以指定父类泛型的具体类型(特化)或者子类也可以作为泛型

3.1 多种数据类型占位符

定义类做如下实例,未采用泛型

class StoreDouble
        {
            public int[] arr3 { get; set; }
            public float[] arr4 { get; set; }
        }

现在将其转化为泛型,此时只有一个类型占位符,导致arr3 、arr4为同一种类型数据

class StoreDouble<T>
        {
            public T[] arr3 { get; set; }
            public T[] arr4 { get; set; }
        }        

需要考虑有没有一种方式,让2个数组相互兼容呢?因此可多加一个占位符,即代码如下:

class StoreDouble<T,U>
        {
            public T[] arr3 { get; set; }
            public U[] arr4 { get; set; }
        }

特化方式:

StoreDouble<int,double> storeDouble = new StoreDouble<int,double>();

3.2 泛型类继承

子类指定父类泛型的具体化,子类继承时,数据类型便实例化

class myprogress<T>
        {

        } 
class myprogress1:myprogress<int>
        {

        }

子类也作为泛型类:写代码时无法确定子类的数据类型,可考虑用泛型代替,直接继承,B仅作为占位符,无其他意义

class myprogress<T>
        {

        } 
class myprogress1:myprogress<int>
        {

        }
class myprogress2<B>: myprogress<B>
        {

        }

将“多种数据类型占位符”与“泛型类继承”,知识点结合如下:

class myprogress<T>
        {
            public T Id { get; set; }
        } 
        class myprogress1<T,U>:myprogress<T>
        {
            public U data { get; set; }   //myprogress1继承myprogress数据类型int,data可能不是int类型,
        }

特例化:

myprogress1<int,float> myprogress2 = new myprogress1<int, float>();

内涵:int Id;float data

4 泛型约束

4.1 泛型约束概念的提出

如下代码,定义一个泛型类,泛型类中有一个方法Say,传入参数t为T类型,返回值为void ,内部调用t.Helllo();方法,此时程序会报错

class myclass<T>
        {
            public void Say(T t) 
            {
                t.Helllo();
            }
        }

错误如下,如果传入的t是一个类,类中没有Helllo()方法,编译上便会报错,能不能对T类型做一个规定,比如一定得有hello方法
在这里插入图片描述
通过上面的分析,可以添加一个接口,将二者结合
在这里插入图片描述
完整代码如下:T类型必须继承IHello的接口,where T : IHello便称为泛型约束

class myclass<T> where T : IHello
        {
            public void Say(T t) 
            {
                t.Hello();
            }
        }
interface IHello
        {
            void Hello();
        }

泛型约束概念:对泛型中传入的类型进行“校验”,规定其必须满足条件
格式:
修饰符 class 类名 where T:约束
实例:

class myclass<T> where T : IHello

4.2 泛型约束分类

  1. class:泛型T必须是引用类型
    例子如下:此时编译会报错,类型非引用类型
 class myclass<T> where T :class
        {

        }
        static void Main(string[] args)
        {
            myclass<int> m = new myclass<int>;

        }   

替换成下面可正确:

//1.class表示引用类型约束,int是值类型,
//myclass<int> m = new myclass<int>();
myclass<string> m = new myclass<string>();
  1. 值类型约束:泛型必须是值类型
 class myclass1<T> where T : struct{}
 //2.struct表示值类型约束,string是值类型,
 //myclass1<string> m1 = new myclass1<string>();
 myclass1<int> m1 = new myclass1<int>();
  1. new约束:泛型必须包含无参构造方法,如果与其他约束共用,则放在最后
 class myclass2<T> where T : new(){}
 //3.new()表示T必须包含无参构造方法,如果有其他约束,则new 放在最走
 myclass2<TestForNew> m2 = new myclass2<TestForNew>();
 class myclass3<T> where T : class,new() //如果有其他约束,则new 放在最走
 //class myclass4<T> where T :  new(), class //如果有其他约束,则new 放在最走
  1. 类名约束:泛型必须是某类或者派生自某类
 class myclass4<T> where T : Person{}
 class Person{}
 class Teacher:Person{}
 //4.类名约束:泛型必须是某类或者派生自某类
 myclass4<Person> m3 = new myclass4<Person>();
 myclass4<Teacher> m4 = new myclass4<Teacher>();
  1. 接口约束:泛型必须实现一个或多个接口
class myclass5<T> where T : myinterface1{}
interface myinterface1      //自定义接口及方法
        {
            void myinterface11();
        }
interface myinterface2      //自定义接口及方法
        {
            void myinterface22();
        }

        class Tank: myinterface1, myinterface2      //继承至接口myinterface1、myinterface2
        {
            public void myinterface11() { }         //继承至接口myinterface1、myinterface2中的方法,并重写
            public void myinterface22() { }
        }
 //5.接口约束:泛型必须实现一个或多个接口
            myclass5<Tank> m5 = new myclass5<Tank>();

以上5种约束代码完整如下:

class TestForNew
        {
            ////public TestForNew(int a)  出错,有参数a构造方法,不符合new定义
            ////{

            ////}
            public TestForNew()
            {

            }
        }
        class myclass<T> where T :class
        {

        }

        class myclass1<T> where T : struct
        {

        }

        class myclass2<T> where T : new()
        {

        }
        class myclass3<T> where T : class,new() //如果有其他约束,则new 放在最走
        {

        }

        //class myclass4<T> where T :  new(), class //如果有其他约束,则new 放在最走
        //{

        //}


        //类名约束:泛型必须是某类或者派生自某类
        class myclass4<T> where T : Person
        {

        }

        class Person
        {

        }

        class Teacher:Person
        {

        }

        //接口约束:泛型必须实现一个或多个接口
        class myclass5<T> where T : myinterface1
        {

        }
        interface myinterface1      //自定义接口及方法
        {
            void myinterface11();
        }

        interface myinterface2      //自定义接口及方法
        {
            void myinterface22();
        }

        class Tank: myinterface1, myinterface2      //继承至接口myinterface1、myinterface2
        {
            public void myinterface11() { }         //继承至接口myinterface1、myinterface2中的方法,并重写
            public void myinterface22() { }
        }

        //类名约束与接口约束结合
        class myclass6<T> where T : Person, myinterface1, myinterface2
        {

        }

        static void Main(string[] args)
        {
            //1.class表示引用类型约束,int是值类型,
            //myclass<int> m = new myclass<int>();
            myclass<string> m = new myclass<string>();

            //2.struct表示值类型约束,string是值类型,
            //myclass1<string> m1 = new myclass1<string>();
            myclass1<int> m1 = new myclass1<int>();

            //3.new()表示T必须包含无参构造方法,如果有其他约束,则new 放在最走
            myclass2<TestForNew> m2 = new myclass2<TestForNew>();

            //4.类名约束:泛型必须是某类或者派生自某类
            myclass4<Person> m3 = new myclass4<Person>();
            myclass4<Teacher> m4 = new myclass4<Teacher>();

            //5.接口约束:泛型必须实现一个或多个接口
            myclass5<Tank> m5 = new myclass5<Tank>();
        }
    }

以上5种泛型约束总结如下图所示:类的约束与接口约束用的最多
在这里插入图片描述
多泛型约束是上述5种约束之间的结合,对多个占位符进行各自的约束
泛型约束知识点进一步延申如下:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值