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 泛型约束分类
- 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>();
- 值类型约束:泛型必须是值类型
class myclass1<T> where T : struct{}
//2.struct表示值类型约束,string是值类型,
//myclass1<string> m1 = new myclass1<string>();
myclass1<int> m1 = new myclass1<int>();
- 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 放在最走
- 类名约束:泛型必须是某类或者派生自某类
class myclass4<T> where T : Person{}
class Person{}
class Teacher:Person{}
//4.类名约束:泛型必须是某类或者派生自某类
myclass4<Person> m3 = new myclass4<Person>();
myclass4<Teacher> m4 = new myclass4<Teacher>();
- 接口约束:泛型必须实现一个或多个接口
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种约束之间的结合,对多个占位符进行各自的约束
泛型约束知识点进一步延申如下: