c# 协变和逆变的理解
- 是什么
===
1.1 协变
协变指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。如 string 到 object 的转换。多见于类型参数用作方法的返回值。
1.2 逆变
逆变指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型。如 object 到 string 的转换。多见于类型参数用作方法的输入值。
泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。
- 怎么理解
===
假如有一个 sub 子类和 parent 父类,我们可以很轻易地将 sub 转换成 parent,这是类型安全的,反之则不行。其实很好理解,子类必然具有相同或更多的属性和方法,所以转换成属性和方法可能更少的父类,只需要去除自身的部分属性和方法即可,这对编译器而言是可行的。反之,你怎么要求编译器为父类增加更多的成员呢。数组也继承了这一特性,对于一个string[]类型而言
理解了上述概念后,让我们来看看协变和逆变的概念,这里我们只谈谈关于接口可变性中的一些内容。以下我简单给出一个接口及其实现。
public interface IMyInTerface<T>
{
T Method1();
void Method2(T t);
}
public class MyClass<T> : IMyInTerface<T>
{
public T Method1()
{
return default(T);
}
public void Method2(T t)
{
Console.WriteLine(typeof(T).Name);
}
}
对应的调用方法代码如下
class Program
{
static void Main(string[] args)
{
// 协变演示
IMyInTerface<Sub> sub = new MyClass<Sub>();
Parent parent1 = sub.Method1();
// 逆变演示
IMyInTerface<Parent> parent2 = new MyClass<Parent>();
parent2.Method2(new Sub());
//// 协变演示
//IMyInTerface<Parent> parent = new MyClass<Parent>();
//Sub sub1 = parent.Method1();
//// 逆变演示
//IMyInTerface<Sub> sub2 = new MyClass<Sub>();
//sub2.Method2(new Parent());
Console.ReadKey();
}
}
以上调用代码都是正常的,但是当我们将上述代码的子类和父类的位置调换,换成上述注释中的代码,编译器则会报错。这便是违背了本文最重要的一个原则:类型转换中,对编译器而言只有子类到父类的转化才是安全的。
比如说,我们不能把 parent 的实例 赋值给 sub1 ,我们也不能把 new Parent() 代替 Sub 传入 Method2() 方法。
所以这一切,都是命运石之门的选择呀。
也正是因此,为了防止开发者写出错误的代码,.net 设计者便用了协变和逆变(对应 out 和 in 关键字)来强制要求正确行为。也就是说,即使你想这么做,一旦你用了微软提供的 IEnumerable 等接口,你也无法进行错误的类型转换了。所以归根到底,协变和逆变只是一种约束而已,这种规范限制了你的泛型接口中要么只能有将类型参数当作返回值的协变相容方法(加了 out 关键字),要么只能有将类型参数当作输入值的逆变相容方法(加了 in 关键字)。
本文针对的是对协变和逆变存在部分理解但是仍然有些迷糊的开发者群体,而笔者也忙于新技术的理解和投入使用,有段时间没能分享所学所得,这次也只是花了十几分钟撷取了重要概念记录答疑,希望能帮到一部分人,以上就是我的期望了。假如有不理解的,欢迎在评论区贴出即可。
c# 协变和逆变的理解的更多相关文章
- 再谈对协变和逆变的理解(Updated)
去年写过一篇博客谈了下我自己对协变和逆变的理解,现在回头看发现当时还是太过“肤浅”,根本没理解.不久前还写过一篇“黑”Java泛型的博客,猛一回头又是“肤浅”,今天学习Java泛型的时候又看到了协变和 ...
- 深入理解 C# 协变和逆变
msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型. “逆变”则是指能够使用派生程度更小的类型. 解释的很正确,大致就是这样,不过不够直白. 直白的理解: “协变” ...
- 【转】深入理解 C# 协变和逆变
http://www.cnblogs.com/qixuejia/p/4383068.html 深入理解 C# 协变和逆变 msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程 ...
- 深入理解 C# 协变和逆变 (转载)
深入理解 C# 协变和逆变 msdn 解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型. “逆变”则是指能够使用派生程度更小的类型. 解释的很正确,大致就是这样,不过不 ...
- Scala教程之:深入理解协变和逆变
文章目录 函数的参数和返回值 可变类型的变异 在之前的文章中我们简单的介绍过scala中的协变和逆变,我们使用+ 来表示协变类型:使用-表示逆变类型:非转化类型不需要添加标记. 假如我们定义一个cla ...
- C# 泛型的协变和逆变
1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如 ...
- 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事
阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...
- c#的协变和逆变
关于协变和逆变要从面向对象继承说起.继承关系是指子类和父类之间的关系:子类从父类继承,所以子类的实例也就是父类的实例.比如说Animal是父类,Dog是从Animal继承的子类:如果一个对象的类型是D ...
- 那些年搞不懂的"协变"和"逆变"
博主之前也不是很清楚协变与逆变,今天在书上看到了有关于协变还是逆变的介绍感觉还是不太懂,后来看了一篇园子里面一位朋友的文章,顿时茅塞顿开.本文里面会有自己的一些见解也会引用博友的一些正文,希望通过本篇 ...
随机推荐
- Minimum Cost(最小费用最大流,好题)
Minimum Cost http://poj.org/problem?id=2516 Time Limit: 4000MS Memory Limit: 65536K Total Submissi ...
- Swift4 - 动态计算UITableView中tableHeaderView的高度 - 获取子控件高度和宽度
核心 : /// 获取 子控件高度 func sizeHeaderToFit(view:UIView) { view.setNeedsLayout() view.layoutIfNeeded() le ...
- 零基础学习hadoop到上手工作线路指导(编程篇)
问题导读: 1.hadoop编程需要哪些基础? 2.hadoop编程需要注意哪些问题? 3.如何创建mapreduce程序及其包含几部分? 4.如何远程连接eclipse,可能会遇到什么问题? 5.如 ...
- discuz回贴通知插件实现-用户状态设置
1.获取用户提交数据 discuz通过$_GET来获取全部数据,包括($_GET,$_POST). else if($_GET['pluginop'] == 'set') { //获取用户提交数据 $ ...
- php下ajax的文件切割上传
html5中的File对象继承Blob二进制对象,Blob提供了一个slice函数,可以用来切割文件数据. <!DOCTYPE HTML> <html lang="zh-C ...
- VMware安装centos虚拟机 通过NAT与主机互通并能上网
1.关于centos虚拟机的安装,我这里就不详细说明了,网上有很多教程,默认你们已经安装好. (我的环境是centos6.6 x86 最小安装版) 2.右键虚拟主机,选择设置选项. 3.在 ...
- kafka 报Failed to load class "org.slf4j.impl.StaticLoggerBinder".[z]
转:http://blog.chinaunix.net/uid-25135004-id-4172954.html 测试kafka producer发送消息 和 consumer 接受消息报错 ...
- Golang之redis
redis是个开源的高性能的key-value的内存数据库,可以把它当成远程的数据结构. 支持的value类型非常多,比如string.list(链表).set(集合). hash表等等 redis性 ...
- This page contains the following error
解决办法:将header头注释掉 header("content-type:text/xml; charset=UTF-8");
- jetty无法即时更新html、js、css等静态文件的解决办法
网上搜了一大堆方法.最常见的是找到jetty\etc\webdefault.xml文件,找到useFileMappedBuffer参数,把true改为false <init-param> ...