详解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(检测主 ...
随机推荐
- 《深入浅出MFC》系列之动态创建
/*************************************************************************************************** ...
- linux初学terminal命令(1)ls、cd、su、man、pwd、useradd、passwd、cat、Ctrl+C、Ctrl+Z、Ctrl+L
terminal命令(terminal终端对应windows 按下win(linux下叫Super键)+r,输入cmd(command,命令),召唤出来的Dos控制台) 1. ls(英文list):简 ...
- POJ3181--Dollar Dayz(动态规划)
Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. ...
- java基础-day19
第08天 异常 今日内容介绍 u 异常体系&异常处理 u Throwable常用方法&自定义异常 u 递归 第1章 异常产生&异常处理 1.1 异常概述 什 ...
- hdu 5087 次长升序串的长度
http://acm.hdu.edu.cn/showproblem.php?pid=5087 求数列次长升序串的长度 还是dp求最长升序串的长度,再开一个数组记录对于dp值的串的个数 最后看一下最长字 ...
- [mysql] mysql 查询语句收集
// 1. 筛选出当天的中奖名单 $where = " date_format(f_ctime,'%Y-%m-%d') = current_date()"; ...
- Android-Kotlin-印章类
上一篇博客介绍了,Android-Kotlin-枚举enum: 由于枚举 和 印章类 有相似之处,所以两者对比一下: Kotlin的枚举,重点区分的数据本身 Kotlin的印章类,重点区分的是数据类型 ...
- Mac怎么安装并配置Homebrew?
1.在打开的命令行工具中输入如下语句: 复制内容到剪贴板 ruby -e "$(curl --insecure -fsSL https://raw.githubusercontent.com ...
- Python:windows下scikit-learn 安装和更新
scikit-learn 报错: from sklearn.model_selection import train_test_split ImportError: No module named m ...
- 背水一战 Windows 10 (44) - 控件(日期类): CalendarView, DatePicker, TimePicker
[源码下载] 背水一战 Windows 10 (44) - 控件(日期类): CalendarView, DatePicker, TimePicker 作者:webabcd 介绍背水一战 Window ...