.NET可变性解析(协变和逆变)
2021-03-28 04:27
标签:复用 write strong ping 技术 block ken 推荐 是什么 可变性是.NET4.0中的一个新特性,可变性可分为 : 协变性、逆变性、不可变性. 那么在.NET4.0之前是否有可变性? 答案是肯定的,我们可以通过下面的几个实例来简单的了解一下.NET4.0之前的协变和逆变. 实例 1 : 方法参数的协变 实例 2 : 数组协变以及执行时类型检查 在上述代码中会抛出异常 "system.ArrayTypeMismatchException",因为从course转换为project会返回原始引用,所以course和project都是引用的同一个数组,对于数组而言,它是一个course数组,所以会拒绝存储对于非course类型的引用。数组的协变会导致类型安全性在执行时才能体现,而不能在编译时体现 可变性种类分类定义: 协变 : 说明泛型类型参数可以从一个派生类更改为它的基类,在C#中,是用out关键字标记协变量形式的泛型类型参数,协变量泛型类型参数只能出现在输出位置,比如作为方法的返回类型 逆变 : 说明泛型类型参数可以从一个基类更改为它的派生类,在C#中,是用in关键字标记逆变形式的泛型类型参数,逆变量泛型类型参数只能出现在输入位置,比如作为方法的参数。 不可变 :按引用类型传递变量,可以看成是ref参数,表示传入的类型必须与参数本身的类型完全一致,传入方法内部的值,将同样以相同的类型输出。 可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用,在我们面向对象编程中,继承这一特性就很好的体现了对象的可变性. 任何使用了协变和逆变的转换都是引用转换,这意味着转换之后将返回相同的引用,它不会创建新的对象,只是认为现有引用与目标类型匹配,这与某个层次中,引用类型之间的转换是相同的。 在.NET4.0之前,泛型是不能够进行协变和逆变的,也就是说泛型的协变和逆变是C#4.0的一个新特性,泛型的协变和逆变也是为了保持类型的绝对安全性. 在泛型接口或者委托的声明中,.NET4.0能够使用out修饰符来指定类型参数的协变性,使用in修饰符来指定逆变性,声明完成之后,就可以对相关的类型进行隐式转换了,在接口和委托中,它们的工作方式是完全相同的. 我们使用的两个接口 : IEnumberable 更多的泛型协变接口 : IEnumerable 泛型逆变接口 : IComparer 下面我们通过实例来演示一下接口的泛型可变性 实例 3 : 查看泛型接口集合IEnumberable 在上述代码中,我们定义了两个类,分别为 : project和course,其中course继承自project,在project中有一个方法GetCourseByProject,这个方法有一个形参类型为 IEnumberable 实例 4 : 定义泛型接口查看逆变 在上述代码中,我们定义一个泛型接口 IBase IBase 在我们看了,泛型接口的协变和逆变之后,对于泛型委托的可变性其实性质是一样的.我们可以通过下面两个实例来演示一下 : 实例 5 : 委托协变 在上述的代码中,我们首先定义了一个委托类型,getproject, 在GetProject projects = GetCourse,GetCourse是一个返回值为Course对象的一个函数, 此处发生了协变,Course类型转换为了Project类型,子类转换为父类. 实例 6 : 泛型委托协变 在上述的代码中,我们定义了一个泛型委托,Find 实例 7 : 委托中的逆变 在上述的代码中,首先我们声明了一个带参数的委托FindCourse,参数类型为 Course , 然后注意在第六行代码中, 我们将 GetProject这个方法赋值给了 委托FindCourse,同时,GetProject这个方法的参数类型为 Project,Project为Course 的基类,所以在第六行代码中它发生了逆变. 实例 8 : 泛型委托中的逆变 相信通过了前面的几个实例,这个例子也就不难看懂了,在上述的代码中,我们首先声明了一个泛型委托,并且泛型中有一个in说明是可以进行逆变,然后在第二行代码中,我们还是通过lambda表达式,创建一个参数类型为Project的函数,注意第三行代码, 第三行代码中将GetProject方法赋值给了getCourse,此处发生了逆变. 【四】.NET中可变性的好处 1 、更好的代码复用性. 通过刚才的几个实例,我们可以知道,如果在Project下还有Excerise,Test等派生类的话, 利用协变和逆变性,我们就可以直接 Project.GetCourseByProjects(ExceriseList); (协变了) . IBase 2、更好的保持了泛型的类型安全性 首先,协变和逆变是通过out,in来指定的,编译器是不知道那种形式是协变那种形式是逆变的,通过out(输出参数)和in(输入参数),来指定参数的输入输出类型这一形式,很好的保持了泛型的类型安全性. PS : ref 也是一种,用来指定不变性,指定要求传入什么类型的就是什么类型,在一般我们开发过程中,通过都是通过这样的形式来传参的,比如: 实例 9 : ref双向传值,要求实参类型必须与形参类型完全一致 调用方法所传入的类型必须要与方法要求的参数类型完全一致 平日里我们觉得一些比较难的技术点,当我们花费一些时间去学习,去总结,去思考一下.会发现其实并不是我们想象中那么难, 难得是我们下定决心去做的那份意念而已. 通过本文我们了解到了协变性、逆变性、不变性的定义,以及它是通过一种什么样的形式来实现的, 另外通过实例我们也可以想到如果用好了它,也会给我的开发带来事半功倍的效果。使我们的代码更加优雅、提高程序可扩展性以及复用性,同时这不也是一种多态的体现吗? 通过协变和逆变也有一些限制,这可能也是因为设计者出于类型安全性的方面考虑,它是不支持类的类型参数的可变性,只有接口和委托可以拥有可变的类型参数. 可变性只支持引用转换. 如果你觉得本文对你有帮助的话,请点右下角的推荐,或者直接关注我,后续将不断更新.NET解析这一系列的文章.... .NET可变性解析(协变和逆变) 标签:复用 write strong ping 技术 block ken 推荐 是什么 原文地址:https://www.cnblogs.com/si-yuan/p/12623206.html【一】何为可变性
static void Main(string[] args)
{
GetProject(new Course()); // Course 继承自 Project 此处进行了协变
}
static void GetProject(Project course)
{
Console.WriteLine(course.Name);
}
Course[] course = new Course[4];
Project[] project = course;
project[0] = new Excercise();
二】泛型接口可变性
class Project
{
public static void GetCourseByProjects(IEnumerable
static void Main(string[] args)
{
IBase
【三】泛型委托可变性
public delegate Project GetProject();
static Course GetCourse()
{
return new Course();
}
GetProject projects = GetCourse;
public delegate T Find
public delegate void FindCourse(Course course);
static void GetProject(Project pro)
{
Console.WriteLine(pro.Name);
}
FindCourse getCourse = GetProject;
public delegate void Find
Project p = new Project();
GetProject( ref p);
public static void GetProject(ref Project project)
{
Console.WriteLine(project.Name);
}
【五】总结
上一篇:JSP面试题