c#-泛型、协变、逆变
介绍
可以使用泛型声明的元素:类、接口、方法、委托
泛型出现之前使用object封装不同类型的参数,缺点:装箱拆箱性能差、运行时判断类型(不安全)...
泛型是在编译期间转为实际类型副本,所以性能好,还可以使用约束对泛型进行约束
泛型约束
约束泛型类型必须满足约束。使用泛型约束后,可以像使用约束类型的方式使用泛型变量
约束种类:
where T:class//引用类型约束
where T:new()//无参构造函数约束,加了此约束后可以使用无参构造函数创建实例:T t = new T();
where T:类//具体类或其子类约束
where T:接口//具体接口或其子类约束
where T:struct//值类型约束
where T:People,new(),ISport //多个约束
协变逆变
协变逆变只能在接口或委托中使用
个人理解:协变逆变可以使我们的代码更灵活,帮我们做了类型转换操作。协变是子类转父类;逆变是父类转子类。
协变(out)
修饰返回值,泛型只能出现在成员的返回类型上,不能做传入参数
常用的支持协变的接口和委托有
- IEnumerable
- IEnumerator
- IQueryable
- IGrouping<out TKey, out TElement>
- Func等共17个
- Converter<in TInput, out TOutput>
协变案例
public class Bird
{
public string ID { get; set; }
}
public class Sparrow : Bird
{
public string Name { get; set; }
}
static void Main(string[] args)
{
Bird bird = new Sparrow();//正确,一只麻雀就是一只鸟
List<Bird> birdList = null;
//birdList = new List<Sparrow>();//报错,一群麻雀和一群鸟没有继承关系
//只能用下面这两种方式
birdList = new List<Sparrow>().Select(s=>(Bird)s).ToList();//方式1
birdList = new List<Sparrow>().Cast<Bird>().ToList();//方式2
//上面这两种方法写起来比较麻烦,所以有了协变,协变就是解决这个问题
//为什么下面这个没有报错
IEnumerable<Bird> iBirdList = new List<Sparrow>();
//就是因为IEnumerable<out T>接口是支持协变的,协变使用out关键字
}
逆变(in)
逆变与协变相反,是父类转换子类,使用in关键字。泛型类型只能出现在传入参数上
常用支持逆变的接口和委托有:
- IComparer
- IComparable
- IEqualityComparer
- Action等共16个
- Predicate
- Comparison
- Converter<in TInput, out TOutput>
逆变案例
public interface ICustomerEnumerable<in T>
{ }
public class CustomerEnumerable<T> : ICustomerEnumerable<T>
{ }
static void Main(string[] args)
{
//in 逆变
ICustomerEnumerable<Sparrow> sparrowList = new CustomerEnumerable<Bird>();
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤