C# 泛型中的协变、抗变和裂变:概念与应用

在 C# 中,泛型是一种强大的工具,它允许我们编写类型安全且灵活的代码。泛型类型参数不仅可以增强代码的可重用性,还允许我们指定类型的约束和行为。然而,当涉及到泛型类型参数的继承关系时,C# 引入了协变(Covariance)、**抗变(Contravariance)裂变(Invariant)**这三个重要概念,用来控制泛型类型参数在类型继承中的转换方式。

什么是协变、抗变和裂变?

在讲解它们之前,我们首先需要了解类型继承的基本概念。当我们定义泛型类型时,泛型类型参数(例如 T)可以是任何类型。根据类型的继承关系,基类和派生类之间有不同的转换规则。C# 为了在处理泛型时提供更多灵活性,定义了协变、抗变和裂变这三种方式,用来描述如何在继承层次结构中进行类型转换。

协变 (Covariance)

协变是指你可以将一个泛型类型参数从一个基类类型转换为其派生类类型。换句话说,协变允许你在返回类型上使用派生类,即你可以返回一个更具体的类型,而不仅仅是基类类型。

  • 协变的特点
    • 适用于输出类型(返回值)。也就是说,泛型类型参数只能用作返回类型,不能用于方法的参数。
    • 在 C# 中,通过 out 关键字声明泛型类型参数为协变。
    • 协变通常用于返回数据的场景,比如 IEnumerable<T>IQueryable<T> 等。
示例:
// 定义一个协变的泛型接口
public interface IShape<out T>
{
    T GetShape();
}

// 基类
public class Circle { }

// 派生类
public class Square : Circle { }

public class ShapeCollection : IShape<Circle>
{
    public Circle GetShape()
    {
        return new Circle();
    }
}

public class Program
{
    public static void Main()
    {
        // 创建一个IShape<Circle>实例
        IShape<Circle> circleShape = new ShapeCollection();
        
        // 协变:将IShape<Circle>赋给IShape<Square>,因为Square是Circle的派生类
        IShape<Square> squareShape = circleShape;  // 协变,Circle类型可以赋给Square类型
    }
}

在上面的代码中,IShape<out T> 表示泛型类型 T 是协变的。这意味着你可以将 IShape<Circle> 赋值给 IShape<Square>,因为 SquareCircle 的派生类。协变仅适用于返回类型,因此 T 只能作为返回值使用,不能作为参数。

抗变 (Contravariance)

抗变与协变相反,抗变允许你将泛型类型参数从派生类类型转换为基类类型。换句话说,抗变允许你在方法的输入类型上使用更一般的类型,也就是基类类型。

  • 抗变的特点
    • 适用于输入类型(方法参数)。也就是说,泛型类型参数只能用作方法参数,不能用作返回值。
    • 在 C# 中,通过 in 关键字声明泛型类型参数为抗变。
    • 抗变通常用于需要接受数据的场景,比如 IComparer<T> 或其他处理类型数据的接口。
示例:
// 定义一个抗变的泛型接口
public interface IProcessor<in T>
{
    void Process(T item);
}

// 基类
public class Animal { }

// 派生类
public class Dog : Animal { }

public class AnimalProcessor : IProcessor<Animal>
{
    public void Process(Animal item)
    {
        Console.WriteLine("Processing animal");
    }
}

public class Program
{
    public static void Main()
    {
        // 创建IProcessor<Dog>实例
        IProcessor<Dog> dogProcessor = new AnimalProcessor();
        
        // 抗变:将IProcessor<Animal>赋给IProcessor<Dog>,因为Dog是Animal的派生类
        dogProcessor.Process(new Dog());
    }
}

在上面的代码中,IProcessor<in T> 表示泛型类型 T 是抗变的。这意味着你可以将 IProcessor<Dog> 类型的实例赋值给 IProcessor<Animal>,因为 DogAnimal 的派生类。抗变使得我们能够接受比当前类型更泛化的类型,这在处理数据输入时非常有用。

裂变 (Invariant)

裂变意味着泛型类型参数既不能进行协变,也不能进行抗变。换句话说,裂变强制要求泛型类型参数完全匹配类型。即使存在继承关系,也不能将派生类类型赋值给基类类型,反之亦然。

  • 裂变的特点
    • 泛型类型参数不能进行协变或抗变。
    • 适用于希望对类型进行严格控制的场景。
    • 在 C# 中,泛型类型参数默认是裂变的。
示例:
// 裂变:不允许进行协变或抗变
public class Box<T>
{
    public T Item { get; set; }
}

public class Animal { }
public class Dog : Animal { }

public class Program
{
    public static void Main()
    {
        // 创建一个Box<Dog>实例
        Box<Dog> dogBox = new Box<Dog>();
        
        // 错误:不能将Box<Dog>赋给Box<Animal>,因为它们是裂变类型
        // Box<Animal> animalBox = dogBox;  // 编译错误
    }
}

在这个例子中,Box<T> 是一个裂变类型,它不允许将 Box<Dog> 转换为 Box<Animal>,即使 DogAnimal 的派生类。泛型类型 T 必须完全匹配,不能进行任何类型转换。

协变、抗变和裂变的总结

特性协变 (Covariance)抗变 (Contravariance)裂变 (Invariant)
关键字outin
适用场景用于返回类型(输出)。用于输入类型(方法参数)。用于要求类型严格匹配的场景。
类型兼容性允许将派生类型转换为基类类型。允许将基类类型转换为派生类型。类型必须完全相同。
示例IEnumerable<out T>IComparer<in T>List<T>Box<T>
使用目的用于返回数据且希望支持类型层次结构中的派生类型。用于处理数据且希望支持类型层次结构中的基类类型。用于严格类型约束,避免类型转换错误。

结论

C# 中的泛型协变、抗变和裂变为开发者提供了不同的类型兼容性策略。这些概念帮助我们在处理泛型类型时做出灵活且类型安全的决策。协变适用于返回数据时使用派生类型,抗变适用于接受数据时使用基类类型,而裂变则用于要求类型严格匹配的场景。掌握这些概念,能够让你在编写泛型代码时更加高效与安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984543.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谷歌AI最新发布的可微分逻辑元胞自动机(DiffLogic CA)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

如何使用Postman,通过Mock的方式测试我们的API

这篇文章将教会大家如何利用 postman&#xff0c;通过 Mock 的方式测试我们的 API。 什么是 Mock Mock 是一项特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行单元测试。通常情况下&#xff0c;Mock 与其他方法的主要区别就是&#xff0c;用于取代代码依赖项的模拟对…

pytest基础知识

pytest知识了解 pytest的基础知识了解&#xff1a;Python测试框架之pytest详解_lovedingd的博客-CSDN博客_pytest框架 (包含设置断点&#xff0c;pdb&#xff0c;获取最慢的10个用例的执行耗时) pytest-pytest.main()运行测试用例&#xff0c;pytest参数&#xff1a; pytest-…

LM Studio 替换源的方式解决huggingface.co无法访问的问题

安装软件完成之后&#xff0c;不要打开&#xff0c;打开了就直接关闭 在安装目录下&#xff0c;比如我安装在E:\Program Files\LM Studio 下面三个文件中的huggingface.co全部替换为hf-mirror.com然后再打开即可。 E:\Program Files\LM Studio\resources\app\.webpack\rende…

【含文档+PPT+源码】基于微信小程序的乡村振兴民宿管理系统

项目介绍 本课程演示的是一款基于微信小程序的乡村振兴民宿管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3.该…

五、OpenGL中Shader与C++数据传输

文章目录 一、概述二、Shader 代码文件的基本格式三、Shader的向量语法介绍四、Shader之间的数据传输五、Shader与C的数据传输uniform六、完整示例 一、概述 在 OpenGL 中&#xff0c;Shader&#xff08;着色器&#xff09;使用 GLSL&#xff08;OpenGL Shading Language&…

docker不停机部署

背景 最近做大疆项目时&#xff0c;后台更新部署时&#xff0c;机场和无人机就会掉线。设备自动重连注册时间比较长&#xff0c;应用长时间不可用。所以需要灰色发布服务。docker-compose的swarm模式可解决此问题。 服务构建脚本Dockerfile # 使用官方Java基础镜像&#xff…

工作记录 2016-12-22

工作记录 2016-12-22 更新的问题 1、修改了Job Summary的Bill Amount的Bug。 2、修改了Account #的宽度。 3、修改了Clearinghouse Status的默认查询的条件。 4、修改了Upload Files的Add File的bug。 5、Pending Pool、Missing Infos加了Write Off&#xff0c;修改了Histor…

QLoggingCategory类使用

QLoggingCategory类使用 QLoggingCategory的概述 QLoggingCategory是Qt的日志策略类&#xff1b;可以通过声明不同的日志策略对象来输出不同的日志信息。打印信息类型如下&#xff1a;宏 Q_DECLARE_LOGGING_CATEGORY(name) 定义一个返回QLoggingCategory对象函数&#xff0c;…

Linux红帽:RHCSA认证知识讲解(五)从红帽和 DNF 软件仓库下载、安装、更新和管理软件包

Linux红帽&#xff1a;RHCSA认证知识讲解&#xff08;五&#xff09;从红帽和 DNF 软件仓库下载、安装、更新和管理软件包 前言一、DNF 软件包管理基础1.1 核心操作命令安装软件包卸载软件包重新安装软件包 1.2 软件仓库原理 二、配置自定义软件仓库步骤 1&#xff1a;清理默认…

Go本地缓存设计与实现

本地缓存是一个项目中很常见的组件。在很多人的眼中就是一个简单的key-value的map存储即可实现&#xff0c;但实际上&#xff0c;设计一个本地缓存需要考虑的问题远比你想象的多&#xff0c;比如说&#xff0c;本地缓存是将数据存储在内存&#xff0c;若数据量激增突破了内存限…

通义万相2.1开源版本地化部署攻略,生成视频再填利器

2025 年 2 月 25 日晚上 11&#xff1a;00 通义万相 2.1 开源发布&#xff0c;前两周太忙没空搞它&#xff0c;这个周末&#xff0c;也来本地化部署一个&#xff0c;体验生成效果如何&#xff0c;总的来说&#xff0c;它在国内文生视频、图生视频的行列处于领先位置&#xff0c…

Jetson Xavier NX安装CUDA加速的OpenCV

我们使用SDKManager刷机完成后&#xff0c;使用jtop查看&#xff0c;发现OpenCV 是不带CUDA加速的&#xff0c;因此&#xff0c;我们需要安装CUDA加速的OpenCV&#xff0c;这样后续在使用的时候速度会快很多。 首先我们先卸载默认OpenCV sudo apt purge libopencv* -y sudo …

基于PaddleNLP使用DeepSeek-R1搭建智能体

基于PaddleNLP使用DeepSeek-R1搭建智能体 最近在学习DeepSeek&#xff0c;找到了PaddleNLP星河社区大模型&#xff0c;跟着敲写了一遍。内容来源&#xff1a;DeepSeek实战训练营&#xff1a;从云端模型部署到应用开发 - 飞桨AI Studio星河社区-人工智能学习与实训社区 本项目基…

给大家推荐8个好玩有趣的网站

1、Home Apothecary 家庭药房 https://apothecary.tips/zh Home Apothecary&#xff08;家庭药房&#xff09;结合传统中医智慧与现代科学验证&#xff0c;提供涵盖睡眠改善、免疫力提升、肠胃调理、活力增强等健康需求的天然养生饮品配方。精选安神助眠、四季调养、舒缓压力…

使用Beanshell前置处理器对Jmeter的请求body进行加密

这里我们用HmacSHA256来进行加密举例&#xff1a; 步骤&#xff1a; 1.先获取请求参数并对请求参数进行处理&#xff08;处理成String类型&#xff09; //处理请求参数的两种方法&#xff1a; //方法一&#xff1a; //获取请求 Arguments args sampler.getArguments(); //转…

利用paddleocr解决图片旋转问题

由于之前使用easyocr识别图片的时候发现旋转的图片或者倒置的图片效果很差&#xff0c;来利用 cv2.minAreaRect()获取旋转角度&#xff0c;只能解决0-90&#xff0c;对于倒置的图片不能很好解决&#xff0c;因此使用paddleocr中方向分类检测&#xff08;只能返回0&#xff0c;1…

数据结构(蓝桥杯常考点)

数据结构 前言&#xff1a;这个是针对于蓝桥杯竞赛常考的数据结构内容&#xff0c;基础算法比如高精度这些会在下期给大家总结 数据结构 竞赛中&#xff0c;时间复杂度不能超过10的7次方&#xff08;1秒&#xff09;到10的8次方&#xff08;2秒&#xff09; 空间限制&#x…

Python 入

Python 入侵交换机 随着网络安全威胁不断增加&#xff0c;对于网络设备的安全防护变得愈发重要。而交换机作为网络中重要的设备之一&#xff0c;也需要加强安全保护。本文将介绍如何利用Python来入侵交换机&#xff0c;并对其进行漏洞扫描和安全检测。 1. Python 入侵交换机原…

自然语言处理:最大期望值算法

介绍 大家好&#xff0c;博主又来给大家分享知识了&#xff0c;今天给大家分享的内容是自然语言处理中的最大期望值算法。那么什么是最大期望值算法呢&#xff1f; 最大期望值算法&#xff0c;英文简称为EM算法&#xff0c;它的核心思想非常巧妙。它把求解模型参数的过程分成…