.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基础 -1.2
Shell是脚本程序的含义 在很多编程语言中为了方便使用者进行代码的开发 都会有shell交互式编程环境 可能是为了进行一些简短的程序验证 但是在java里面就必须编写很多的结果代码才可以实现 为了解 ...
- docker进入交互界面
进入cmd交互界面 docker run -it python:3.5 /bin/bash 退出 exit ctrl + d
- Linux系统在IT行业处于什么位置
相信每一位程序员对于linux都不陌生,不管是新入行的小白,还是有着十几年编程经验的大佬,都知道Linux在IT行业中的位置吧! 我是一名Web JAVA开发的小白,对于初入IT行业的小白来说,工 ...
- 最近公共祖先(LCA)问题
目录 最近公共祖先 1.向上标记法 2.树上倍增法 3.Tarjan算法 最近公共祖先 定义:给定一颗有根树,若结点 z 既是 x 的祖先,也是 y 的祖先,则称 z 是 x,y 的公共祖先.在 x, ...
- 一个元素的宽度 及带padding,border,margin的各自情况
width() - 设置或返回元素的宽度 height() - 设置或返回元素的高度 innerWidth() - 返回元素的宽度(包含 padding) innerHeight() - 返回元素的高 ...
- HackerOne去年发放超过8200万美元的赏金,联邦政府参与度大幅上涨
2019年,由黑客驱动的漏洞赏金平台HackerOne支付的漏洞奖金几乎是前几年总和的两倍,达到8200万美元. HackerOne平台在2019年也将注册黑客数量翻了一番,超过了60万,同时全年收到 ...
- R语言 which() 、 which.min() 、 which.max() 函数
函数 which() 可以用来找到满足条件的下标,如 x <- c(3, 4, 3, 5, 7, 5, 9) which(x > 5) 5 7 seq(along=x)[x > 5] ...
- Django 学习 之ORM简介与单表操作
一.ORM简介 1.ORM概念 对象关系映射(Object Relational Mapping,简称ORM). 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到 ...
- pyhton的返回值
a=input() input()函数返回的是字符‘2’ a=bin(2) bin函数返回的是二进制形式的字符串'0b10' type()判断类型 bin()转化成二进制 int(a,2)可以强制把字 ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 按钮:禁用按钮
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...