协变与逆变
协变不是理所当然的,逆变也没有“逆”
可以看到,从”实际流程图”来看,逆变根本没有“逆”,都离不开只能安全地把子类的引用赋给父类引用这个根本。
来到这里应该基本理解逆变与协变了,不过装配脑袋的这篇文章有个更高级的问题,原文也有解答,这里我用上面画图的方式去理解它。
图解逆变与协变的相互作用
问题的提出,你知道那个正确吗?
public interface IBar<in T> { } //应该是in public interface IFoo<in T> { void Test(IBar<T> bar); } //还是out public interface IFoo<out T> { void Test(IBar<T> bar); }
答案是,如果是in的话,会编译失败,out才正确(当然不要泛型修饰符也能通过编译,但IFoo就没有协变能力了)。这里的意思就是说,一个有协 变(逆变)能力的泛型(IBar),作为另一个泛型(IFoo)的参数时,影响到了它(IFoo)的泛型的定义。乍一看以为是in的其中一个陷阱是T是在 Test方法的参数里的,所以以为是in。但这里Test的参数根本不是T,而是IBar<T>。
我们画个图来理解它。既然out可以通过,那么它的“协变流程图”应该如下:
图跟前面那些大致一样,但理解它要跟问题相反(上面问题是先定义好IBar,再去定义IFoo)。1.我们定义好一个有协变能力的IFoo,这是前 提。2.可以推出,上面的流程是成立的。3.这个流程重点是参数流向,要使整个流程成立,就必须使IBar<string> = IBar<object>成立,这不就是逆变吗?整个结论就是,有协变能力的IFoo要求它的泛型参数(IBar)有逆变能力。其实根据上面的箭头也可以理解,因为原式和变式的变向跟参数的变向是相反的,导致了它们要有相反的能力,这就是装配脑袋文章说的:方法参数的协变-反变互换原则。根据这个原理,也很容易得出,如果Test方法的返回值是IBar<T>,而不是参数,那么就要求IBar<T>要有协变能力,因为返回值的箭头与原式和变式的变向的箭头是同向的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现