C# 泛型的协变和逆变

1. 可变性的类型:协变性和逆变性

可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量。协变和逆变是两个相互对立的概念:

  • 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变
  • 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的。

2. C# 4.0对泛型可变性的支持

在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。

C# 4.0通过两个关键字:outin来分别支持以协变和逆变的方式使用泛型。

我们来看一段利用了协变类型参数的代码:

public class BaseClass
{//...
}public class DerivedClass : BaseClass
{//...
}

下面我们利用协变类型参数,可以执行类似于普通的多态性的分配:

IEnumerable<DerivedClass> d = new List<DerivedClass>();
IEnumerable<BaseClass> b = d;

在上面的实例中,在C# 4.0之前是不能正常编译的,除了对赋值给基类集合时将子类集合做一个强制转换,但是在运行时仍然会抛出一个类型转换的异常。

下面我们再看一个关于逆变的实例代码:

Action<BaseClass> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<DerivedClass> d = b;
d(new DerivedClass());

在上面的示例中我们 Action<BaseClass> 类型的委托分配给类型 Action<DerivedClass> 的变量,根据逆变的定义我们可以知道 Action<T> 类型是支持逆变的。

为什么IEnumerable<T>Action<T> 可以分别支持类型的协变和逆变呢?我们查看这两个类型在 .NET 中的定义:

//IEnumerable<T> 接口的定义(支持协变)
public interface IEnumerable<out T> : IEnumerable//Action<T> 委托的定义(支持逆变)
public delegate void Action<in T>(T obj);

为了保证类型的安全,C#编译器对使用了 outin 关键字的泛型参数添加了一些限制:

  • 支持协变(out)的类型参数只能用在输出位置:函数返回值、属性的get访问器以及委托参数的某些位置
  • 支持逆变(in)的类型参数只能用在输入位置:方法参数或委托参数的某些位置中出现。

3. C#中泛型可变性的限制

1. 不支持类的类型参数的可变性

只有接口和委托可以拥有可变的类型参数。inout 修饰符只能用来修饰泛型接口和泛型委托。

2. 可变性只支持引用转换

可变性只能用于引用类型,禁止任何值类型和用户定义的转换,如下面的转换是无效的:

  • IEnumerable<int> 转换为 IEnumerable<object> ——装箱转换
  • IEnumerable<short> 转换为 IEnumerable<int> ——值类型转换
  • IEnumerable<string> 转换为 IEnumerable<XName> ——用户定义的转换

3. 类型参数使用了 out 或者 ref 将禁止可变性

对于泛型类型参数来说,如果要将该类型的实参传给使用 out 或者 ref 关键字的方法,便不允许可变性,如:

delegate void someDelegate<in T>(ref T t)

这段代码编译器会报错。

4. 可变性必须显式指定

从实现上来说编译器完全可以自己判断哪些泛型参数能够逆变和协变,但实际却没有这么做,这是因为C#的开发团队认为:

必须由开发者明确的指定可变性,因为这会促使开发者考虑他们的行为将会带来什么后果,从而思考他们的设计是否合理。

5. 注意破坏性修改

在修改已有代码接口的可变性时,会有破坏当前代码的风险。例如,如果你依赖于不允许可变性的is或as操作符的结果,运行在.NET 4时,代码的行为将有所不同。同样,在某些情况下,因为有了更多可用的选项,重载决策也会选择不同的方法。所以在对已有代码引入可变性时要做好足够的单元测试以及防御措施。

6. 多播委托与可变性不能混用

下面的代码能够通过编译,但是在运行时会抛出 ArgumentException 异常:

Func<string> stringFunc = () => "";
Func<object> objectFunc = () => new object();
Func<object> combined = objectFunc + stringFunc;

这是因为负责链接多个委托的 Delegate.Combine方法要求参数必须为相同的类型。上面的示例我们可以修改成如下正确的代码:

Func<string> stringFunc = () => "";
Func<object> defensiveCopy = new Func<object>(stringFunc);
Func<object> objectFunc = () => new object();
Func<object> combined = objectFunc + defensiveCopy;

参考&扩展阅读

协变和逆变
泛型中的协变和逆变
委托中的协变和逆变
《深入理解C#》:13.3 接口和委托的泛型可变性
《Effective C#》:条目29:支持泛型协变和逆变
《CLR via C#》:12.5 委托和接口的逆变和协变泛型类型实参

转载于:https://www.cnblogs.com/IPrograming/p/4471130.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java数据类型的站位_Java 数据类型在实际开发中应用

在前边的文章中&#xff0c;我已经介绍了Java核心的容器IO等&#xff0c;现在我来说一下java中的数据类型。在java中&#xff0c;一切东西皆为对象(这句话意思是java中绝大数情况都用对象)&#xff0c;极少数不是对象的&#xff0c;也存在与之对应的对象(比如基本数据类型存在与…

块状元素的居中,首先设置宽度,再设 margin: 0 auto

块状元素的居中&#xff0c;首先设置宽度 width"10px"&#xff0c;再设 margin: 0 auto; 。转载于:https://www.cnblogs.com/npk19195global/p/4475576.html

java amf3_Java AMF3 反序列化漏洞分析

写在前面的话AMF(Action Message Format)是一种二进制序列化格式&#xff0c;之前主要是Flash应用程序在使用这种格式。近期&#xff0c;Code White发现有多个Java AMF库中存在目前&#xff0c;漏洞相关信息已上报至美国CERT(详情请参考美国CERT VU#307983)概述目前&#xff0c…

typecho除了首页其他大部分网页404怎么办?

server {listen 80;server_name blog.localhost; #绑定域名index index.htm index.html index.php; #默认文件root /data/webroot/blog; #网站根目录location ~ \.php$ {fastcgi_pass 127.0.0.1:9000;fastcgi_index index.php;include fastcgi_params;}if (-f $request_filenam…

java能写复杂的查询么_spring-data-jpa 复杂查询的写法(包含or的查询)

场景如下&#xff1a;很简单的CMS常用查询&#xff0c;栏目下有多个子栏目&#xff0c;子栏目有包含内容。public class Channel{....private String parentIds;//所有的父节点&#xff0c;简化查询策略 例如 0,1,11,private List channels Lists.newArrayList(); //所有的儿子…

在Ubuntu上安装misterhouse

配置Ubuntu 您将对Ubuntu的配置做某些微小更改以便远程管理MH BOX和安装Misterhouse正常运行所需的必要的软件模块。首先要进行系统升级。因为系统提供的欧洲源更新实在是太慢了&#xff01;更换源是为了节省时间&#xff0c;尽快得将系统更新一遍。安装完系统后这是重要的一个…

java+路径拦截实现_SpringBoot实现拦截器

首先&#xff0c;你的项目要能跑起来&#xff0c;且基于springboot的boot的拦截器不用配置web.xml&#xff0c;按照特定名字书写(后缀注解)&#xff0c;它会自动识别(位置随便放&#xff0c;只要在代码目录下就可以)1.拦截器 SessionInterceptor.javapackage com.hfanss.blog.u…

Codeforces 524C Idempotent functions

题目链接&#xff1a;http://codeforces.com/problemset/problem/542/C 题目大意&#xff1a;给定一种运算f&#xff0c;对于输入的数组来说&#xff0c;一步操作&#xff0c;f(x) a[x]&#xff0c;两步操作&#xff0c;f^2(x) a[a[x]]....倘若每次进行k步操作之后&#xff0…

c 获取mysql安装路径_linux查看mysql安装路径

Linux下查看mysql、apache是否安装,并卸载。指令 ps -ef|grep mysql 得出结果root 17659 1 0 2011 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe --datadir/var/lib/mysql --socket/var/lib/mysql/mysql.sock --log-error/var/log/mysqld.log --pid-file/var/run/mysqld/mysqld.p…

微信JS-SDK

<div class"lbox_close wxapi_form"><h3 id"menu-basic">基础接口</h3><span class"desc">判断当前客户端是否支持指定JS接口</span><button class"btn btn_primary" id"checkJsApi">c…

java exif 语言编码_Java读取图片EXIF信息的代码

Java读取图片EXIF信息的代码本文实例讲述了Java读取图片EXIF信息的方法。分享给大家供大家参考。具体分析如下&#xff1a;首先介绍一下什么是EXIF&#xff0c;EXIF是Exchangeable Image File的缩写&#xff0c;这是一种专门为数码相机照片设定的格式。这种格式可以用来记录数字…

Java和pathion_Spring配置中的classpath:与classpath*:的区别

概念解释及使用场景&#xff1a;classpath是指WEB-INF文件夹下的classes目录。通常我们一般使用这种写法实在web.xml中&#xff0c;比如spring加载bean的上下文时&#xff0c;如下&#xff1a;contextConfigLocationclasspath*:/spring-context-*.xmlorg.springframework.web.c…

PHP单引号 ' ',没有任何意义,对所引内容不经任何处理直接拿过来

单引号 没有任何意义&#xff0c;对单引号内部内容不经任何处理直接拿过来。转载于:https://www.cnblogs.com/npk19195global/p/4490758.html

UILabel 宽高自适应

在iOS中&#xff0c;经常遇到需要根据字符串的内容动态指定UILabel&#xff0c;UITextView,UITableViewCell等的高度的情况&#xff0c;这个时候就需要动态的计算字符串内容的高度&#xff0c;下面是计算的方法&#xff1a; /** method 获取指定宽度情况ixa&#xff0c;字符串…

php debug build no,php – 尝试安装xdebug:找不到配置文件

我按照这些说明下载xdebug&#xff1a;http://xdebug.org/wizard.php.向导的输出是这样的&#xff1a;SummaryXdebug installed: noServer API: Apache 2.0 HandlerWindows: noZend Server: noPHP Version: 5.4.17Zend API nr: 220100525PHP API nr: 20100525Debug Build: noTh…

自动自发

<自动自发> 第1章 序言 你属于哪种人&#xff1f; 我们经常会听到这些熟悉的话语&#xff1a; “现在是午餐时间&#xff0c;请您3点以后再打过来吧。” “那根本就不是我负责的工作。” “我现在很忙。” “那是汉曼的工作。” “我不知道该怎样帮助你。” “你去图书馆…

php修改js内容,js怎样修改html元素的内容?HTML DOM实现修改内容

js怎样修改html元素的内容&#xff1f;本章就给大家介绍在js中利用HTML DOM是怎样修改html元素内容的。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对你们有所帮助。首先我们要了解HTML DOM是什么&#xff1f;HTML DOM的作用是什么&#xff1f;HTML…

java中的删除函数的使用方法,如何用Java删除文件

本文概述有两种删除Java文件的方法&#xff1a;使用File.delete()方法使用File.deleteOnExit()方法Java File.delete()方法在Java中, 我们可以使用File类的File.delete()方法删除文件。 delete()方法删除由抽象路径名表示的文件或目录。如果路径名是目录, 则该目录必须为空才能…

windows操作笔记

使用服务或其他windows应用的过程中&#xff0c;可能会遇到莫名其妙的错误&#xff0c;这时候从控制面板中&#xff0c;找到管理工具&#xff0c;打开事件查看器&#xff0c;或者通过计算机管理&#xff0c;找到日志中的记录&#xff0c;如果是代码错误&#xff0c;会给出提示信…

php smarty模板配置,Smarty模板简单配置与使用方法示例

这篇文章主要介绍了Smarty模板简单配置与使用方法,涉及Smarty模板的基本配置与缓存文件的相关使用技巧,需要的朋友可以参考下本文实例讲述了Smarty模板简单配置与使用方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;在Smarty目录中建立templates,templates_c,conf…