图解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 配置…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
