.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 ...
随机推荐
- idea没有import project解决办法
参考:https://blog.csdn.net/zengxiaosen/article/details/52807540
- ZeroTier 局域网组建工具
无公网IP通过ZeroTier实现内网穿透 需求:想要在公司访问家里内网NAS,或是在家里访问公司服务 有固定的公网IP或动态的公网IP:常见的方案动态域名解析做端口转发方式等 无公网IP:常见的实现 ...
- 【 JdbcUtils 】mysql数据库查询
JdbcUtils package k.util; import java.sql.*; import java.util.ArrayList; import java.util.HashMap; i ...
- Android开发实例之miniTwitter登录界面的实现
原文: http://www.jizhuomi.com/android/example/134.html 本文要演示的Android开发实例是如何完成一个Android中的miniTwitter登录界 ...
- vue-cli 手脚架mock虚拟数据的运用,特别是坑!!!
1.现在基本的趋势就是前后分离,前后分离就意味着当后台接口还没完成之前,前端是没有接口可以拿来调用的 ,那么mock虚拟数据就很好的解决了这一问题,前端可以直接模拟真实的数据AJAX请求! 运用 步骤 ...
- webservice之实现天气预报
前通过传智的视频自学了webservice的基本使用,也了解到webservice就是一种跨编程语言和跨操作系统平台的远程调用技术. 对于这些理论知识在这里也不再做过多的解释,本次主要就是记录与分享使 ...
- 63 滑动窗口的最大值 &&front(),back()操作前一定要判断容器的尺寸不能为0
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6, ...
- Xcode Edit Schemes
关于本文:有关“Xcode Edit Schemes”的设置,还是有很大的学问的.由于时间关系,我一点一点的补充. 1.在开发的时候,至少将Run的Build Configuration设置为Debu ...
- Java中Comparator的使用
在某些特殊情况,我们需要对一个对象数组或集合依照对应的属性排序:此时,我们就可以用Comparator接口处理. 上代码 TestComparaTo 类 package com.test.interf ...
- 初始化加载和导航时脚本执行的函数(初始化脚本执行环境)page.evaluateOnNewDocument
/** * Copyright 2017 Google Inc., PhantomJS Authors All rights reserved. * * Licensed under the Apac ...