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

发布时间:2026/8/24 21:12:42

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/8/24 23:47:18

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

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

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

2026/8/24 15:10:59

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

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

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

2026/8/22 19:50:39

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

爬取微信公众号?3 行代码过 SNUID 校验

爬取微信公众号?3 行代码过 SNUID 校验

2026/8/25 0:34:36

爬取微信公众号?3 行代码过 SNUID 校验 【免费下载链接】weixin_sogou 爬取微信公众号文章 项目地址: https://gitcode.com/gh_mirrors/we/weixin_sogou 想从搜狗微信搜索里爬微信公众号文章,请求很容易在目标页之前就被 SNUID 校验打回。weixin_…

Goo-Engine安装与编译指南

Goo-Engine安装与编译指南

2026/8/25 0:34:36

Goo-Engine安装与编译指南 【免费下载链接】goo-engine Custom build of blender with some extra NPR features. 项目地址: https://gitcode.com/gh_mirrors/go/goo-engine Goo-Engine 是基于 Blender 定制构建的 NPR(非真实感渲染)引擎&#xf…

Ryzen处理器调不明白?SMUDebugTool帮你把电压和SMU状态都摊开看

Ryzen处理器调不明白?SMUDebugTool帮你把电压和SMU状态都摊开看

2026/8/25 0:34:36

Ryzen处理器调不明白?SMUDebugTool帮你把电压和SMU状态都摊开看 【免费下载链接】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. 项目地址: h…

免越狱 iOS 虚拟定位工具:3 步完成 iPhone 位置模拟完整指南

免越狱 iOS 虚拟定位工具:3 步完成 iPhone 位置模拟完整指南

2026/8/25 0:34:36

免越狱 iOS 虚拟定位工具:3 步完成 iPhone 位置模拟完整指南 【免费下载链接】iFakeLocation Simulate locations on iOS devices on Windows, Mac and Ubuntu. 项目地址: https://gitcode.com/gh_mirrors/if/iFakeLocation 有些位置信息,你未必想…

RPG Maker MV 解密终极指南:30秒恢复rpgmvp图片,3招拿密钥

RPG Maker MV 解密终极指南:30秒恢复rpgmvp图片,3招拿密钥

2026/8/25 0:34:36

RPG Maker MV 解密终极指南:30秒恢复rpgmvp图片,3招拿密钥 【免费下载链接】RPG-Maker-MV-Decrypter You can decrypt RPG-Maker-MV Resource Files with this project ~ If you dont wanna download it, you can use the Script on my HP: 项目地址: …

Nucleus Co-Op 本地分屏上手:从拉代码到四人同屏只需 30 分钟

Nucleus Co-Op 本地分屏上手:从拉代码到四人同屏只需 30 分钟

2026/8/25 0:24:35

Nucleus Co-Op 本地分屏上手:从拉代码到四人同屏只需 30 分钟 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 周五晚上,朋友…

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

2026/8/24 19:53:32

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

2026/8/24 19:56:07

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

2026/8/24 21:16:09

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

2026/8/25 0:04:34

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory Meta Description:GetQzonehistory 是一个QQ空间历史说…

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

2026/8/25 0:04:35

【题目来源】 https://www.luogu.com.cn/problem/P7912 【题目描述】 小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里&#x…

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

2026/8/25 0:04:35

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG 【免费下载链接】transformers.js State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server! 项目地址: https:/…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/22 2:02:26

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/22 4:13:47

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/22 1:32:34

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…