- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
简单整理一下逆变和协变.
什么是逆变和协变呢?
首先逆变和协变都是术语.
协变表示能够使用比原始指定的派生类型的派生程度更大的类型.
逆变表示能够使用比原始指定的派生类型的派生程度更小的类型.
这里student 继承 person.
这里这个报错合情合理.
这里可能有些刚入门的人认为,person 不是 student 的父类啊,为什么不可以呢?
一个列表student 同样也是一个列表的 person啊.
这可能是初学者的一个疑问.
但是实际情况是list 是一个类型, list 是一个类型.
所以他们无法隐式转换是正常的.
但是这样写就可以
static void Main(string[] args)
{
IEnumerable<Student> students = new List<Student>();
IEnumerable<Person> peoples = students;
}
这样写没有报错,理论上IEnumerable 是一种类型,IEnumerable 是一种类型,不应该能隐私转换啊.
为什么呢?因为支持协变.
协变表示能够使用比原始指定的派生类型的派生程度更大的类型.
他们的结构如上。因为student是person的派生类,IEnumerable 的派生程度比IEnumerable 大.
协变怎么声明呢
public interface IEnumerable<out T> : IEnumerable
{
//
// 摘要:
// Returns an enumerator that iterates through the collection.
//
// 返回结果:
// An enumerator that can be used to iterate through the collection.
new IEnumerator<T> GetEnumerator();
}
这里协变有个特点,那就是协变参数T,只能用于返回类型.
原因是在运行时候还是new List (),返回自然是Student,那么student 可以赋值给person,这没问题.
那么协变参数T,不能用于参数呢? 是这样的.
比如 IEnumerable 里面有一个方法是
public void test(T a)
{
}
在IEnumerable 中原本要传入一个Student,现在使用了IEnumerable ,那么就可以传入person.
person 要转换成student,显然是不符合的.
那么协变是这样的,那么逆变呢?
public interface ITest<in T>
{
public void Run(T obj);
}
public class Test<T> : ITest<T>
{
public void Run(T obj)
{
throw new NotImplementedException();
}
}
然后这样使用
static void Main(string[] args)
{
ITest<Person> students = new Test<Person>();
ITest<Student> peoples = students;
peoples.Run(new Student());
}
这里的逆变只能作用于参数.
先说一下为什么能够作用于参数,就是在运行的时候本质还是new Test (),要传递的是一个person,如果传递一个student,那么也是可以的.
然后为什么不能作用于返回值呢?
假如ITest 可以这样
public interface ITest<in T>
{
public T Run()
{
}
}
在运行时候是Test (),那么调用run返回的是person,但是赋值给了Student类型,和上面同样的问题哈.
所以协变不能作用于参数,逆变不能作用于返回值.
那么也就是说要摸只能协变,要摸只能逆变.
下面是委托中的逆变
Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());
原理就是Derived继承自Base,原本需要传入base,现在传入Derived,当然也是可以的.
之所以这么设计是一个哲学问题,那就是子类可以赋值给父类,父类能办到的子类也能办到,他们分别对应的是协变和逆变.
下一节委托.
最后此篇关于重学c#系列——逆变和协变[二十四]的文章就讲到这里了,如果你想了解更多关于重学c#系列——逆变和协变[二十四]的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在我的设置中,我试图有一个界面 Table继承自 Map (因为它主要用作 map 的包装器)。两个类继承自 Table - 本地和全局。全局的将有一个可变的映射,而本地的将有一个只有本地条目的映射。
Rust Nomicon 有 an entire section on variance除了关于 Box 的这一小节,我或多或少地理解了这一点和 Vec在 T 上(共同)变体. Box and Vec
我是一名优秀的程序员,十分优秀!