目录
一、泛型的引入
-
泛型---泛:宽泛的---不确定的; 型:类型---不确定的类型
-
无处不在
-
调用普通方法的时候,参数类型在声明的时候就确定了,调用时按照类型传递参数即可;
-
Object类型作为参数,可以传递不同的类型进去。
-
泛型方法---做到了性能高---可以一个方法满足不同类型的需求
二、泛型的声明设计思想
- 泛型方法:在方法名称后面多一个尖括号new class Base()
{ },尖括号中有占位符 - 延迟声明:在执行调用时声明
- 占位符:T 类型参数——类型变量
- 类型参数当做方法的参数的时候,明确参数类型
三、泛型的原理
1.在高级语言中,定义的泛型T,在计算机执行的执行的时候,必须是一个具体的类型;
2.在底层看到,生成的结果是:List `[T] Dictionary 2 [TKey,TValue]
3.编译器必须能够支持泛型
4.CLR运行时环境也必须要支持泛型
5.泛型是框架升级支持的——泛型并非是语法糖
四、泛型的使用
- 泛型方法----可以一个方法满足不同类型的需求
- 泛型接口----可以一个接口满足不同类型的需求---尖括号+占位符
- 泛型类------可以一个类型满足不同类型的需求---尖括号+占位符
- 泛型委托----可以一个委托满足不同类型的需求
实例代码
{
//泛型方法
{
GenericInterfac<string> sgenericInterfac = null;
GenericInterfac<DateTime> dtgenericInterfac = null;
}
{
GenericAbstractClass<string> sGenericClass = null;
GenericAbstractClass<DateTime> dtGenericClass = null;
GenericAbstractClass<int> iGenericClass = null;
//sGenericClass dtGenericClass iGenericClass 三者是什么关系?--一没有关系都没有
}
{
Genericdelegate<string> sGenericdelegate = null;
Genericdelegate<DateTime> dtGenericdelegate = null;
}
}
五、泛型的约束
为什要有泛型约束?
- Object类型有安全问题,代码可能会存在隐患,但是编译器监测不到原因
- 如何避免类型安全问题--泛型--不存在类型安全问题
1.基类约束
- 就是把类型参数当做Cart
- 调用---就可以传递Cart或者Cart的子类型
- 泛型约束:要么不让你进来,如果让你进来,就一定是没有问题
- 直接new T()会报错,可能没有无参数构造构造函数
示列:
class MyGenericClass<T>
where T : Cart
{
}
2.接口约束
- 把这个T 当做ICart
- 就只能传递ICart 这个接口或者是实现过这个接口的类
- 就可以增加功能,可以获取新的功能
示列:
public class MyGenericClass<T> where T:ICart { }
3.引用类型约束
- 就只能传递类型进来
- 直接new T()会报错,可能没有无参数构造构造函数
示列:
public class MyGenericClass<T> where T:class
4.值类型约束
- 就只能传递值类型进来
示列:
public static void ShowStruct<T>(T t) where T : struct
{
}
5.无参构造函数约束
- 必须有一个无参数构造函数才能当做参数传入
示列:
public static void ShowNew<T>(T t) where T : new()
{
T model = new T();
}
6.枚举约束
- 必须是一个枚举类型才能传入
示列:
public static void ShowEnum<T>(T t) where T : Enum
{
}
六、协变与逆变
1.泛型的协变/逆变
协变/逆变是一种高级约束,是为了规避:
- 把子类做参数,却把父类当参数传入
- 把子类做返回值,却返回的时候,返回了一个父类
2.协变逆变
- 只针对于泛型接口和泛型委托的
3.协变
-
协变: 就可以让右边用子类,能让左边用父类
-
协变:Out 类型参数只能做返回值 ,不能做参数
4.逆变
- 逆变:就可以让右边用父类;左边用子类
- 逆变 In : 只能做参数 ,不能做返回值
5.协变逆变的存在
- 就是为了满足常规场景添加一个避开风险的约束