什么是泛型 Generic
简单的来讲,泛型相当于模板,它允许您编写一个可以与任何数据类型一起工作的类或方法(说白了就是允许你根据模板制定自己需要的数据类型方法、类、接口)
使用泛型是一种增强程序功能的技术,有助于您最大限度地重用代码、保护类型的安全以及提高性能
标志就是以
<T>
为占位符的占位委托
注意:泛型类型要避免使用object
类型,避免反复装箱拆箱的操作,会带来性能损耗
泛型的优点和原理
- 最大的特点就是“通用”,
C#
在编译器下【这里还没确定泛型的确定类型,但是有T
(机器码内以 ` 反单引号)作为一个占位符】生成/运行的时候会生成一个DLL/EXE
文件,然后通过CLR
环境【这一步确定泛型的具体类型 】进行二次编译,生成机器码(假如直接生成机器码的话在跨环境的情况下,机器内部硬件不同就可能不能执行,这样的话跨平台能力就会比较差)
泛型的约束
- 规定泛型数据的类型,防止什么类型的数据都输入进来,写法:
where
;
约束的类型:
–new()
约束–表示只接受带有一个无参数的构造函数
–struct
值类型约束:(值类型:结构体struct
/int
、double
、bool
和枚举
。引用类型:类
,接口
,委托
、object
、字符串
)
–class
引用类型约束
– 自定义类型约束where T : student
– 多类型约束where T : class,new()
【new()
约束一定要写在多类型约束的最后面】,但是参数约束不能有冲突,比如值类型和引用类型不能同时存在。
public static void Show<T>(T t1) where T:new() //只接收可以实例化的类,比如接口就不行
下面来写一个泛型的小demo,来更好的观测不同数据类型作为类型约束时,数据输出的差异,记得在测试时打上断点,方便调试
using System;
namespace GenericTestProject
{
public class GenericTestClass<T> {
//定义私有域数组变量,并且泛型定义数组类型
private T[] genericArray;
//使用构造方法对类中的属性进行初始化
public GenericTestClass(int size){
//初始化创建出泛型对应的数组,包括类型和长度
//例如 T 为int 时,下面语句就是:genericArray = new int[size + 1];
genericArray = new T[size + 1];
}
//设置数组的值
public void setItem(int index, T value) {
genericArray[index] = value;
}
//获取数组的值
public T getItem(int index) {
return genericArray[index];
}
}
class Program
{
//程序入口
public static void Main(string[] args){
//测试当泛型数据为int类型时的情况
GenericTestClass<int> genTest = new GenericTestClass<int>(5);
for (int i = 0; i < 5; i++) {
genTest.setItem(i, i + 1);
}
for (int j = 0; j < 5; j++) {
Console.Write(genTest.getItem(j) + " ");
}
//下面测试使用char作为泛型类型数据时的情况
Console.WriteLine("\n----------------------------");
GenericTestClass<char> genTest2 = new GenericTestClass<char>(5);
for (int a = 0; a < 5; a++) {
genTest2.setItem(a, (char)(a + 96));
}
for (int b = 0; b < 5; b++) {
Console.Write(genTest2.getItem(b) + " ");
}
}
}
}
泛型的其它使用
泛型字典Dictionary<K,V> ,键/值对,是成对存在的
- 泛型字典内部有自带的方法(Add)
Dictionary<int, string> dic = new Dictionary<int,string>();
//
dic.Add(1,"C#");
dic.Add(1,"C++");
dic.Add(1,"C");
foreach(var item in dic){
Console.WriteLine(item.Key.ToString()+" " + item.Value);
}
- ContainsValue,数据匹配;Remove,根据键位进行删除
bool isContains = dic.ContainsValue("C#");
dic.Remove(1); //根据键位匹配,删除了字典内对应的数据值
泛型自定义
- 举例:操作数据库,读取数据时,每个表返回数据格式都不同。使用泛型自定义的话可以封装这样用不同的格式进行接收!通用的函数方法可以使代码的复用率提高,减少冗余代码
/// <summary>
/// 自定义泛型类,这样可以提高代码复用
/// 假如是SQL helper类提取数据库内不同数据时,可以使用
/// 一段代码块适配不同的数据然后调取配置
/// 使用占位符T表示待定数据类型,通过实例化传递类型数据定义泛型类的数据类型,泛型定义符号可以定义多个
/// </summary>
/// <typeparam name="T"></typeparam>
class myGeneric<T>
{
private T dataTest;
//构造函数
public myGeneric(T dataTest)
{
this.dataTest = dataTest;
}
public void getDataTest() {
Console.WriteLine(dataTest);
}
}
........
public static void Main(string[] args){
myGeneric<int> mg = new myGeneric<int>(111);
mg.getDataTest();
myGeneric<string> mg2 = new myGeneric<string>("abc");
mg2.getDataTest();
}
泛型方法
- 使用泛型之后,数据库的crud就不再需要写这么多个方法就能通用
public void Show<T>(T showSomething){
Console.WriteLine(showSomething.ToString());
}
......
show("展示泛型方法");
show(101010);
泛型约束
public static void Main(string[] args){
//实例化两个对象实例
ChineseHuman ch = new ChineseHuman() { HumanName = "小明" };
BirdClass bc = new BirdClass() {AnimalType = "鹦鹉" };
//调用函数,对泛型类型进行定义,并且将实例作为实参传入
//可以发现的是,泛型定义类型已经被方法体内的泛型约束给限制了,
//无法定义为其它类型的泛型定义
//泛型定义必须要与展示方法show的where约束类型相同,并且传参也要与泛型类型相同
FunClass.ShowAnimal<AnimalClass>(bc);
FunClass.ShowHuman<ChineseHuman>(ch);
}
//展示方法
public class FunClass {
//泛型约束where
public static void ShowHuman<T>(T Typedef) where T : HumanClass {
Console.WriteLine($"这个人名字是{Typedef.HumanName}");
}
public static void ShowAnimal<T>(T Typedef) where T : AnimalClass{
Console.WriteLine($"这只鸟的种类是{Typedef.AnimalType}");
}
}
... 继承
public class BirdClass : AnimalClass{
public string BirdName { get; set; }
}
... 继承
public class ChineseHuman : HumanClass{
public string Cname { get; set; }
}
... 父类
public class HumanClass{
public string HumanName { get; set; }
}
... 父类
public class AnimalClass{
public string AnimalType { get; set; }
}
结尾
那么话说回来,使用泛型对于我这个菜鸡来说有什么好处呢?好处就是,从前刚接触代码都是有一个需求就写一个,现在使用泛型我可以把一些重复的、近似度高的代码复用起来,通过泛型这个写法最大程度地复用代码,同时泛型可以在一些特殊场景让我的代码具备一定的安全性。
回到我的专业领域,在嵌入式中,开发芯片的内存可谓是寸土寸金,如果不将可复用的代码最大程度地利用起来,转而为每个功能都写上一个功能函数,那ROM岂不是被大量的浪费掉了,这只是个人比较片面的看法,希望对你有所帮助