图解C#高级教程(四):协变、逆变
本章的主题是可变性(variance),这里的可变性更多的是指基类和派生类之间的转换。可变性分为三种:协变(covariance)、逆变(contravariance)和不变(invariance)。
文章目录
- 1. 协变
- 1.1 协变的概念
- 1.2 语法
- 1.3 使用场景
- 1.4 代码例子:使用委托实现协变
- 1.5 代码例子:LINQ 中使用协变
- 2. 逆变
- 2.1 逆变的概念
- 2.2 语法
- 2.3 使用场景
- 2.4 代码示例:委托中使用逆变
- 2.5 代码示例:事件处理中使用逆变
- 3. 一些问题
- 3.1 协变和多态的区别
- 概念区别
- 主要区别总结
1. 协变
在C#中,协变(Covariance)是一种允许将派生类类型替换为基类类型的特性。这种特性通常用于泛型类型或委托中,特别是在返回类型时。协变的主要目的是提高代码的灵活性和可重用性,使得在使用派生类时能够有效地利用基类的接口或方法。
1.1 协变的概念
协变是指在泛型类型的使用中,允许将某个类型参数替换为该参数的派生类。换句话说,协变允许你在泛型委托或接口中使用更具体的类型。在C#中,协变通常用于返回值的情况。
1.2 语法
在C#中,可以通过使用out关键字来声明协变类型参数。下面是协变的基本语法:
public delegate TResult MyDelegate<out TResult>();
这里,TResult 参数前面加了out关键字,表明这个类型参数是协变的。返回类型可以是派生类。
1.3 使用场景
协变的常见使用场景包括:
- 委托:在使用委托时,协变允许将一个返回派生类的委托赋值给返回基类的委托。
- LINQ:在LINQ查询中,使用协变来处理不同类型的集合。
- 事件处理:在事件处理程序中,使用协变来处理不同类型的事件。
1.4 代码例子:使用委托实现协变
using System;// 基类
public class Animal
{public virtual void Speak(){Console.WriteLine("Animal speaks");}
}// 派生类
public class Dog : Animal
{public override void Speak(){Console.WriteLine("Dog barks");}
}// 定义一个协变的委托
public delegate T AnimalDelegate<out T>();class Program
{static void Main(){// 将返回Dog类型的委托赋值给返回Animal类型的委托AnimalDelegate<Animal> animalDelegate = GetDog;// 调用委托并输出结果Animal animal = animalDelegate();animal.Speak(); // 输出: Dog barks}static Dog GetDog(){return new Dog();}
}
输出:
Dog barks
1.5 代码例子:LINQ 中使用协变
using System;
using System.Collections.Generic;
using System.Linq;public class Animal
{public string Name { get; set; }
}public class Dog : Animal { }class Program
{static void Main(){List<Dog> dogs = new List<Dog>{new Dog { Name = "Buddy" },new Dog { Name = "Max" }};// 使用LINQ进行查询,并返回基类类型的集合IEnumerable<Animal> animals = dogs.Select(d => d);foreach (var animal in animals){Console.WriteLine(animal.Name); // 输出: Buddy, Max}}
}
使用协变时,应该注意以下几点:
- 只能在返回值中使用协变,而不能在方法参数中使用。
- 协变使得代码更加灵活,但也可能引入类型安全问题,因此在使用时应谨慎。
2. 逆变
在C#中,逆变(Contravariance)是与协变相反的特性,允许将基类类型替换为派生类类型。逆变主要用于参数类型的上下文,特别是在方法参数时。通过逆变,我们可以使用更通用的类型来替代特定的类型,从而提高代码的灵活性和可重用性。
2.1 逆变的概念
逆变是指在泛型类型的使用中,允许将某个类型参数替换为该参数的基类。这种特性通常在需要处理不同类型的对象时非常有用。表现在代码上就是某个函数的参数类型是基类类型,但是可以接受其派生类类型的实参。
2.2 语法
在C#中,可以通过使用 in 关键字来声明逆变类型参数。下面是逆变的基本语法:
public delegate void MyDelegate<in T>();
2.3 使用场景
逆变的常见使用场景包括:
- 委托:在使用委托时,逆变允许将一个接受派生类的委托赋值给接受基类的委托。
- 事件处理:在事件处理程序中,使用逆变来处理不同类型的事件。
- 集合操作:在处理集合时,逆变可以帮助简化参数类型的定义。
2.4 代码示例:委托中使用逆变
下面的程序实现了一个基类类型的委托指向参数类型为基类类型的方法,但是在执行委托时,传入给委托的参数类型为派生类类型。
using System;// 基类
public class Animal
{public string Name { get; set; }
}// 派生类
public class Dog : Animal { }// 定义一个逆变的委托
public delegate void AnimalAction<in T>(T animal);class Program
{static void Main(){// 将接受Animal类型的委托赋值给接受Dog类型的委托AnimalAction<Animal> animalAction = MakeSound;Dog dog = new Dog { Name = "Buddy" };animalAction(dog); // 输出: Buddy makes a sound}static void MakeSound(Animal animal){Console.WriteLine($"{animal.Name} makes a sound");}
}
2.5 代码示例:事件处理中使用逆变
下面的代码中实现了使用事件和逆变,统一处理不同用户的目的。
public void AddUser(User user)
输出:
John has been added.
Admin has been added.
使用 in 和 out 关键字只适用于委托和接口,不适用于类、结构和方法。
不包括 in 和 out 关键字的委托和接口类型参数叫做不变。这些类型参数不能用于协变和逆变。
3. 一些问题
3.1 协变和多态的区别
概念区别
- 多态:多态是指子类可以替代父类的实例,调用相同的方法但可能会有不同的实现。在面向对象编程中,常通过继承和接口来实现多态性。
public class Animal
{public virtual void Speak(){Console.WriteLine("Animal speaks");}
}public class Dog : Animal
{public override void Speak(){Console.WriteLine("Woof!");}
}Animal myDog = new Dog();
myDog.Speak(); // 输出: Woof!
- 协变:协变是指在泛型类型参数中允许用派生类替代基类。在 C# 中,协变通常与泛型委托和接口相关,允许使用更具体的类型作为返回值。
public delegate T CovariantDelegate<out T>();public class Animal { }
public class Dog : Animal { }public static Dog GetDog() => new Dog();CovariantDelegate<Animal> animalDelegate = GetDog;
Animal animal = animalDelegate(); // 使用协变
主要区别总结
概念范围:
- 多态是一个更广泛的概念,涵盖了通过接口和继承实现不同类型之间的行为相同。
- 协变是关于泛型类型参数的特定实现,主要用于返回值的场景。
实现方式:
- 多态通常通过方法重写(override)和接口实现来实现,允许子类定义父类方法的具体实现。
- 协变是通过在泛型定义中使用 out 关键字来实现,允许使用更具体的类型作为返回值。
适用场景:
- 多态主要用于运行时行为的动态选择,允许对象通过父类接口调用不同的实现。
- 协变主要用于数据结构和类型安全的情况下,特别是在返回类型的灵活性方面。
各位道友,码字不易,记得一键三连呐。
相关文章:
图解C#高级教程(四):协变、逆变
本章的主题是可变性(variance),这里的可变性更多的是指基类和派生类之间的转换。可变性分为三种:协变(covariance)、逆变(contravariance)和不变(invariance)…...
详解CSS中的伪元素
4.3 伪元素 可以把样式应用到文档树中根本不存在的元素上。 ::first-line 文本中的第一行 ::first-letter 文本中的第一个字母 ::after 元素之后添加 ::before 元素之前 代码: <!DOCTYPE html> <html> <head><meta charset"utf-8&q…...
paper_template
paper_template Title 文章标题 Abstract 摘要 Keywords 关键词 Highlights Highlights / 创新点 Summary 写完笔记之后最后填,概述文章的内容,以后查阅笔记的时候先看这一段。 Backgrounds 描述当前研究背景 Research Objective 作者的研…...
【Bug】解决 Ubuntu 中 “error: Unable to Find Python3 Executable” 错误
解决 Ubuntu 中 “Unable to Find Python3 Executable” 错误 在 Ubuntu 系统上使用 Python 进行开发时,遇到找不到 python3 可执行文件的错误。 主要问题是无法正常打开终端(原生与terminator),找不到python3,且无法…...
CUDA与TensorRT学习六:模型部署-CNN、模型部署-YOLOv8检测器、部署BEVFusion模型
文章目录 一、模型部署-CNN二、模型部署-YOLOv8检测器三、部署BEVFusion模型 一、模型部署-CNN 二、模型部署-YOLOv8检测器 三、部署BEVFusion模型...
防sql注入的网站登录系统设计与实现
课程名称 网络安全 大作业名称 防sql注入的网站登录系统设计与实现 姓名 学号 班级 大 作 业 要 求 结合mysql数据库设计一个web登录页面密码需密文存放(可以采用hash方式,建议用sha1或md5加盐)采用服务器端的验证码&#…...
如何快速切换电脑的ip地址
在当今的数字化时代,IP地址作为网络身份的重要标识,其重要性日益凸显。无论是出于保护个人隐私的需要,还是为了访问特定的网络服务等,快速切换电脑的IP地址已成为许多用户的迫切需求。本文将为你介绍几种实用的方法,帮…...
鸿蒙HarmonyOS之选择相册文件(照片/视频)方法
一、新建文件工具类FileUtil.ets 包含:选择照片方法、获取文件类型方法、去除后缀、获取后缀方法 import { BusinessError, request } from kit.BasicServicesKit; import photoAccessHelper from ohos.file.photoAccessHelper; import bundleManager from ohos.b…...
【QT Qucik】C++交互:接收QML信号
在本节课中,我们将深入探讨如何在C中接收QML发出的信号。我们将分为几个部分,详细说明信号的定义、发送及其在C中的接收。 理解信号和槽机制 Qt的信号与槽机制是一种用于对象之间通信的强大工具。信号是对象在特定事件发生时发送的通知,而槽…...
【C++】关键字+命名空间
大家好,我是苏貝,本篇博客带大家了解C的命名空间,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 一. 关键字二. 命名空间2.1 命名空间的定义2.2 命名空间的使用a. 命名空间名称作用域限定…...
网络层——IP
IP地址 结构: 由32位二进制数组成,通常用点分的形式被分为四个部分,每个部分1byte,最大值为255。 从功能的角度看,ip地址由两部分组成,网络号和主机号。网络号标识了ip所在的网段,主机号标识了…...
随笔 漫游互联网
网络编程基础:漫游互联网 温故而知新,可以为师矣。互联网我们可以想象成一个立体的网状结构,由一个一个的小网络组成的网状结构,在一个一个小网络中通过一台一台机器组成,经过几十年的发展终于有了今天这个样子。谈论…...
8.9K Star,开源自托管离线翻译引擎
Hi,骚年,我是大 G,公众号「GitHub 指北」会推荐 GitHub 上有趣有用的项目,一分钟 get 一个优秀的开源项目,挖掘开源的价值,欢迎关注。 在全球化的今天,跨语言交流已成为日常需求,然…...
MySQL基础之DML
MySQL基础之DML 语法不区分大小写 分类 DD(definition)L 定义DM(manipulation)L 操作DQ(query)L 查询DC(control)L 控制 添加数据 # 指定字段添加数据(一条)insert into 表名(字段1,字段2,...) values(值1,值2,...);# 全部字段添加数据(一条)insert into 表名 values(值1,值…...
男单新老对决:林诗栋VS马龙,巅峰之战
听闻了那场激动人心的新老对决,不禁让人热血沸腾。在这场乒乓球的巅峰之战中,林诗栋与马龙的对决无疑是一场视觉与技术的盛宴。 3:3的决胜局,两位选手的每一次挥拍都充满了策略与智慧,他们的每一次得分都让人心跳加速。 林诗栋&am…...
Java如何判断堆区中的对象可以被回收了?
如何判断堆区中的对象可以被回收了 在Java中,垃圾回收机制会帮助我们自动回收不再被使用的对象,已到达即使释放内存的效果,但是Java又是怎么知道哪些对象不会再被我们继续使用了呢,希望你通过本篇文章,理解引用计数法与…...
.Net 6.0 监听Windows网络状态切换
上次发了一个文章获取windows网络状态,判断是否可以访问互联网。传送门:获取本机网络状态 这次我们监听网络状态切换,具体代码如下: public class WindowsNetworkHelper {private static Action<bool>? _NetworkStatusCh…...
UE4 材质学习笔记01(什么是着色器/PBR基础)
1.什么是shader 着色器是控制屏幕上每个像素颜色的代码,这些代码通常在图形处理器上运行。 现如今游戏引擎使用先进的基于物理的渲染和照明。而且照明模型模型大多数是被锁定的。 因此我们创建着色器可以控制颜色,法线,粗糙度,…...
算法 | 位运算(哈希思想)
位运算 &与两个位都为1时,结果才为1(有0为0)|或两个位都为0时,结果才为0(有1为1)^异或两个位相同为0,相异为1~取反0变1,1变0<<左移各二进位全部左移若干位,高…...
前端提升方向
1、脚手架配置:首先你会发现,一旦团队项目里多个项目之间的配置或者规范不同步,那么每个项目的配置都需要手动修改,而这很浪费时间。所以,你可以发起了一个团队的脚手架项目,把项目中的代码规范、Vite 配置…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
