.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. linux下nc提交web报文问题

    1.用wireshark截取访问百度首页拿到的请求数据包: GET /index.php?tn=newbdie_bd_dg&bar= HTTP/1.1 Host: www.baidu.com ...

  2. 最大子段和(Max Sum)

    Max Sum. The following is an instance. a)    (-2,11,-4,13,-5,-2) 思路: 最大子段和:给定一个序列(元素可正可负),找出其子序列中元素和 ...

  3. spring-boot分环境打包为war包

    1.启动类修改 @EnableSwagger2 @SpringBootApplication public class CustWebAcApplication extends SpringBootS ...

  4. GUC-11 线程池

    import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java ...

  5. jsp页面中注释 <!-- --> 和<%-- --%> 的区别

    jsp页面中注释 <!-- --> 和<%-- --%> 的区别 原创 2016年09月01日 17:55:44 标签: jsp注释 5605 今天发现一个问题:在jsp代码中 ...

  6. cent7.0 mysql 修改端口

    如何查看mysql 默认端口号和修改端口号 2015-03-19 17:42:18 1. 登录mysql [root@test /]# mysql -u root -p Enter password: ...

  7. NHibernate 学习导航

    http://www.cnblogs.com/lyj/archive/2008/10/30/1323099.html

  8. 纯CSS实现3D图像轮转

    CSS演武场今天继续,今天看一个纯css实现的3D图像轮转效果,请大家猛戳研究效果先,也可下载收藏先. 首先看html文件,div.billboard为效果的容器,利用10个div.poster分割图 ...

  9. thinkphp中常用的模板变量

    在thinkphp中的模板要加载静态文件如css,js等文件时要经常用到模板常量. 假如项目放在/web/shop中,则如下所示对应常量的输出值: 1 2 3 4 5 6 7 8 9 // 不含域名 ...

  10. Pycharm 激活码(转) 有效期到2019/10月

    Pycharm 激活码(转) 有效期到2019/10月 2018年11月13日 17:15:32 may_ths 阅读数:64   [激活码激活] 修改hosts文件 添加下面一行到hosts文件,目 ...