.NET泛型或许是借鉴于C++泛型模版,借助它可以实现对类型的抽象化、泛型处理,实现了类型和方法之间的解耦。一个最经典的运用是在三层架构中,针对不同的领域模型,在基接口、基类中实现针对各个领域模型的泛型处理。

本篇主要包括:
为什么需要泛型
    ※ 不用泛型
    ※ 使用泛型
    ※ 泛型的运行时本质
泛型语法
典型的泛型类

为什么需要泛型

不用泛型

来看一个比较类型的方法。

public class Calculator
{
public static bool AreEqual(int value1, int value2)
{
return value1 == value2;
}
}

在客户端调用。

class Program
{
static void Main(string[] args)
{
bool result = Calculator.AreEqual(1, 2);
if (result)
{
Console.WriteLine("相等");
}
else
{
Console.WriteLine("不等");
}
Console.ReadKey();
}
}

运行结果:不等

不用泛型的缺点一:不是类型安全

如果我们想使用现在的方法来比较字符串类型。

bool result = Calculator.AreEqual("A", "B");

这时,看到编译器报错。从这点来看,AreEqual()方法不是类型安全的方法,当输入string类型,编译器就会报错。

如果把AreEqual()方法的参数类型改成object,编译器就不再报错。

public class Calculator
{
public static bool AreEqual(object value1, object value2)
{
return value1 == value2;
}
}

以上,运行也正常。

不用泛型的缺点二:装箱与拆箱导致性能降低

现在,对于AreEqual(object value1, object value2),从方法本身来讲是没有问题的,但在客户端调用的时候,比如我们还是想比较值类型。

bool result = Calculator.AreEqual(1, 2);

在运行时,当整型值类型参数1和2传递、赋值给AreEqual(object value1, object value2)中的引用类型参数value1和value2的时候,发生了一次"装箱"操作。而当把引用类型转换成值类型的时候,又会发生一次"拆箱"操作,这导致性能的降低。

使用泛型

把AreEqual()改成泛型方法。

public class Calculator
{
public static bool AreEqual<T>(T value1, T value2)
{
return value1.Equals(value2);
}
}

于是,在客户端可以这样:

bool result = Calculator.AreEqual<string>("A", "A");
bool result = Calculator.AreEqual<int>(5, 3);

由此,使用泛型的好处有:
1、实现了方法和类型的解耦。
2、不会造成类型转换,规避了因装箱于拆箱引起的性能问题。
3、泛型保证了类型的绝对安全。

当然,还可以把T的位置放在类上:

public class Calculator<T>
{
public static bool AreEqual(T value1, T value2)
{
return value1.Equals(value2);
}
}

然后这样使用:

bool result = Calculator<string>.AreEqual("A", "A");
bool result = Calculator<int.AreEqual(1, 2);

泛型的运行时本质

CLR中有专门的IL指令支持泛型操作。
→初次编译时,生成IL代码和元数据,T只是类型占位符,在编译时不进行实例化
→JIT编译时,以实际类型替换元数据中的T占位符
→将元数据转换为本地代码

泛型语法

class MyArray<T> where T : Student, new()
{
private T[] _items;
public T myData; public MyArray()
{
myData = default(T);
} public void Add(T item)
{}
}

创建泛型实例要指定实际的数据类型:
MyArray<Int32> myArr = new MyArray<Int32>();

值类型的默认值为0,引用类型的默认值为null,使用泛型默认值:
myData = default(T);

泛型约束:
T : 基类名,表示必须是基类名的派生类
T :new(), 表示必须具有无参构造函数,new()约束必须放在最后面
T :struct, 表示必须是值类型
T :class, 表示必须是引用类型
T :接口名,表示必须实现该接口,或实现该接口的接口

泛型类本质上仍然是一个类,依然可以继承:

internal class GenericeComparer<T> : Comparer<T> where T : IComparable<T>
class MyArray<T> : ArrayList

典型的泛型类

在System.Collections.Generic命名空间和System.Collections.ObjectModel中,定义了不同的泛型类和泛型接口,这些泛型多为集合类。

List<T> 对应ArrayList集合类
SortedList<TKey, TValue> 对应SortedList集合类
Queue<T> 先进先出的集合类
Stack<T> 后进先出的集合类
Collection<T> 自定义泛型集合的基类
Dictionary<TKey, TValue> 对应于Hashtable集合类

参考资料:
《你必须知道的.NET(第2版)》,作者王涛。

".NET泛型"系列包括:

.NET泛型01,为什么需要泛型,泛型基本语法

.NET泛型02,泛型的使用

.NET泛型03,泛型类型的转换,协变和逆变

.NET泛型04,使用Lazy<T>实现延迟加载

.NET泛型01,为什么需要泛型,泛型基本语法的更多相关文章

  1. [C# 基础知识梳理系列]专题六:泛型基础篇——为什么引入泛型

    引言: 前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解 ...

  2. 对比两个同类型的泛型集合并返回差异泛型集合 ——两个List<类名>的比较

    1: /// <summary> 2: /// 对比两个同类型的泛型集合并返回差异泛型集合 3: /// </summary> 4: /// <typeparam nam ...

  3. java 泛型详解(普通泛型、 通配符、 泛型接口)

    java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...

  4. 3. Java面向对象之泛型-指定多个泛型

    3. Java面向对象之泛型-指定多个泛型 package generic; class MutiGeneric<K, T> { private K key; private T take ...

  5. java泛型基础、子类泛型不能转换成父类泛型

    参考http://how2j.cn/k/generic/generic-generic/373.html 1.使用泛型的好处:泛型的用法是在容器后面添加<Type>Type可以是类,抽象类 ...

  6. java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  7. java泛型基础、子类泛型不能转换成父类泛型--未完待续

    参考http://how2j.cn/k/generic/generic-generic/373.html 1.使用泛型的好处:泛型的用法是在容器后面添加<Type>Type可以是类,抽象类 ...

  8. 泛型 System.Collections.Generic及泛型继承、运算符、结构、接口、方法、委托、事件、可空类型等

    一.定义泛型类 void Main() { //实例化泛型类时,才指定具体的类型 MyGenericClass<); Console.WriteLine(MyGeneri.InnerT1Obje ...

  9. java数据类型:集合存储元素类型限制<泛型> ;自定义类指定泛型<T> 以及限制用法;派生子类泛型<T> super(泛型内参数); 泛型通配符?以及?限制用法

    问题背景 Java 集合有个缺点,把一个对象"丢进"集合里之后,集合就会"忘记"这个对象的数据类型,当再次取出该对象时 该对象的编译类型就变Object类型(其 ...

随机推荐

  1. 删除数据库所有存储过程的SQL语句

    --/第1步**********删除所有表的外键约束*************************/ DECLARE c1 cursor for select 'alter table ['+ o ...

  2. LeetCode403. Frog Jump

    A frog is crossing a river. The river is divided into x units and at each unit there may or may not ...

  3. 关于URL编码(针对URL含有中文的参数)

    http://www.ruanyifeng.com/blog/2010/02/url_encoding.html 一.问题的由来 URL就是网址,只要上网,就一定会用到. 一般来说,URL只能使用英文 ...

  4. PHP array_diff 计算数组的差集

    array_diff (PHP 4 >= 4.0.1, PHP 5) array_diff — 计算数组的差集 说明 array array_diff ( array $array1 , arr ...

  5. day1作业登录接口总结

    作业一:编写登陆接口 1.输入用户名和密码 2.认证成功后显示欢迎信息 3.输错三次后锁定 上面作业,用了几种思路来解决问题:但是本质上其实都是一样的:核心都是对文件的操作,文件的增删改查:并且这些操 ...

  6. MVC图片上传并显示缩略图

    前面已经说了怎么通过MVC来上传文件,那么这次就说说如何上传图片然后显示缩略图,这个的实用性还是比较大.用UpLoad文件夹来保存上传的图片,而Temp文件夹来保存缩略图,前面文件上传部分就不再重复了 ...

  7. Rookey.Frame之系统初始化

    昨天介绍了数据库的配置,今天继续介绍系统的初始化功能:针对系统初始化在开发中也是很重要的一部分,它可以提前将相关数据提前自动初始化到系统中,同时也可以为上线测试提供方便,可以很方便进行系统测试演练,防 ...

  8. 黑马程序员_java基础笔记(09)...HTML基本知识、CSS、JavaScript、DOM

    —————————— ASP.Net+Android+IOS开发..Net培训.期待与您交流! —————————— 基本标签(a.p.img.li.table.div.span).表单标签.ifra ...

  9. (转)看懂UML类图

    转自:http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html 这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关 ...

  10. CentOS 编译源码安装MySQL-5.6.16

    mysql5.6.16的安装和之前的5.5.5.1有些不同,编译的时候不再使用./configure来进行了,使用了cmake命令来进行编译项目. 1.准备编译环境 yum -y installmak ...