.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 ...
随机推荐
- Python学习笔记008
while循环 while 条件 : 执行 num =1 while num<=10: print(num) num+=1 1-100偶数 方法1 num =2 while num& ...
- $.fn.exted({})与$.extend({})区别
$.fn.extend({}) $.fn.extend({ aaa:function(){ alert(1); } }); 可以通过对象调用方法 $('.aaa').aaa(); $.extend({ ...
- 超参数 hyperparameters
转载:https://www.cnblogs.com/qamra/p/8721561.html 超参数的定义:在机器学习的上下文中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据 ...
- Redis 的使用
1. 概念: redis是一款高性能的NOSQL系列的非关系型数据库 1.1.什么是NOSQL NoSQL(NoSQL = Not Only SQL),意即"不仅仅是SQL",是一 ...
- unity优化-CPU(网上整理)
CPU方面性能考虑:引擎和代码渲染模块.动画模块.物理模块.ui模块.粒子模块.加载模块.GC模块最重要的是渲染模块.UI模块和加载模块1.渲染模块主要是:场景.物体和特效的渲染a.降低Draw ca ...
- 洛谷 P5242 [USACO19FEB]Cow Dating P
这道题很有意思. 不难发现,对于一个区间 \([l, r]\),恰好只有一个奶牛接受邀请的概率为 \[\prod_{i=l}^r(1-p_i) \cdot \sum_{i=l}^r \frac {p_ ...
- python面向对象之元类
目录 元类 造类 第一阶段 第二阶段 造对象 元类 元类(A) ---> 类(B) ---> 实例(C) 对于实例C而言,它是对象,它的类就是类B 对于类B而言,它其实也是对象,那它的类就 ...
- eclipse安装SVN插件的两种方法
eclipse里安装SVN插件,一般来说,有两种方式: 直接下载SVN插件,将其解压到eclipse的对应目录里 使用eclipse 里Help菜单的“Install New Software”,通过 ...
- os期末复习
登记之后会发生两个变化:读者数增加(v操作).座位数减少(p操作) 注销之后会发生的变化:读者数减少(p操作).座位数增加(v操作) 必须要清楚释放的是甚麽,以及申请的是甚麽资源(在具体的题目当中) ...
- Link Analysis_2_Application
US Cities Distribution Network 1.1 Task Description Nodes: Cities with attributes (1) location, (2) ...