.NET via C#笔记12——泛型
12 泛型
使用值类型作为参数的泛型容器,传入值类型的参数时,不需要进行装箱
12.1 FCL中的泛型
System.Array
中提供了很多泛型方法
AsReadOnly
BinarySearch
ConvertAll
Exists
Find
FindAll
FindIndex
FindLast
FindLastIndex
ForEach
IndexOf
LastIndexOf
Resize
Sort
TrueForAll
12.2 泛型基础结构
12.2.1 开放类型和封闭类型
- 具有泛型类型参数的类型成为开放类型
- CLR禁止构建开放类型的实例
- 为所有类型参数传递了具体类型,则成为封闭类型
2. 使用不同参数创建的封闭类型,静态成员不同享
12.2.3 泛型类型的同一性
使用using
关键字来创建一个类的别名
using DataList = List<Data>;
// 返回true
typeof(DataList) == typeof(List<Data>);
12.2.4 代码爆炸
- 使用引用类型的泛型类共享一份代码
- 使用值类型的泛型类由编译器生成独一份的代码
12.5 委托和接口的逆变和协变泛型类型实参
泛型委托
泛型委托的泛型类型可以标记为你变量和协变量,默认为不变量
- 不变量(invariant):默认,参数类型不可变
- 逆变量(contravariant):可以变为子类,只能作为输入
- 协变量(covariant):可以变为基类,只能作为输出
delegate TResult Func<in T, out TResult>(T arg);
Func<object, ArgumentException> fn1 = null;
// 可以赋值给另一种使用不同类型参数的泛型委托
Func<string, Exception> fn2 = fn1;
// 这是错的
fn1 = fn2;
这也很好理解,Func<string, Exception>
的参数和输出是兼容Func<object, ArgumentException>
的,例如下面的程序展示的那样:
ArgumentException Func1(object arg) {
...
}
Exception Func2(string arg) {
return Func1(arg);
}
在Func2中调用Func1是OK的,参数可以隐式转化为其基类类型,而反过来则是不对的,不能隐式转为其子类类型。
泛型接口
泛型接口的类型参数也可以声明为逆变量或协变量
interface IWhatTheFuck<in T, out TResult> {
TResult Func(T arg);
}
IWhatTheFuck<object, ArgumentException> if1 = null;
IWhatTheFuck<string, Exception> if2 = null;
if2 = if1; // 通过编译
if1 = if2; // 编译失败
值类型
值类型作为模板参数时不能逆变或协变(涉及装箱和拆箱)。
12.6 泛型方法
如果普通方法和泛型方法同时匹配一个调用,编译器优先适配普通方法
12.7 泛型和其他成员
属性,索引器,事件,操作符方法,构造器,终结器本身不能有类型参数,但可以使用类的泛型参数
12.8 可验证性和约束
这样的函数无法通过编译
static T Min<T>(T o1, T o2) {
if (o1.CompareTo(o2) < 0) return o1;
return o2;
}
通过添加约束
static T Min<T>(T o1, T o2) where T : IComparable<T> {
if (o1.CompareTo(o2) < 0) return o1;
return o2;
}
- 通过泛型约束无法重载
- 通过参数个数可以重载
- 重写虚泛型方法时,所有的约束会继承,不能添加新的约束
主要约束
- 主要约束可以是除了这些类型之外的引用类型:
- Object
- Array
- Delegate
- MultiCastDelegate
- ValueType
- Enum
- Void
- 指定了主要类型之后,类型参数只能是主要类型或者其子类。
- 有两种特殊的主要约束:
class
:包括了class,interface,delegate,arraystruct
:所有的值类型,包括了一个默认的无参数构造函数
次要约束
- 指明需要实现的多个接口
- 指明多个类型参数之间的关系
static List<TBase> ConvertIList<T, TBase>(IList<T> list) where T : TBase {
List<TBase> baseList = new List<TBase>(list.Count);
for (int i = 0; i < list.Count; i++) {
baseList.Add(list[i]);
}
}
构造函数约束
- 约束中可以包含一个构造器约束
- 指定类型可以有一个public,无参数的构造函数
- 主要约束为值类型,无需指定构造函数约束
其他情况
- 隐式转型模板函数中的未定类型变量是非法的,除非在约束中指定过了
- 使用
defaut(T)
来初始化一个未定类型的变量- 为引用类型初始化为
null
- 为值类型初始化内存为0
- 为引用类型初始化为
- 未定类型变量与null进行比较是否相等,编译器不会报错,如果是值类型作为参数,编译器判断为不等
void func<T>(T o) {
if (o == null) {
// 如果T是引用类型,判断成立
// 如果T是值类型,判断不成立
// 但是编译器不会报错
}
}
- 相同类型的泛型类型变量之间不能直接比较是否相等
void func<T>(T a, T b) {
if (a == b) { ... } // 编译不通过
if (EqualityComparer<T>.Default.Equals(a, b)) { ... } // 编译通过
}
- 泛型类型变量无法作为操作数使用
void func<T>(T a, T b) {
var c = a + b; // 编译不通过
}
总结
- C#的泛型比C++模板是更好的存在
- 通过各种约束,可以直接编译泛型函数\类
- 模板需要实例化,导致代码爆炸,各模块\编译单元的实例化模板类还不通用,需要
template class std::vector<int>;
显式实例化template class __declspec(dllexport) std::vector<int>;
实例化导出实例化的模板类extern template class std::vector<int>;
引用别处的实例化模板类
- C#函数是如何调用的?
- 如果像C++那样,直接绑定函数地址,那么对于不同参数的模板类,类型成员函数的参数地址是不同的,如何做到不同参数的模板共享一份代码的呢?
- 如果模板类没有源码,怎样用dll中的开放类(已经编译成IL),生成一个参数为值类型的封闭类呢?
.NET via C#笔记12——泛型的更多相关文章
- Ext.Net学习笔记12:Ext.Net GridPanel Filter用法
Ext.Net学习笔记12:Ext.Net GridPanel Filter用法 Ext.Net GridPanel的用法在上一篇中已经介绍过,这篇笔记讲介绍Filter的用法. Filter是用来过 ...
- 《CLR via C#》读书笔记 之 泛型
第十二章 泛型 2014-06-15 初始泛型 12.3 泛型基础结构 12.3.1 开放类型与封闭类型 12.3.2 泛型类型和继承 12.3.3 泛型类型同一性 12.3.4 代码爆炸 12.6 ...
- 机器学习实战 - 读书笔记(12) - 使用FP-growth算法来高效发现频繁项集
前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第12章 - 使用FP-growth算法来高效发现频繁项集. 基本概念 FP-growt ...
- C#&java重学笔记(泛型)
C#部分: 1.泛型的出现主要用于解决类.接口.委托.方法的通用性,通过定义泛型类.接口.委托.方法,可以让不同类型的数据使用相同运算规则处理数据,方便了开发. 2.利用System.Nullable ...
- TypeScript笔记[5]泛型+Dictionary 转
TypeScript笔记[5]泛型 在C++.C#.Java等主流编程语言中,一般对泛型编程提供了支持.合理利用泛型,可以提高开发效率.提升代码质量. 例如在C++编程语言中,常常利用下面的结构表 ...
- SQL反模式学习笔记12 存储图片或其他多媒体大文件
目标:存储图片或其他多媒体大文件 反模式:图片存储在数据库外的文件系统中,数据库表中存储文件的对应的路径和名称. 缺点: 1.文件不支持Delete操作.使用SQL语句删除一条记录时,对应的文 ...
- JAVA自学笔记12
JAVA自学笔记12 1.Scanner 1)JDK5后用于获取用户的键盘输入 2)构造方法:public Scanner(InputStream source) 3)System.in 标准的输入流 ...
- golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题
golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...
- Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建
Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...
随机推荐
- java list 清空列表所有元素
Java list 清空列表所有元素 List<String> list = new ArrayList<String>(3);list.add("hello&quo ...
- 学习笔记(11)- 文本生成RNNLG
https://github.com/shawnwun/RNNLG 数据集 给出了4个行业的语料,餐馆.酒店.电脑.电视,及其组合数据. 数据格式 任务 根据给定格式的命令,生成自然语言. 方法.模型 ...
- 【项目】小试牛刀-polo360静态网页项目(附psd文件资源)
笔者尝试下开发简单的静态网页,下面分享过程及源码.这是polo360的下载链接:https://pan.baidu.com/s/1WqGxKMYY_DHfrSJ9lLL-WA 提取码:v2qi (一 ...
- Centos7 nginx的目录结构与nginx主配置文件解析
一.nginx的目录结构 [root@node nginx_116]# ls client_body_temp conf fastcgi_temp html logs proxy_temp ...
- Hive的存储和MapReduce处理——数据清洗(Part2)
日期:2019.11.14 博客期:116 星期四 基本的处理类 import java.sql.Connection; import java.sql.DriverManager; import j ...
- python中对闭包的理解
运行环境声明:本人的代码在sublime text 3中写的,可以Ctrl+b运行.python版本是python3.6.如果您直接运行的,请自觉加入if __name__ == '__main__' ...
- idea增删改查
idea应用mybatis写增删改查 entity层 private Integer id;private String userCode;private String userName;privat ...
- Intend之Date的几个功能
封装为一个方法 1.跳转到拨号页面 //跳转到拨号页面的方法 protected void takeCall(String info){ Intent intent=new Intent(); int ...
- spyder崩溃修复
实验室突然断电,重启电脑后spyder崩溃 在anaconda命令行中输入命令失败 StackOverflow上找的解决方案,适合win10系统,简单粗暴 在win10搜索里面找,点一下就自动修复了
- MySQL日常使用笔记
逍遥山人的MySQL使用笔记,持续更新中 表结构 新建表以及添加表和字段的注释 create table t_user( ID INT(11) primary key auto_increment c ...