泛型的用处非常大,我们编程的时候,有时候感觉不到它的存在,原因就是我们的代码过于简单,或者架构比较陈旧,所以用不用感觉不出来,一般高级框架里面就有大量的泛型。
泛型的优点有:代码重用、类型安全、高效率,凡是带有<>的尖括号,都是泛型的表示。
以下案例来自.net6环境
1.泛型由来,为什么要用泛型
以下3组不同类型的数据,定义的时候,我们必须知道类型,否则无法定义,而且后期不能再次增加或者减少数据
int[] a = new int[2] { 1, 2 }; //定义一个int类型的数组
double[] b = new double[2] { 1.0, 2.0 }; //定义一个double类型的数组
string[] c = new string[2] { "张三", "李四" }; //定义一个string类型的数组
于是,后来有了ArrayList,不需要知道类型,ArrayList中的Add方法可以增加任何类型的数据,但是有装箱拆箱的性能损失。
ArrayList list1 = new ArrayList(); //定义一个ArrayList
list1.Add(1); // int类型
list1.Add(1.0); // double类型
list1.Add("张三"); // string类型
再后来,我们使用泛型List<T>,其中T,可以是任何类型,不仅解决数组不能自由改变的问题,还不会有性能的问题。
List<int> list2 = new List<int>();
list2.Add(1);
list2.Add(5);
List<string> list3 = new List<string>();
list3.Add("张三");
list3.Add("李四");
2.泛型方法
如果不用泛型方法,那么Show需要定义3个类型的方法,分别是int double string。这里使用泛型方法后,只需要一个方法即可,并且在调用的时候,指定类型,然后传递参数,代码重用,效率高等等有点。
当参数使用,无返回值
Show<int>(3); //调用的时候指定int类型
Show<double>(3.0); //调用的时候指定int类型
Show<string>("张三"); //调用的时候指定int类型
static void Show<T>(T param)
{
Console.WriteLine($"类型:{param?.GetType().Name + param}"); //?表示可空
}
当返回值使用,
Show1<int>(); //调用的时候指定int类型
static T? Show1<T>() //?表示可空
{
return default(T); //值类型不能为null,引用类型可以为null
}
当局部使用
Show2<double>(2,3); //调用的时候指定int类型
static void Show2<T>(int a,int b)
{
T t;//j局部变量 string的时候t是null int的时候t是0 double也是0
}
3.泛型类
定义一个泛型类
public class Alist<T>
{
private Alist<T>? alist; //?表示可空
public void Add(T param)
{
alist?.Add(param); //?表示可空
}
}
使用泛型类
Alist<int> list = new Alist<int>(); //实例化调用,调用的时候指定类型,int
list.Add(123);
4.泛型的约束
where T:class,需要传递引用类型,只要是引用类型都行,比如传递string,或者Alist<T>。
A<string>(); //调用,并且指定string
A<Alist<int>>(); //调用,并且指定为引用类型的Alist<T>
static void A<T>() where T:class //class是引用类型
{
}
public class Alist<T>
{
private Alist<T>? alist; //?表示可空
public void Add(T param)
{
alist?.Add(param); //?表示可空
}
}
where T:struct, 需要传递值类型,只要是值类型都行,比如传递int,double。
值类型都有一个隐式的公共构造方法
A1<int>(); //调用,并且指定int
A1<double>(); //调用,并且指定double
static T A1<T>() where T : struct //struct是值类型
{
return new T();//值类型都有一个隐式的公共构造方法
}
where T : new() ,要放在最后。包含(值类型,值类型就有构造参数)和(引用类型带构造参数的)
A2<int>(); //调用,并且指定int int是值类型,有构造方法
A2<Alist<string>>(); //调用,并且指定Alist<string> Alist<string>是引用类型,也有构造方法
static void A2<T>() where T : new() //new,包含(值类型)和(引用类型带构造参数的)
{
}
public class Alist<T>
{
public Alist() //构造方法 不写构造方法就不行了
{
}
private Alist<T>? alist; //?表示可空
public void Add(T param)
{
alist?.Add(param); //?表示可空
}
}
where T : B ,自定义约束,B是一个类,只能是B或者继承B类的(C也行)
A3<B>(); //调用,并且指定B
A3<C>(); //调用,并且指定C
static void A3<T>() where T : B //B,自定义约束,只能是B或者继承B类的(C也行)
{
}
public class B
{
}
public class C : B
{
}
可以约束多个接口,一个参数有多个约束
static void A4<T>() where T : B,AA,AA1 //可以约束多个接口
{
}
interface AA
{
}
interface AA1
{
}
也可以 多个参数中每个参数都有一个或者多个约束
A5<B, AA, C>(); //D调用
static void A5<T, T1, T2>()
where T : B
where T1 : AA
where T2 : C
{
}
所有代码
using System.Collections;
int[] a = new int[2] { 1, 2 }; //定义一个int类型的数组
double[] b = new double[2] { 1.0, 2.0 }; //定义一个double类型的数组
string[] c = new string[2] { "张三", "李四" }; //定义一个string类型的数组
ArrayList list1 = new ArrayList(); //定义一个ArrayList
list1.Add(1); // int类型
list1.Add(1.0); // double类型
list1.Add("张三"); // string类型
List<int> list2 = new List<int>();
list2.Add(1);
list2.Add(5);
List<string> list3 = new List<string>();
list3.Add("张三");
list3.Add("李四");
//Show<int>(3); //调用的时候指定int类型
//Show<double>(3.0); //调用的时候指定int类型
//Show<string>("张三"); //调用的时候指定int类型
//static void Show<T>(T param)
//{
// Console.WriteLine($"类型:{param?.GetType().Name + param}"); //?表示可空
//}
//Show1<int>(); //调用的时候指定int类型
//static T? Show1<T>() //?表示可空
//{
// return default(T); //值类型不能为null,引用类型可以为null
//}
//Show2<double>(2,3); //调用的时候指定int类型
//static void Show2<T>(int a,int b)
//{
// T t;//j局部变量 string的时候t是null int的时候t是0 double也是0
//}
//Alist<int> list = new Alist<int>(); //实例化调用,调用的时候指定类型,int
//list.Add(123);
//Console.WriteLine(list);
//A<string>(); //调用,并且指定string
//A<Alist<int>>(); //调用,并且指定为引用类型的Alist<T>
//A1<int>(); //调用,并且指定int
//A1<double>(); //调用,并且指定double
//static void A<T>() where T : class //class是引用类型
//{
//}
//static T A1<T>() where T : struct //struct是值类型
//{
// return new T(); //值类型都有一个隐式的公共构造方法
//}
//A2<int>(); //调用,并且指定int int是值类型,有构造方法
//A2<Alist<string>>(); //调用,并且指定Alist<string> Alist<string>是引用类型,也有构造方法
//static void A2<T>() where T : new() //new,包含(值类型)和(引用类型带构造参数的)
//{
//}
//A3<B>(); //调用,并且指定B
//A3<C>(); //调用,并且指定C
//static void A3<T>() where T : B //B,自定义约束,只能是B或者继承B类的(C也行)
//{
//}
static void A4<T>() where T : AA, AA1 //可以约束多个接口
{
}
A5<B, AA, C>(); //D调用
static void A5<T, T1, T2>()
where T : B
where T1 : AA
where T2 : C
{
}
interface AA
{
}
interface AA1
{
}
public class A
{
}
public class B
{
}
public class C : B
{
}
public class Alist<T>
{
public Alist() //构造方法 不写构造方法就不行了
{
}
private Alist<T>? alist; //?表示可空
public void Add(T param)
{
alist?.Add(param); //?表示可空
}
}