.NET via C#笔记12——泛型
12 泛型
使用值类型作为参数的泛型容器,传入值类型的参数时,不需要进行装箱
12.1 FCL中的泛型
System.Array中提供了很多泛型方法
AsReadOnlyBinarySearchConvertAllExistsFindFindAllFindIndexFindLastFindLastIndexForEachIndexOfLastIndexOfResizeSortTrueForAll
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 ...
随机推荐
- 三大JavaScript框架对比——AngularJS、BackboneJS和EmberJS
<三大JavaScript框架对比——AngularJS.BackboneJS和EmberJS> 本文转载自 作者:chszs,博客主页:http://blog.csdn.net/chs ...
- [POI 2014]PTA-Little Bird
Description 题库连接 给你 \(n\) 棵树,第 \(i\) 棵树的高度为 \(d_i\).有一只鸟从 1 号树出发,每次飞跃不能超过 \(k\) 的距离.若飞到下一棵树的高度大于等于这一 ...
- 《容器化.NET应用架构指南》脑图学习笔记(第一部分)
一.关于这本官方“圣经” 作为.NET程序员,对于微软官方推动的架构示例总是特别关注,从PetShop到MusicStore再到eShopOnContainers,每一次关注,都会了解到业界最新的架构 ...
- 随机游走模型(Random Walk)
给定了一个时间顺序向量\(z_1,...,z_T\),rw模型是由次序r来定义的,\(z_t\)仅取决于前\(t-r\)个元素.当r = 1时为最简单的RW模型. 给定了向量的其他元素,\(z_t\) ...
- mysql 命令行个性化设置
通过配置显示主机和用户名 mysql -u root -p --prompt="(\u@\h) [\d]>" 或在配置文件中修改,可在命令行中的目标位置查看 --tee na ...
- Python连载60-Tkinter布局、按钮以及属性详解
一.Tkinter 1.组件的大致使用步骤 (1)创建总面板 (2)创建面板上的各种组件: i.指定组件的父组件,即依附关系:ii.利用相应的属性对组件进行设置:iii.给组件安排布局. (3)同步 ...
- 防火墙问题 Linux系统 /etc/sysconfig/路径下无iptables文件
虚拟机新装了一个CentOs7,然后做防火墙配置的时候找不到iptables文件,解决方法如下: 因为默认使用的是firewall作为防火墙,把他停掉装个iptable systemctl stop ...
- 解决css中display:inline-block产生的空隙问题
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- sql语句中,传入的参数带单引号的问题
今天在大批量操作数据时,遇到此问题,解决如下: if(cateName.indexOf("'")!=-1){ oql = " select * where name = ...
- [*CTF2019]babyflash
用JPEXS反编译flash.swf得到441张黑白图片和1个mp3文件 软件下载地址:https://github.com/jindrapetrik/jpexs-decompiler/release ...