第十二章 泛型

目录:

12.1 FCL中的泛型

12.2 泛型基础结构

12.3 泛型接口

12.4 泛型委托

12.5 委托和接口的逆变和协变泛型类型实参

12.6 泛型方法

12.7 泛型和其他成员

12.8 可验证性和约束

 

泛型时CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用,即“算法重用”。

大多数算法允许都封装在一个类型中,CLR允许创建泛型引用类型和泛型值类型,但不允许创建泛型枚举类型。CLR还允许创建泛型接口和泛型委托。方法偶尔也封装有用的算法,所以CLR允许在引用类型,值类型或接口中定义泛型方法。

定义泛型类型或方法时,为类型指定的任何变量(比如T)都称为类型参数。T是变量名,源代码能使用数据类型的任何地方都能使用T。

使用泛型类型或方法时指定的具体数据类型称为类型实参。

优势:

源代码保护:使用泛型算法的开发人员不需要访问算法的源代码。

类型安全:将泛型算法应用于一个具体的类型时,编译器和CLR能理解开发人员的意图,并保证只有与指定数据类型兼容的对象才能用于算法。

更清晰的代码:由于编译器强制类型安全性,所以减少了源代码中必须进行的强制类型转换次数,使代码易于编写和维护。

更佳的性能:CLR不需要执行装箱操作,值类型的实例以传值的方式传递。

12.1 FCL中的泛型

泛型最明显的应用就是集合类。集合类型实现了许多接口,放入集合中的对象可实现接口来执行排序和搜索等操作。

12.2 泛型基础结构

CLR添加泛型需要的工作:

创建新的IL指令,使之能够识别类型参数。

修改现有元数据表的格式,以便表示具有泛型参数的类型名称和方法。

修改各种编程语言(C#,Microsoft Visual Basic .NET等)来支持新语法,允许开发人员定义和引用泛型类型和方法。

修改编译器,使之能生成新的IL指令和修改的元数据格式。

修改JIT编译器,以便处理新的支持类型实参的IL指令来生成正确的本机代码。

创建新的反射成员,使开发人员能查询类型和成员,以判断它们是否具有泛型参数。另外,还必须定义新的反射成员,使开发人员能在运行时创建泛型类型和方法定义。

修改调试器以显示和操纵泛型类型,成员,字段以及局部变量。

修改VS的“智能感知”功能。将泛型类型或方法应用于特性数据类型时能显示成员的原型。

12.2.1 开放类型和封闭类型

具有泛型类型参数的类型成为开发类型,CLR禁止构造开放类型的任何实例。这类似于CLR禁止构造接口类型的实例。

代码引用泛型类型时可指定一组泛型类型实参。为所有类型参数都传递了实际的数据类型,类型就成为封闭类型。CLR允许构造封闭类型的实例。然而,代码引用泛型类型的时候,可能留下一些泛型类型实参未指定。这会在CLR中创建新的开发类型对象,而且不能创建该类型的实例。

12.2.2 泛型类型和继承

泛型类型仍然时类型,所以能从其他任何类型派生。使用泛型类型并指定类型实参时,实际是在CLR中定义一个新的类型对象,新的类型对象从泛型类型派生自的那个类型派生。

12.2.3 泛型类型同一性

12.2.4 代码爆炸

使用泛型类型参数的方法在进行JIT编译时,CLR获取方法的IL,用指定的类型实参替换,然后创建恰当的本机代码(这些代码未操作指定数据类型“量身定制”)。缺点:CLR要为每种不同的方法/类型组合生成本机代码。这个现象时“代码爆炸”。

CLR内建了一些优化措施能缓解代码爆炸。假如为特定的类型实参调用了一个方法,以后再用相同的类型实参调用这个方法,CLR只会为这个方法/类型组合编译一次代码。

CLR还有另一优化,它认为所有引用类型实参都完全相同,所以代码能够共享。(所有引用类型的参数或变量实际上只是指向堆上对象的指针,所有对象指针都以相同方式操纵)

如果某个类型时值类型,CLR就必须专门为那个值类型生成本机代码。因为值类型的大小不定,CLR无法共享代码,因为可能要用不同的本机CPU指令来操纵这些值。

12.3 泛型接口

使用非泛型接口来操纵值类型会发生装箱,而且会失去编译时的类型安全性。更到内容请参考在13章接口

12.4 泛型委托

CLR支持泛型委托,目的是保证任何类型的对象都能以类型安全的方式传给回调函数。泛型委托允许值类型实例在传给给回调方法时不进行任何装箱。 

12.5 委托和接口的逆变和协变泛型类型实参

委托的每个泛型类型参数都可标记为协变量或逆变量。这样就可将泛型委托类型的变量转换为相同的委托类型(但泛型参数类型不同)。泛型类型参数:

不变量:意味着泛型类型参数不能更改。

逆变量:意味着泛型类型参数可以从一个类更改为它的某个派生类。在C#中用in关键字标记逆变量形式的泛型类型参数。逆变量泛型类型参数只出现在输入位置,比如作为方法的参数。

协变量:意味着泛型类型参数可以从一个类更改为它的某个基类。C#是用out关键字标记协变量形式的泛型类型参数。协变量泛型类型参数只能出现在输出位置,比如作为方法的返回类型。

(协变性指定返回类型的兼容性,逆变性指定参数的兼容性)

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

12.6 泛型方法

定义泛型类,结构或方法时,类型中定义的任何地方都可引用类型指定的类型参数。类型参数可作为方法参数,方法返回值或方法内部定义的局部变量的类型使用。CLR也允许方法指定它自己的类型参数。这些参数也可以作为参数,返回值,或局部变量使用。

泛型方法和类型推断

C#编译器支持在调用泛型方法时进行类型推断。这意味着编译器会在调用泛型方法时自动推断要使用的类型。

12.7 泛型和其他成员

在C#中,属性,索引器,事件,操作符方法,构造器和终结器本身不能有类型参数。但它们能在泛型类型中定义,而且这些成员中的代码能使用类型的类型参数。

12.8 可验证性和约束

 约束机制:约束的作用是限制能指定成泛型实参的类型数量。通过限制类型的数量,可以对那些类型执行更多操作。

约束可应用于泛型类型的类型参数,也可用于泛型方法的类型参数。CLR不允许基于类型参数名称或约束来进行重载;只能基于元数(类型参数个数)对类型或方法进行重载。

重写虚泛型方法时,重写的方法必须指定相同数量的类型参数,而且这些类型参数继承在基类方法上指定的约束。事实上,根本不允许为重写方法的类型参数指定任何约束。但类型参数的名称是可以改变的。类似地,实现接口方法时,方法必须指定与接口方法等量地类型参数,这些类型参数将继承由接口方法指定地约束。

12.8.1 主要约束

类型参数可以指定零个或者一个主要约束。主要约束可以代表非密封类地一个引用类型。

指定引用类型约束时,相当于向编译器承诺:一个指定的类型实参要么时与约束类型相同的类型,要么时从约束类型派生的类型。

12.8.2 次要约束

类型参数可以指定零个或者多个次要约束,次要约束代表接口类型。这种约束向编译器承诺类型实参实现了接口。由于能指定多个接口约束,所以类型实参必须实现了所有接口约束。

还有一种次要约束称为类型参数约束,有时也称为裸类型约束。这种约束用得比接口约束少得多。它允许一个泛型类型或方法规定:指定的类型实参要么就是约束的类型,要么时约束的类型的派生类。一个类型参数可以指定零个或者多个类型参数约束。

12.8.3 构造器约束

类型参数可指定零个或一个构造器约束,它向编译器承诺类型实参是是实现了公共无参构造器的非抽象类型。where T : new()

12.8.4 其他可验证性问题

1.泛型类型变量的转型

将泛型类型的变量转型为其他类型是非法的,除非转型为与约束兼容的类型。

2.将泛型类型变量设为默认值

将泛型类型变量设为null是非法的,除非将泛型类型约束成引用类型。

3.将泛型类型变量与null进行比较

无论泛型类型是否被约束,使用==或!=操作符将泛型类型变量与null进行比较都是合法的。

4.两个泛型类型变量相互比较

如果泛型类型参数不能肯定是引用类型,对同一个泛型类型的两个变量进行比较是非法的。

5.泛型类型变量作为操作数使用

将操作符应用于泛型类型操作数会出现大量问题。不能将操作符应用于泛型类型的变量。

 

转载于:https://www.cnblogs.com/terry-1/p/10028569.html

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

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

相关文章

memset()函数详解

1、头文件C中为<memory.h> 或 <string.h>C中为<cstring> 2、原型及作用 void *memset(void *s,int c,size_t n)其中&#xff0c;s是一个指针或数组&#xff0c;c是赋给s的值&#xff0c;n是将修改的s的长度&#xff0c;即s的前n个字节。作用&#xff1a;将已…

jq 如何让点击其他地方隐藏_详解jQuery除指定区域外点击任何地方隐藏DIV功能

本文主要介绍了jQuery除指定区域外点击任何地方隐藏p的相关资料,代码简单易懂&#xff0c;非常不错&#xff0c;具有参考借鉴价值&#xff0c;需要的朋友可以参考下&#xff0c;希望能帮助到大家。具体代码如下所示&#xff1a;$(body).click(function(e) {var target $(e.tar…

linux系统编程之进程(八):守护进程详解及创建,daemon()使用

一&#xff0c;守护进程概述 Linux Daemon&#xff08;守护进程&#xff09;是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务&#xff0c;不是对整个系统就是对某个用户程序提供服务…

您还在调试吗?

调试是“以交互方式运行程序/方法&#xff0c;在每个语句后中断执行流程并显示……的过程。”简而言之&#xff0c;它是一种非常有用的技术……对于一个糟糕的程序员而言。 或仍然在用C编写过程代码的老程序员。面向对象的程序员从不调试其代码-他们编写单元测试。 我的意思是&…

RESTful规范

本文目录 什么是RESTful RESTful API设计 基于Django实现 什么是RESTful REST与技术无关&#xff0c;代表的是一种软件架构风格&#xff0c;REST是Representational State Transfer的简称&#xff0c;中文翻译为“表征状态转移”REST从资源的角度类审视整个网络&#xff0c;它…

反向输出dna序列_蛋白质序列反向(逆向)翻译成DNA序列-在线工具

请粘贴蛋白质序列&#xff0c;如果需要输入多个序列&#xff0c;请以fasta格式输入&#xff0c;输入总长度不超过2万个字符。>testACDEFGHIKLMNPQRSTVWY*推荐使用IE 8.0以上、chrome或者Firefox等浏览器。请输入该蛋白来源物种的密码子使用表(GCG格式)&#xff0c;下表示大肠…

子函数的指针释放问题

C语言中遇到一个这样的问题&#xff1a;子函数中malloc了一个指针存储数据&#xff0c;作为该子函数的返回值&#xff0c;return到主函数。那么这个指针应该在哪里释放呢&#xff1f;显然不能在子函数里释放&#xff0c;否则返回值没有意义。这样就应该在主函数里释放&#xff…

利用cookie模拟登陆知乎

我们知道一些网站是需要账号密码才可以登陆的&#xff0c;例如知乎。而利用requests库里的get方法的headers参数可以达到这个目的 首先在知乎的网页上登陆自己的知乎账号&#xff0c;利用chrome的开发者工具&#xff08;F12&#xff09;可以捕获我们的get方法向浏览器提供的coo…

linux中fork()函数详解(原创!!实例讲解)

一、fork入门知识 一个进程&#xff0c;包括代码、数据和分配给进程的资源。fork&#xff08;&#xff09;函数通过系统调用创建一个与原来进程几乎完全相同的进程&#xff0c;也就是两个进程可以做完全相同的事&#xff0c;但如果初始参数或者传入的变量不同&#xff0c;两个进…

C++ vector的释放

项目上用到vector容器&#xff0c;没有手动释放&#xff0c;总是会在这里出现内存分配不成功的问题&#xff0c;因此对vector的释放了解了一下。初始代码如下&#xff1a; vector <float*> dets(nTotalLayers); //dets : 记录每层图像的 Hessian 行列式&#xff1b; for …

设计模式 工厂方法_使用工厂方法模式设计最佳实践

设计模式 工厂方法在前面的“设计模式”示例中&#xff0c;我们解释了当今常用的“工厂”模式。 在本节中&#xff0c;我们将了解具有更多抽象的更高级的解决方案。 该模式称为工厂方法设计模式。 定义&#xff1a; Factory方法模式提供了一种用于创建对象的方法&#xff0c;…

C Programming Language

代做module作业、代做C/C编程设计作业、代写Programming Language作业、代做C/C课程设计作业C Programming LanguageContribution to module (weighting: 20 %)1st Semester 2018-2019Out: WED. 5th Dec. 2018 Due: 18:00[GMT], WED. 19th Dec. 2018Main objective of the assi…

python修改列表中字典内的值_python修改字典内key对应值的方法

python学习笔记&#xff1a;字典python版本&#xff1a;Python 2.6.6系统环境&#xff1a;CentOS release 6.2 x86_64本文参考了互联网上前辈的一些文章一、字典是python中最灵活的内置数据结构类型&#xff0c;如果把列表看作是有序的对象集合&#xff0c;那么字典就是无序的集…

MATLAB使用技巧

1、ctrl c 或者 ctrl break 强行中断程序运行 2、变量X 右键save as为DX后&#xff0c;再次使用时load有区别&#xff1a;load(DX.mat) 得到的是X这个变量&#xff0c;直接出现在workspace里&#xff1b;Xnew load(DX.mat)得到的是一个名称为Xnew的结构体&#xff0c;里面包含…

Beta 冲刺 (2/7)

团队信息 队名&#xff1a;爸爸饿了组长博客&#xff1a;here作业博客&#xff1a;here组员情况 组员1&#xff08;组长&#xff09;&#xff1a;王彬 过去两天完成了哪些任务 完成考试确定历史记录页面与排行榜页面的前端页面风格接下来的计划 & 还剩下哪些任务 各个食堂平…

您真的需要instanceof吗?

使用instanceof是一种代码味道。 我认为我们可能对此表示同意。 每当我看到这样的构造时&#xff0c;我肯定会出现问题。 也许有人只是在进行更改时没有注意到问题&#xff1f; 也许有一个主意&#xff0c;但是它太复杂了&#xff0c;以至于需要太多的精力或时间才能让开发人员…

python 累积正态分布函数_Python编程基础—Python语句书写规范

Python语句中没有专门的“结束符”。Python解释器不是根据"结束符"来判断语句是否结束,而是根据语法的完整性来判断。一、Python语句编写规则①通常是一行一句x1 1 x2 2 x3 3 print(x1,x2,x3)②也可以一行多句&#xff0c;用语句分隔符“;”对两个语句进行标识x1 …

MATLAB批量改变图片大小

%2018年6月28日11:07:15 %把一个目录下的图片缩放到指定大小 clc clear ratio 0.2;%缩放比例 cd(F:\数据集\crumpled clothes\cloth3\original image\);%不加这句话找不到图片 dpath F:\数据集\crumpled clothes\cloth3\original image\*.JPG;%找到路径下所有格式为.JPG的文件…

基于Libevent的HTTP Server

简单的Http Server 使用Libevent内置的http相关接口&#xff0c;可以很容易的构建一个Http Server&#xff0c;一个简单的Http Server如下&#xff1a; #include <event2/event.h> #include <event2/buffer.h> #include <event2/http.h> #include <Winso…

python写入数据的一种措施_Python 文件数据读写的具体实现

文件数据读写读写文件&#xff0c;本质上是请求操作系统打开一个文件对象&#xff0c;然后&#xff0c;通过操作系统提供的接口从这个文件对象中读取数据(读文件)&#xff0c;或者把数据写入这个文件对象(写文件)。文件读取使用 Python 内置 open() 函数&#xff0c;以 rt 的模…