gpt4 book ai didi

重学c#系列——逆变和协变[二十四]

转载 作者:我是一只小鸟 更新时间:2022-11-23 22:31:15 30 4
gpt4 key购买 nike

前言

简单整理一下逆变和协变.

正文

什么是逆变和协变呢?

首先逆变和协变都是术语.

协变表示能够使用比原始指定的派生类型的派生程度更大的类型.

逆变表示能够使用比原始指定的派生类型的派生程度更小的类型.

这里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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com