1. 泛型概述
泛型最大的优点就是做到了通用。在编程程序时,经常会遇到功能非常相似的模块,只是它们处理的数据不一样。泛型可以用同一个方法来处理传入不同类型的参数
//下面三个方法把参数类型写定了
public static void ShowIntData(int a, int b)
{
Console.WriteLine($"{a},{b}");
}
public static void ShowDateData(DateTime a, DateTime b)
{
Console.WriteLine($"{a},{b}");
}
public static void ShowStringData(string a, string b)
{
Console.WriteLine($"{a},{b}");
}
//上面三个方法除了传入的参数不同外,其里面实现的功能都是一样的
//使用继承,也就是int, string, datetime类都继承自object这个一切类型的父类
//使用obejct类型进行转换为int, string,datetime时会有装箱和拆箱,会损耗程序的性能
public static void ShowDate(object a, object b)
{
Console.WriteLine($"{a},{b}");
}
//泛型方法,解决上述所有问题
//在调用ShowData<T>方法时,T要传入具体的参数类型,比如int
public static void ShowData<T>(T a, T b )
{
Console.WriteLine($"{a},{b}");
}
int a = 3, b = 5;
ShowData<int>(a, b);
PhoneCustomer phoneone = new PhoneCustomer()
{
FirstName = "aa",
Age = 1
};
PhoneCustomer phonetwo = new PhoneCustomer()
{
FirstName = "bb",
Age = 2
};
//T都是PhoneCustomer类型,T类型要统一
ShowData<PhoneCustomer>(phoneone, phonetwo);
1. 类型参数T是在实例化泛型类型的一个变量时指定的特定类型的占位符,它在使用时必须传入具体的参数类型,比如int, 自定义person类等
2. 泛型是延迟声明的:即定义的时候没有指定具体的参数类型,把参数类型的声明推迟到了调用的时候才指定参数类型。 延迟思想在程序架构设计的时候很受欢迎。例如:分布式缓存队列、EF的延迟加载等等
3. 泛型方法的性能最高,其次是普通方法,object方法的性能最低
1.1 泛型性能
泛型的一个主要优点是性能,避免了装箱和拆箱。而对值类型使用非泛型集合类时,使用装箱和拆箱。拆箱和装箱性能损失较大
装箱:值类型转换为引用类型
拆箱:引用类型转换为值类型,需要使用类型强制转换运算符
var list = new ArrayList();
//装箱
list.Add(44);
//拆箱,使用了类型强制转换运算符
int arr = (int)list[0];
1.2 类型安全
泛型另一个特性是类型安全
//泛型类List<T>,这里T定义为int,所以List中只能添加Int类型的数据,添加string类型的数据,会报错
var list = new List<int>();
list.Add(33);
//编译器报错
list.Add("baab");
2. 创建泛型类(接口,方法)
方法可以是泛型的,类也可以是泛型的,接口也可以是泛型的
1. 泛型在声明的时候可以不指定具体的类型,但是在使用的时候必须指定具体类型
2. 子类也是泛型的,那么继承的时候可以不指定具体类型
//泛型类
public class GenericClass<T>
{
public T _T;
}
// T是int类型
GenericClass<int> genericInt = new GenericClass<int>();
genericInt._T = 123;
// T是string类型
GenericClass<string> genericString = new GenericClass<string>();
genericString._T = "123";
/// <summary>
/// 泛型接口
/// </summary>
public interface IGenericInterface<T>
{
//泛型类型的返回值
T GetT(T t);
}
//泛型方法
//T,K,S可以分别是三种不同的类型,比如int,string,double
public static void ShowData<T, K, S>(T a, K k, S s)
{
Console.WriteLine($"{a},{k},{s}");
}
public delegate void SayHi<T>(T t);//泛型委托
3. 泛型类的功能
3.1 默认值(default)
1. 不能把null赋予泛型类型,但是可以用default,将null赋予引用类型,将0赋予值类型
public T GetDocument()
{
T doc = default(T);
return doc;
}
3.2 约束(where)
泛型类需要调用泛型类型中的方法,就必须添加约束。所谓的泛型约束,实际上就是约束的类型T。使T必须遵循一定的规则。比如T必须继承自某个类,或者T必须实现某个接口等等。
约束 | s说明 |
T:结构 | 类型参数必须是值类型(比如:struct/int/double/bool/enum等) |
T:类 | 类型参数必须是引用类型(数组/类/接口/委托/object/字符串等);这一点也适用于任何类、接口、委托或数组类型。 |
T:new() | 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。 |
T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类。 |
T:<接口名称> | 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。 |
//指定泛型参数为值类型
public class MyListVlue<T> where T : struct
{
//
}
//指定泛型参数为引用类型
public class MyListClass<T> where T : class
{
//
}
// 指定泛型参数有无参的公共的构造函数
public class MyListBase<T> where T : new()
{
//
}
//指定泛型参数必须派生于指定基类, 要调用MyListHer<T>方法,必须派生于SeniorAnimal的类才能调用
public class MyListHer<T> where T : SeniorAnimal
{
//
}
//指定泛型参数 必须实现指定接口,要调用MyListInterface<T>方法,必须继承IBankAccount接口的类才能调用
public class MyListInterface<T> where T : IBankAccount
{
//
}
//泛型约束也可以同时约束多个
public static void Show<T>(T tParameter)
where T : People, ISports, IWork, new()
{
//
}
//泛型方法
//T,K,S可以分别是三种不同的类型,比如int,string,double
public static void ShowData<T, K, S>(T a, K k, S s)
where T:struct
where K:class
where S:new()
{
//
}
1. 基类(约束T必须是自定义的People类型或者是People的子类)约束时,基类不能是密封类,即不能是sealed类。sealed类表示该类不能被继承,在这里用作约束就无任何意义,因为sealed类没有子类
2. 有多个泛型约束时,new()约束一定是在最后
4. 泛型的协变和逆变
只能放在接口或者委托的泛型参数前面,out 协变covariant,用来修饰返回值;in逆变contravariant,用来修饰传入参数。
代码解释:
1. Teacher是People的子类
4.1 协变
使用了协变以后,左边声明的是基类,右边可以声明基类或者基类的子类。