C#协变与逆变

精通C#之逆变与协变 C#精通之路——逆变与协变 从C#4.0开始,泛型接口和泛型委托都支持协变和逆变,由于历史原因,数组也支持协变。 里氏替换原则:任何基类可以出现的地方,子类一定可以出现 协变(out) 协变:及自然的变化,遵循里氏替换原则,表现在代码上则是任何基类都可以被其子类赋值,如Animal = Dog、Animal = Cat 使用out关键字声明(注意和方法中修饰参数的out含义不同)。 被标记的参数类型只能作为方法的返回值(包括只读属性) 在没有协变时: abstract class Ani 阅读详情

C#协变与逆变

在学习C#过程中要灵活运用接口与委托那么协变与逆变是重中之重,本人开始学习时也处于懵懂状态,经过几天的查阅与思考才得出答案。

先来看一个例1:

    class Animal
    {
        public int NumberOfLegs = 4;
    }


    class Dog : Animal
    {


    }


    class Program
    {
        static void Main(string[] args)
        {
            Animal a1 = new Animal();
            Animal a2 = new Dog();//将Dog类型隐式转化为Animal类型
            Console.WriteLine("Number of dog legs:" + a2.NumberOfLegs);
        }
    }

从上面这个例子中我们可以看到,基类是Animal,有一个Dog类从Animal类派生。在Main中,我们创建了一个Dog类型的对象,并且将他成功赋值给Animal类型对象的变量a2。所以说每一个变量都有一种类型,你可以将派生类对象的实例赋值给基类的变量,这叫做赋值兼容性。

那么为什么可以将不同类型的引用转化呢?因为派生类包含了基类的信息,当派生类发现自己要转化和自己基类一个类型时,编译器知道这种做法是完全安全的,因为即将被转化的类型与自己基类型一样,所以不会造成基类里面的数据类型不匹配,所以编译器允许这样的类型转化。而反过来如果Animal类型要转化为Dog那么编译器会报错,这个就是因为如果基类要转化为与自己派生类一样的类型,那么必定会缺省派生类派生出来的那部分信息,这样是非常不安全的,编译器阻止这样做。总结一点,赋值兼容性实际是就是数据转化的安全性。

协变

先来看一个例2:

     class Animal
    {
        public int Legs = 4;
    }


    class Dog : Animal
    {


    }


    delegate T Factory<T>();


    class Program
    {
        static Dog MakeDog()
        {
            return new Dog();
        }


        static void Main(string[] args)
        {
            Factory<Dog> dogMaker = MakeDog;//创建委托对象
            Factory<Animal> animalMaker = MakeDog;//尝试赋值委托对象,转化成功
            Console.WriteLine(animalMaker().Legs);
        }
    }

先让dogMaker方法执行了返回Dog类型然后再把返回的Dog类型转化为Animal类型(Dog类型转换为Animal类型上面陈述了符合赋值兼容性),所以转化成功。

而另一个例3:

 class Animal
    {
        public int Legs = 4;
    }


    class Dog : Animal
    {


    }


    delegate T Factory<T>();


    class Program
    {
        static Dog MakeDog()
        {
            return new Dog();
        }


        static void Main(string[] args)
        {
            Factory<Dog> dogMaker = MakeDog;//创建委托对象
            Factory<Animal> animalMaker = dogMaker;//尝试赋值委托对象,编译器报错。
            Console.WriteLine(animalMaker().Legs);
        }
    }

首先说一点委托和类一样,是一种用户自定义类型。但类表示的是数据和方法的集合,而委托是持有一个或多个方法,以及一系列预定义操作,委托算是特殊的类。上面例子问题在于尽管Dog是Animal的派生类,但是委托Factory<Dog>没有从委托Factory<Animal>派生,两个委托对象是平级的(就是两个互不相干的类),所以赋值兼容性不适用。

那么怎样才可行呢?其实我们看到例2是能成功转换的,所以按照例2原理,加上out关键字即可(请看例4),这样就是强调编译器准备将返回值转化为目标值,这样委托的返回值与方法返回值就匹配了。其实本质上来讲就是根据的赋值兼容性。

例4:

class Animal
    {
        public int Legs = 4;
    }


    class Dog : Animal
    {


    }


    delegate T Factory<out T>();


    class Program
    {
        static Dog MakeDog()
        {
            return new Dog();
        }


        static void Main(string[] args)
        {
            Factory<Dog> dogMaker = MakeDog;//创建委托对象
            Factory<Animal> animalMaker = dogMaker;//成功转换
            Console.WriteLine(animalMaker().Legs);
        }
    }

逆变

先来看看例5:

  class Animal
    {
    }

   class Dog : Animal
    {
    }

    class Program
    {
        delegate Animal Factory<T>();
        static Animal MakeAnimal()
        {
            return new Animal();
        }

        static void Main(string[] args)
        {
            Factory<Animal> animalMaker = MakeAnimal;
            Factory<Dog> dogmaker = animalMaker;//编译器报错,转换失败
        }
    }

这个例子有两点问题,第一:这两个委托对象之间是平级的,没有继承关系,不能转换。第二:你会说像协变一样我加一个out关键字来要求编译器转换委托方法的返回值,但是Dog是父类,而Animal是子类,要求子类转化为父类是不行的,上面提到过因为不安全,但是怎么样才能转换呢?其实你发现可以先让dogmaker委托方法返回值降一级,就是将派生类Dog转化为基类Animal,然后就与animalMaker对应起来了,这个顺序与协变相反,但是仍然是父类转化为子类,本质上满足赋值兼容性,所以我们要加in关键字要求编译器准备好这样的变换。这就是例6:

    class Animal
    {
    }


    class Dog : Animal
    {
    }


    class Program
    {
        delegate Animal Factory<in T>();
        static Animal MakeAnimal()
        {
            return new Animal();
        }


        static void Main(string[] args)
        {
            Factory<Animal> animalMaker = MakeAnimal;
            Factory<Dog> dogmaker = animalMaker;//编译器正常,转换成功
        }
    }
协变与逆变就说到这里,要是我先学的是C++那么这些东西我理解得更快了=~=



C#中的协变逆变 文章目录一、C#中的协变二、C#中的逆变 一、C#中的协变 众所周知,子类对象可以赋值给父类,例如 string str = string.Empty; object obj = str; 不会报错。如果换一种方式,代码如下: public class Father { private string Name { get; set; } } public class Son : Father { private string Name { get; set; } } class Prog 阅读详情

相关推荐

C# 中的协变与逆变

在 C# 中,协变(Covariance)和逆变(Contravariance)是两个重要的概念,主要用于处理泛型类型参数的可变性。这两个概念允许在泛型类型之间进行更灵活的转换,提高了代码的可重用性和灵活性。协变允许将一个派生类型的泛型参数转换为基类型的泛型参数。简单来说,如果有两个泛型接口IFoo和IBar,并且IFoo的泛型参数T是协变的,那么如果A是B的派生类,就可以将IFoo转换为IFoo。逆变允许将一个基类型的泛型参数转换为派生类型的泛型参数。如果有两个泛型接口IFoo和IBar

2401_83367969的博客 2233

C#中的协变与逆变

一:什么是协变与逆变 协变指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,逆变指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型只有泛型接口和泛型委托参数支持协变和逆变 二:引言 using System; using System.Collections.Generic; class MainClass { static void Main() { object o = "str"; List<object&g.

L的博客 5426

你了解C#的协变和逆变吗,看完这篇就懂了

主要介绍了你了解C#的协变和逆变吗,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

C#逆变与协变

C#中逆变与协变一般应用到以下三种场景 一、数组的协变 数组的协变允许派生程度更大的类型的数组隐式转换为派生程度更小的类型的数组。 比如:Object[] os = new String[5]; 二、泛型接口的逆变与协变     public class Program     {         static void Main(string[] args)         {

lcprogram的专栏 1199

Golang实现企业级AI智能体安全合规自动化检测系统

需求:自动化解析欧盟AI法案、中国网络安全法、美国NIST框架等多国法规,提取具体合规要求并映射到企业技术实现。技术挑战法规文本非结构化,语义复杂,存在大量法律术语与技术术语交叉多语言支持(中文、英文、欧盟24种官方语言)法规动态更新(平均每季度更新2-3次)架构创新性:提出了"规则引擎+AI模型+实时监控"的混合架构,兼顾了合规要求的明确性与智能体行为的动态性。性能突破:通过Golang协程、多级缓存、数据库优化等技术,实现了12,800 req/s的高吞吐量和38ms的低延迟。准确性提升。

shao.bing的专栏 723

Ubuntu运行vmware时报错Vmware Unable to install all modules!Use shipped Linux kernel AIO access library解决。

Ubuntu运行vmware时报错Vmware Unable to install all modules! vmware --version 提示下面内容: [AppLoader] Use shipped Linux kernel AIO access library. An up-to-date "libaio" or "libaio1" package from your system is preferred. VMware Workstation 16.1.2 build-17966106

wto882dim的博客 2848

C#进阶-协变与逆变

C#中的逆变和协变是一组强大的泛型特性,它们为我们在处理泛型委托和泛型接口时提供了更灵活、更安全的类型转换机制。本文将深入讨论逆变和协变的语法和用法,并通过具体的示例代码进行演示。

踏雨歌青春,诗酒趁年华 2580

3张RTX 4090就能跑!BAGEL-7B多模态模型本地部署避坑指南(附Flash Attention安装技巧)

本文详细介绍了如何在3张RTX 4090显卡上本地部署BAGEL-7B多模态模型,包括硬件配置、环境设置、多卡并行优化及常见问题解决方案。特别提供了Flash Attention安装技巧和显存优化策略,帮助开发者高效运行这一开源多模态模型,实现文本与图像生成任务。

1a2s3d4f5g的博客 653

图解C#高级教程(四):协变、逆变

本章的主题是可变性(variance),这里的可变性更多的是指基类和派生类之间的转换。可变性分为三种:协变(covariance)、逆变(contravariance)和不变(invariance)。

聚焦C++/Python编程语言、大模型应用开发 2698

Edge浏览器IE模式失效?最新142版本隐藏按钮的3种解决方案

本文针对Edge浏览器142版本中IE模式按钮消失的问题,提供了3种实用解决方案,包括注册表修改、手动添加网站到IE模式列表和企业策略部署方案,帮助用户解决兼容性问题,确保老旧系统正常访问。

weixin_28308325的博客 504

Open3D 合并两个点云【2025最新版】

两个点云中的数据连接,博客长期更新,本文最新更新时间为:2025年4月26日。

点云侠的博客 7435

谈谈我对C#协变和逆变的理解

然后我们在程序中可以这么调用,我们用Person去接受一个Student,这是非常好理解的,从面向对象继承的角度来讲完全合理,因为我需要一个人Person,你给了我一个学生Student。注意这个委托的泛型T使用了out关键字,表示这个委托的参数T是支持协变的。这两个方法的声明和委托的声明保持一致。当然你应该看不懂这个公式,没关系,这边文章将会循序渐进的给大家介绍协变和逆变的概念,以及协变和逆变是为了解决什么问题。以上就是逆变的介绍,简单点说,就是将一个父类型的委托或者接口转换成子类型的委托或者接口。

曹瑞鹏的博客(专注于.NET开发,Unity开发) 2051

在Simulink中搭建一个基于IoT设备间通信协议栈的仿真模型

物联网(IoT)设备间的通信协议栈仿真对于理解不同层级的协议如何协同工作,以及优化网络性能至关重要。通过Simulink结合MATLAB的强大功能,我们可以模拟和分析从物理层到应用层的完整通信过程。下面详细介绍如何使用Simulink进行IoT设备间通信协议栈的仿真。

amy_mhd的博客 156

C#协变和逆变

协变(covariant)和逆变(contravariant)是C#4.0新增的概念。 首先,我们都知道C#中父类可以充当子类来使用,就像我们在泛型中说到的object是所有类型的父类,那么以下变量赋值时不会报错的: string str = string.Empty; object obj = str; 但是如果换一种方式来表达:  创建一个Human类: using System; usin...

kun_wang的博客 1973

电子元器件-三极管终终篇:三极管工作原理详解、参数/手册详解、开关应用详解、Ib电流研究、开关应用实例

三极管工作原理与应用摘要 三极管是一种电流控制器件,通过小基极电流控制大集电极电流。其内部结构由高掺杂发射区、薄低掺杂基区和中等掺杂集电区组成,形成NPN或PNP结。三极管有三种工作状态: 截止区:发射结和集电结均反偏,无放大作用 放大区:发射结正偏,集电结反偏,Ic=βIb 饱和区:两结均正偏,Ic不再随Ib增大 作为开关使用时,需确保三极管深度饱和(Vce≈0.2V)。设计要点包括: 基极电阻计算确保Ib足够大 集电极电阻控制Ic 基极加下拉电阻提高抗干扰 NPN管作地开关,PNP管作电源开关 典型应用

qq_60226558的博客 313

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C# 泛型中的协变和逆变

💎协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。在引用类型系统时,协变、逆变和不变性具有如下定义。这些示例假定一个名为Base的基类和一个名为Derived的派生类。Covariance使你能够使用比原始指定的类型派生程度更大的类型。你可以将的实例分配给类型的变量。

李赛赛的专栏 1719

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

C# 泛型(协变和逆变)

纵观本章,大家已经看到了,如果你创建泛型类型的实例,编译器会接受泛型类型声明以及类型实参来创建构造类型。但是,大家通常会犯的一个错误就是将派生类型的委托分配给基类型委托的变量。下面来看一下这个主题,这叫作可变性(variance)。它分为三种一一一协变(convariance)、逆变(contravariance)和不变(invariance)。

钢铁男儿 1186

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C# 泛型的协变与逆变

那么先讲讲什么是“泛型类协变”你可以这样理解,正常的变换 好比object = String 这样的表达式一样协变主要是向上安全的变换,但是需要根据继承链条一层一层 的向上变化 而不需要进行转换因为是隐式转换,先上一层继承类的代码 不过使 用最容易的理解的,你认为是拆箱与装箱也没什么太大的出入 但是指泛型上的

3922

C# 逆变与协变

该文章中使用了较多的 委托delegate和Lambda表达式,如果你并不熟悉这些,请查看我的文章《委托与匿名委托》、《匿名委托与Lambda表达式》以便帮你建立完整的知识体系。 在C#从诞生到发展壮大的过程中,新知识点不断引入。逆变与协变并不是C#独创的,属于后续引入。在Java中同样存在逆变与协变,后续我还会写一篇Java逆变协变的文章,有兴趣的朋友可以关注一下。 逆变与协变,听起来很抽象、高深,其实很简单。看下面的代码: class Person { } clas

dreamgis的专栏 477

C#中的协变和逆变

C#从2.0开始推出了泛型,极大的简化了相同算法对应不同数据类型的工作。在此基础上,C#4.0更进一步推出了泛型中的协变和逆变,有不少人对这个概念感觉比较模糊,今天就让我们来简单的捋一捋。 最常见的情况 考虑下面代码 object[] objs = new string[]{}; List&amp;lt;object&amp;gt; objList = new List&amp;lt;string&amp;gt;(); 这段代...

君子务本 2743

C# 逆变和协变详解

协变是指在泛型类型的使用中,允许将某个类型参数替换为该参数的派生类(即更具体的类型)。换句话说,协变允许在泛型委托或接口中使用更具体的类型作为返回类型。

学习和分享 1023

深入理解C#的协变和逆变及其限制原因

本篇文章的主题:变体(Variance),上面的object[] objArray = strArray形式被称为协变,此外还有另一种变体形式被称为逆变(一些书籍或文章翻译为抗变)。 变体的主要形式包括协变(Covariance)和逆变(Contravariance),后面我会使用What-Why-How的方式逐个说明这两个概念。

fjjaylz的博客 2602

C#进阶学习(十二)协变逆变

本文简单介绍了C#中协变与逆变的相关知识点。

FAREWELL00075的博客 1079

C# 协变、逆变 看完这篇就懂了

原标题:C# 协变、逆变 看完这篇就懂了 转自:Virgil-Zhou cnblogs.com/VVStudy/p/11404300.html 一、基本概念 官方:协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。 公式: 协变:IFoo<父类> = IFoo<子类>; 逆变:IBar<子类> = IBar<父类>; 暂时不理解没关系,您接着往下看。

qq_37390527的博客 1425
上一篇: C#事件
下一篇: Unity Shader笔记
EagleGoing
博客等级 码龄10年 9粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值