详解C#泛型(三)
一、前面两篇文章分别介绍了定义泛型类型、泛型委托、泛型接口以及声明泛型方法:
首先回顾下如何构建泛型类:
public class MyClass<T>
{
public void MyFunc()
{
//…
}
}
其中,尖括号<>中的T代表的是该泛型类的类型参数,在使用时可以指定其类型,例如,指定类型参数为整数类型,创建封闭式构造类MyClass<int>:
MyClass<int> myObj = new MyClass<int>();
二、这一篇我们了解一下泛型的作用机制,泛型是运行时起作用的一套机制,根据运行时类型参数被指定为值类型还是引用类型其使用方式有所不同:
1.当类型参数被指定为值类型时,会在第一次指定该特定值类型的类型时创建该类型唯一的专用化泛型类型,泛型类型中的类型参数会被替换为相应的值类型;
※此时,运行时会创建不同封闭式构造类型的类型信息对象,它们的类型句柄指向不同的类型信息对象,不同封闭式构造类型的方法句柄也指向不同的方法信息对象;
2.当类型参数被指定为引用类型时,会在第一次指定任意引用类型时创建一个通用化泛型类型,泛型类型中的类型参数会被替换为该引用类型,并在之后每次指定为引用类型时重用该泛型类型并修改其中类型参数的类型;造成这种差异的原因可能在于所有的引用大小相同;
※此时,运行时依然会创建不同封闭式构造类型的类型信息对象,它们的类型句柄也指向不同的类型信息对象,但是它们共用一套方法句柄,即不同封闭式构造类型的方法句柄指向同一个方法信息对象;
3.对于给定的泛型类:
public class MyClass<T>
{
public void MyFunc()
{
//…
}
}
获取不同封闭式构造类型的类型句柄和方法句柄:
Type type1 = typeof(MyClass<int>);
Type type2 = typeof(MyClass<long>);
Type type3 = typeof(MyClass<string>);
Type type4 = typeof(MyClass<Array>);
//以下类型句柄各不相同
Console.WriteLine(type1.TypeHandle.Value);
Console.WriteLine(type2.TypeHandle.Value);
Console.WriteLine(type3.TypeHandle.Value);
Console.WriteLine(type4.TypeHandle.Value);
//最后两个方法句柄相同,其它方法句柄各不相同
Console.WriteLine(type1.GetMethod("MyFunc").MethodHandle.Value);
Console.WriteLine(type2.GetMethod("MyFunc").MethodHandle.Value);
Console.WriteLine(type3.GetMethod("MyFunc").MethodHandle.Value);
Console.WriteLine(type4.GetMethod("MyFunc").MethodHandle.Value);
※在访问任何泛型类型之前,CLR会先创建MyClass<>的类型信息对象;
打印结果:

可以发现,最后两个泛型类型的MyFunc方法的方法句柄指向相同;但是不同类型参数的情况下,还是会创建对应的泛型类型对象,这使得泛型单例成为可能:
三、对于封闭式构造类型,只要其类型参数不完全相同,CLR就会在初次访问该类型之前创建该类型的类型信息对象并调用其对应唯一的静态构造函数,例如对于有静态构造函数的泛型类MyClass<T>,在初次访问MyClass<int>、MyClass<string>等封闭式构造类之前都会调用一次其对应唯一的静态构造函数,这也是创建泛型单例的基础:
public class MyClass<T>
{
static MyClass()
{
Console.WriteLine(typeof(T).FullName);
}
}
MyClass<int> obj1 = new MyClass<int>();
MyClass<long> obj2 = new MyClass<long>();
MyClass<Array> obj4 = new MyClass<Array>();
打印结果:

四、运行时动态构建泛型:
Type myType = typeof(MyClass<>); //获取未指定任何类型参数的开放式构造类的类型信息,多个类型参数时添加,:typeof(MyClass<,>)
myType = myType.MakeGenericType(typeof(int)); //通过类型信息的实例方法MakeGenericType()构建指定所有类型参数的封闭式构造类的类型信息,如未指定所有类型参数会抛出异常ArgumentException
//也可以直接获取封闭式构造类的类型信息,当类型参数在一开始就确定时推荐使用此种方式
//myType = typeof(MyClass<int>); //多个类型参数时需要同时指定:typeof(MyClass<int, string>)
注意:通过反射只可以获取未指定任何类型参数的开放式构造类MyClass<,>的类型信息和指定所有类型参数的封闭式构造类MyClass<int, string>的类型信息,即无法获取MyClass<int, >的类型信息;
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
详解C#泛型(三)的更多相关文章
- 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码
详解C#泛型(二) 一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...
- JDBC详解系列(三)之建立连接(DriverManager.getConnection)
在JDBC详解系列(一)之流程中,我将数据库的连接分解成了六个步骤. JDBC流程: 第一步:加载Driver类,注册数据库驱动: 第二步:通过DriverManager,使用url,用户名和密码 ...
- 详解TCP的三次握手四次断开
本文将分别讲解经典的TCP协议建立连接(所谓的“3次握手”)和断开连接(所谓的“4次挥手”)的过程. 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提 ...
- 第6章 传输层(详解TCP的三次握手与四次挥手)
第6章 传输层 传输层简介 传输层为网络应用程序提供了一个接口,并且能够对网络传输提供了可选的错误检测.流量控制和验证功能.TCP/IP传输层包含很多有用的协议,能够提供数据在网络传输所需的必要寻址信 ...
- CocoaPods详解之(三)----制作篇
CocoaPods详解之----制作篇 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/20067595 转载请注明出处 ...
- [转]iOS学习之UINavigationController详解与使用(三)ToolBar
转载地址:http://blog.csdn.net/totogo2010/article/details/7682641 iOS学习之UINavigationController详解与使用(二)页面切 ...
- iOS学习之UINavigationController详解与使用(三)ToolBar
1.显示Toolbar 在RootViewController.m的- (void)viewDidLoad方法中添加代码,这样Toobar就显示出来了. [cpp] view plaincopy [ ...
- 详解C#泛型(二)
一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { Type generi ...
- Linux常用命令详解(第三章)(ping、kill、seq、du、df、free、date、tar)
本章命令(共7个): 1 2 3 4 5 6 7 8 ping kill seq du df free date tar 1." ping " 作用:向网络主机发送ICMP(检测主 ...
随机推荐
- issubclass ,isinstance,反射
issubclass() 函数 issubclass() 方法用于判断参数 class 是否是类型参数 classinfo 的子类. 语法 以下是 issubclass() 方法的语法: issubc ...
- Different timers in .net
Multi-threads timers: System.Threading.Timer and System.Timers.Timer (.net framework): App will hand ...
- java类与继承(转载)
关于java的类与继承面链接是一个网友总结的,还有列子我个人觉得很详细 固拿来收藏,需要的朋友可从这里访问: http://www.cnblogs.com/dolphin0520/p/3803432. ...
- Visual Studio 简单使用常识操作
Visual Studio 简单使用个人总结 转载请注明来源:www.cnblogs.com/icmzn(后续会持续更新) 可以查看一下链接,官方关于visual studio 2010 的介绍 ...
- 4、C语言的编译过程链
在学校学C语言的时候,很多人都不是很注重编译过程链,但是其实编译过程是项目过程中很重要的一部分,有时候有些语法诸如static.volatile等关键词不理解时大多数都是对整个C语言编译链没有进行过详 ...
- 《mysql必知必会》学习_第七章_20180730_欢
第七章:数据过滤 P43 select prod_id,prod_price,prod_name from products where vend_id =1003 and prod_price &l ...
- Opencv打开摄像头,读不到图像,一般来说先读取第一帧,舍弃,然后就正常了
舍弃第一帧的程序: cap >> img; cv::waitKey(100); if (cvWaitKey(5) == 27) break; cap >> img;
- TVS二极管
TVS管命名规则: TVS管的型号由三部分组成:系列名+电压值+单/双向符号 系列名代表不同的峰值脉冲功率和封装形式 ① SMAJ.SMBJ.SMCJ.SMDJ表示贴片封装:分别代表的峰值脉冲 ...
- Android GridView 滑动条设置一直显示状态
模拟GridView控件: <GridView android:id="@+id/picture_grid" android:layout_width="match ...
- C#基础——C#中问号的使用
1. 可空类型修饰符(?): 引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空. 例如:string str=null; 是正确的,int i=null; 编译器就会报错. 为了使值 ...