C#泛型的“变形术“:协变逆变的5个致命实战,99%人踩过坑!

发布时间:2026/7/8 9:13:05

C#泛型的“变形术“:协变逆变的5个致命实战,99%人踩过坑!
关注墨瑾轩带你探索编程的奥秘超萌技术攻略轻松晋级编程高手技术宝库已备好就等你来挖掘订阅墨瑾轩智趣学习不孤单即刻启航编程之旅更有趣硬核拆解协变逆变的变形术附血泪案例1. 协变从子类到父类的优雅下跪IEnumerable的真相// 你写的方法接收Animal集合publicstaticvoidSave(IEnumerableAnimalanimals){// 你只想把动物们喂饱foreach(varanimalinanimals)animal.Feed();}// 你得意地调用vardogListnewListDog();// Dog是Animal的子类Save(dogList);// 编译器居然没报错关键注释IEnumerableout T中的out是协变的通行证它告诉编译器“T只会被输出不会被输入”。所以IEnumerableDog能安全地降级为IEnumerableAnimal——因为Dog是Animal的子类喂狗和喂动物本质没区别。墨氏冷幽默协变就像儿子给老子发红包——儿子的钱是老子的钱的子集所以发红包没问题。逆变呢就像老子给儿子发红包——这得看儿子有没有本事接住不然就是打脸为啥能这样namespaceSystem.Collections.Generic{// 重点out关键字让T可以向下兼容publicinterfaceIEnumerableoutT:IEnumerable{IEnumeratorTGetEnumerator();}}血泪教训去年我写了个IEnumerableDog的API结果调用方传了个IEnumerableAnimal进来编译器直接给我报错了原因我忘了在接口里加out——协变不是魔法是编译器的安全锁2. 逆变从父类到子类的危险上位IComparer的真相// 你设计的比较器比较AnimalpublicclassAnimalComparer:IComparerAnimal{publicintCompare(Animalx,Animaly)x.Weight.CompareTo(y.Weight);}// 你想用这个比较器比较DogvardogComparernewAnimalComparer();// 但Dog是Animal的子类vardogsnewListDog();dogs.Sort(dogComparer);// 编译器你疯了关键注释IComparerin T中的in是逆变的通行证它告诉编译器“T只会被输入不会被输出”。所以IComparerAnimal能安全地升级为IComparerDog——因为比较Dog时Animal的比较逻辑依然适用。墨氏吐槽逆变就像爸爸让儿子去接客——儿子是爸爸的子类所以接客能力不比爸爸差。但如果你让儿子接客去比较爸爸那就是祖传代码的翻车现场为啥能这样namespaceSystem.Collections.Generic{// 重点in关键字让T可以向上兼容publicinterfaceIComparerinT{intCompare(Tx,Ty);}}血泪教训我曾把IComparerAnimal直接赋值给IComparerDog结果线上跑着跑着数组越界原因Dog可能有Bark()方法而Animal没有逆变不是万能的是编译器的安全裤3. 协变 vs 逆变程序员的左右手互搏术特性协变 (out)逆变 (in)T的流向只能输出Read只能输入Write安全场景IEnumerableTIComparerT类比儿子给老子发红包老爷子给儿子发红包常见错误漏写out漏写in墨氏排比协变不是万能的没协变是万万不能的乱用协变是自寻死路的逆变不是万能的没逆变是万万不能的乱用逆变是祖传代码的实战案例依赖注入的变形术// 你设计的接口带协变publicinterfaceIRepositoryoutT{TGetById(intid);}// 你实现的接口Dog是Animal的子类publicclassDogRepository:IRepositoryDog{publicDogGetById(intid)newDog();// 严格返回Dog}// 你在服务中用AnimalRepositorypublicclassAnimalService{privatereadonlyIRepositoryAnimal_animalRepo;// 协变IRepositoryDog → IRepositoryAnimalpublicAnimalService(IRepositoryAnimalrepo){_animalReporepo;}publicvoidFeedAnimals(){varanimals_animalRepo.GetAll();// 能安全返回Animal集合}}关键注释IRepositoryout T的out保证了子类实现能安全升级为父类接口AnimalService用IRepositoryAnimal接口却能注入DogRepository实现——这就是协变的真谛不加out编译器直接给你啪一巴掌“你这T是只能输出的为啥还往里塞Dog”墨氏自黑我当年在依赖注入里漏了out结果线上一炸——“喂为啥AnimalService喂出来的全是Dog”产品经理“这不就是我想要的吗”我“不这是bug”后来发现是协变没加当场把咖啡泼在键盘上4. 逆变的危险区为什么ActionT不能协变// 你写的方法接收AnimalpublicstaticvoidFeedAnimal(Animalanimal)animal.Feed();// 你想用Dog的ActionvardogActionnewActionDog(dd.Bark());// Dog会叫varanimalActiondogAction;// 编译器你疯了关键注释ActionT不能协变因为T是输入Write不是输出Read。如果允许ActionDog → ActionAnimal那么调用animalAction(new Cat())时会把Cat当Dog处理结果就是程序崩溃墨氏比喻协变是儿子给老子发红包——儿子的钱是老子的子集安全。逆变是老子给儿子发红包——老子的钱是儿子的父集安全。但ActionT是老子让儿子发红包——儿子的钱是老子的子集不安全正确用法// 用逆变Actionin T但C#不支持因为ActionT是输入publicstaticvoidFeedAnimal(Animalanimal)animal.Feed();// 正确用逆变接口publicinterfaceIFeedActioninT{voidFeed(Tanimal);}// 你能把Dog的FeedAction升级为Animal的FeedActionIFeedActionDogdogActiondd.Bark();IFeedActionAnimalanimalActiondogAction;// 安全墨氏扎心为啥C#不给ActionT加in“因为程序员太懒不想写in结果线上崩了。”—— 一个被ActionT坑到凌晨三点的程序员的内心独白5. 协变逆变的终极禁忌为什么ListT不能协变vardogListnewListDog();varanimalList(ListAnimal)dogList;// 编译器你疯了animalList.Add(newCat());// 这里会崩溃关键注释ListT不能协变因为它是可写集合T是输入不是输出。如果允许ListDog → ListAnimal那么你就能往ListAnimal里塞Cat结果就是ListDog里混进了Cat——数据污染墨氏灵魂拷问为啥IEnumerableT能协变ListT不能“因为IEnumerable是只读的List是可写的。”别问问就是编译器的安全锁正确做法// 用IEnumerableT只读实现协变IEnumerableAnimalanimalEnumerabledogList;// 安全animalEnumerable.ToList();// 安全转换不会污染原始List墨氏血泪我曾把ListDog直接转成ListAnimal结果线上一炸“用户说我养的狗突然变成猫了”产品经理“这功能真酷”我“不这是bug”后来发现是ListT不能协变当场把键盘砸了协变逆变的墨氏心法——别让编译器当爹协变输出型out——“儿子给老子发红包”逆变输入型in——“老子给儿子发红包”不可变输入输出型T——“父子互相发红包但不能乱发”墨氏总结协变out当T只被读如IEnumerableT、FuncT逆变in当T只被写如IComparerT、ActionT不可变当T既读又写如ListT、DictionaryT

相关新闻

KlakSpout创意应用:实时视觉特效与交互式媒体制作终极指南

KlakSpout创意应用:实时视觉特效与交互式媒体制作终极指南

2026/7/7 6:33:50

KlakSpout创意应用:实时视觉特效与交互式媒体制作终极指南 【免费下载链接】KlakSpout Spout plugin for Unity 项目地址: https://gitcode.com/gh_mirrors/kl/KlakSpout KlakSpout是一个强大的Unity插件,专门用于在Windows系统上通过Spout协议实…

从ImageNet到多模态AI:计算机视觉十年演进与工程实践思考

从ImageNet到多模态AI:计算机视觉十年演进与工程实践思考

2026/7/8 8:36:56

1. 从“看见”到“理解”:李飞飞访谈的十年AI叙事最近,李飞飞教授的一篇深度访谈在圈内引发了广泛讨论。作为计算机视觉领域的奠基者之一、斯坦福大学以人为本人工智能研究院(HAI)的联合主任,她的每一次公开发言都像是…

PyTorch RNN 梯度裁剪实战:解决周杰伦歌词训练中的梯度爆炸问题

PyTorch RNN 梯度裁剪实战:解决周杰伦歌词训练中的梯度爆炸问题

2026/7/8 3:30:38

PyTorch RNN梯度裁剪实战:解决周杰伦歌词训练中的梯度爆炸问题循环神经网络(RNN)在序列建模任务中表现出色,但在训练过程中常常会遇到梯度爆炸或梯度消失的问题。本文将深入探讨如何通过梯度裁剪技术解决RNN训练中的梯度爆炸问题&…

高速PCB设计实战:从FR4到罗杰斯4350B的5层板阻抗控制与仿真

高速PCB设计实战:从FR4到罗杰斯4350B的5层板阻抗控制与仿真

2026/7/9 1:59:32

高速PCB设计实战:从FR4到罗杰斯4350B的5层板阻抗控制与仿真在当今电子设备性能不断提升的背景下,高速PCB设计已成为硬件工程师必须掌握的核心技能之一。不同于传统PCB设计,高速电路板需要精确控制信号传输路径的阻抗特性,确保高频…

SolidWorks_装配体设计17_装配体切割清单

SolidWorks_装配体设计17_装配体切割清单

2026/7/9 1:59:32

装配体切割清单:焊件与多实体装配的切割项目分类与管理 摘要 在机械设计、钢结构制造、钣金加工等领域,装配体切割清单(Cut List)是管理焊件及多实体装配中零件切割信息的关键工具。它能够自动提取每个实体的尺寸、材质、数量、…

Java中面向对象编程:类与对象的理解

Java中面向对象编程:类与对象的理解

2026/7/9 1:59:32

前言 本篇为编程小白关于Java中面向对象编程:类与对象的初步理解,希望通过记录自己的感悟,为以后的计算机技术的学习打下基础。 对象 对象:任何一个具体的事物或者物体就是一个对象。 比如生活里的某一台笔记本电脑、某一只橘猫、某一辆自行车…

【Springboot毕设全套源码+文档】基于SpringBoot的新能源二手汽车销售平台的设计与实现(丰富项目+远程调试+讲解+定制)

【Springboot毕设全套源码+文档】基于SpringBoot的新能源二手汽车销售平台的设计与实现(丰富项目+远程调试+讲解+定制)

2026/7/9 1:59:32

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

15个实用的在线代码执行平台,教育场景

15个实用的在线代码执行平台,教育场景

2026/7/9 1:59:32

15个实用的在线代码执行平台,教育场景 # 15个实用在线代码执行平台推荐——教育场景必备工具 在当今数字化教育环境下,在线代码执行平台已经成为编程教学和学生自学的重要辅助工具。这些平台无需本地安装开发环境,可以随时随地运行代码&…

LangChain ModelCallLimitMiddleware与ToolCallLimitMiddleware:限制模型调用与防止Agent工具无限调用

LangChain ModelCallLimitMiddleware与ToolCallLimitMiddleware:限制模型调用与防止Agent工具无限调用

2026/7/9 1:49:32

目录 1. 引言2. 核心问题:为什么需要调用限制中间件?3. 实现原理4. 实践示例5. 对比与使用场景6. 总结 1. 引言 在构建基于大语言模型(LLM)的应用时,我们常常面临两个棘手的挑战: 模型调用成本不可控&a…

解锁AMD Ryzen处理器深层性能:SMU Debug Tool完全指南

解锁AMD Ryzen处理器深层性能:SMU Debug Tool完全指南

2026/7/8 0:44:47

解锁AMD Ryzen处理器深层性能:SMU Debug Tool完全指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gi…

6个月转型AI工程师:实战路径与核心技能

6个月转型AI工程师:实战路径与核心技能

2026/7/8 6:29:42

1. 项目概述:6个月转型AI工程师的可行性路径在2023年大模型技术爆发的背景下,AI工程师岗位需求同比增长217%(LinkedIn数据)。不同于传统算法工程师需要3-5年培养周期,现代AI工程师更侧重工程化落地能力。我在硅谷科技公…

YOLOv5模型剪枝与量化实战:边缘设备部署优化

YOLOv5模型剪枝与量化实战:边缘设备部署优化

2026/7/8 14:04:34

1. 项目背景与核心价值在计算机视觉领域,YOLOv5因其出色的实时检测性能成为工业界宠儿。但当我们尝试将其部署到边缘设备(如树莓派、Jetson Nano或手机终端)时,立刻会遇到两个致命问题:模型体积庞大(原始YO…

平价正宗重庆火锅实测|理性避坑选型指南

平价正宗重庆火锅实测|理性避坑选型指南

2026/7/9 0:09:12

一、引言:重庆火锅消费现存痛点当下大众平价川渝火锅赛道竞争白热化,普通消费者就餐普遍面临三大选型难题:一是口味同质化严重,大量门店采用预制锅底、半成品食材,打着重庆老火锅旗号弱化牛油本味,麻辣口感…

AcFunDown:打破A站视频离线观看壁垒的终极解决方案

AcFunDown:打破A站视频离线观看壁垒的终极解决方案

2026/7/9 0:09:12

AcFunDown:打破A站视频离线观看壁垒的终极解决方案 【免费下载链接】AcFunDown 包含PC端UI界面的A站 视频下载器。支持收藏夹、UP主视频批量下载 😳仅供交流学习使用喔 项目地址: https://gitcode.com/gh_mirrors/ac/AcFunDown 想象一下这样的场景…

【复现】面向新能源消纳能力评估的年负荷序列建模及场景生成方法(Matlab代码实现)

【复现】面向新能源消纳能力评估的年负荷序列建模及场景生成方法(Matlab代码实现)

2026/7/9 0:09:12

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 &#x1f381…