文章目录
9. 泛型Generic
9.1 泛型
什么是泛型?
泛型相当于一个类型的占位符,它可以实现在编写代码时不进行显式的类型定义,而是在编译运行时进行自动类型定义。使用泛型可以很大程度提高代码复用率,再出现逻辑问题进行修改或更新时也会更加方便。
泛型可以看作是一辆公交车里面的乘客,在乘客没上车之前没人知道他会是男还是女还是其他小动物,但可以确信的是公交车肯定会有乘客。
为什么使用泛型,使用object不是也可以进行不定类型的处理吗?
object
在使用时需要进行拆箱装箱,对性能会有损耗,而泛型在直接使用时则不会进行装箱拆箱的操作可以节约系统性能。
泛型的基础使用
泛型使用包括泛型类、泛型接口和泛型方法,他们都可以通过泛型来表示未确定的数据类型,定义泛型需要在类名或接口名或方法名后使用尖括号<>
,并在其中填入泛型占位符,使用时可以直接使用占位符进行声明泛型变量或处理泛型数据。
例如如下代码中的定义:
namespace 泛型Generic
{
//泛型接口
public interface IEat<TInstance>
{
//使用泛型的方法
void Eat(TInstance instance);
}
//继承了泛型接口的 泛型类
//继承泛型接口时必须指定泛型对应的类型
public class Apple<TName, TPrice> : IEat<People>
{
//创建泛型变量
public TName name;
public TPrice price;
public Apple(TName name, TPrice price)
{
this.name = name;
this.price = price;
}
public void Eat(People instance)
{
Console.WriteLine($"{instance.name}正在吃价格为{price}叫做{name}的苹果");
}
}
}
我们对其进行简单的调用:
Apple<string, float> apple = new("水晶红富士", 10.5f);
apple.Eat(new People("泠曦"));
结果为:
泠曦正在吃价格为10.5叫做水晶红富士的苹果
而对于上面的吃的函数,也可以不继承接口,使用泛型进行书写:
public void Eat<T>(T instance) where T : People
{
Console.WriteLine($"{instance.name}正在吃价格为{price}叫做{name}的苹果");
}
9.2 泛型约束
什么是泛型约束?
泛型可以代表任何数据类型,但因为它本身的广泛性,在许多地方不进行类型限制可能会导致严重的程序逻辑错误,就比如吃这个方法,通常而言我们吃的是食物,但如果不进行约束限制,可能调用时传入的是一些不可名状的东西。因此出现了泛型约束,限制可以传入的类型大类,防止出现大的逻辑错误,除此之外,在未进行泛型约束的情况下也无法对泛型变量进行较为精细的操作,例如如下代码:
而当我们对它进行约束后,就可以进行比较:
如何对泛型进行约束?
对泛型进行约束需要使用where
关键字,在关键字后写上泛型符号和约束类型,多个约束类型可以用逗号隔开,语法为:
where 占位符 : 约束类型
例如我们在Animal
中的Eat
方法对传入参数进行约束,必须要是Food
类型或其子类:
public class Animal
{
public void Eat<TFood>(TFood food) where TFood : Food
{
food.BeEat();
}
}
在传参时,如果传入的不是限制的类型,编译器将会报错:
只有当传入参数符合要求是才可以编译通过。
有哪些类型的泛型约束?
约束的种类有很多,在官方文档中有13个(类型参数的约束 - C# | Microsoft Learn),常用的有六个,分别为:
- 值类型约束
where 泛型字母:struct
- 引用类型约束
where 泛型字母:class
- 存在无参构造函数
where 泛型字母:new()
- 某个类本身或其派生类
where 泛型字母:类名
- 某个接口
where 泛型字母:接口名
- 另一个泛型或其派生类
where 泛型字母:另一个泛型字母
例如对刚刚的Eat进行约束,必须要是Food并且要有无参构造: