理解C#泛型
在C# 2.0中引入了泛型,泛型的出现解决了编码中的很多问题。相信大家一定经常用到"System.Collections.Generic"命名空间中的泛型集合类("Generic"就是泛型的意思)。在C# 1.0中,我们还在使用"System.Collections"命名空间中的非泛型集合类,那么看看我们在没有泛型的时候遇到的问题。
问题1:强制类型转换
ArrayList stuList = new ArrayList();
Student wilber = new Student { Name = "Wilber", Age = , Gender = "Male" };
stuList.Add(wilber);
Student stu = (Student)stuList[];
stuList.Add();
在使用非泛型集合ArrayList时,所有的对象都是以object类型加入ArrayList,当对象从ArrayList取出的时候也是object类型,这时我们就需要进行强制类型转换,如果转换不当,就会得到一个运行时的错误;即使我们向ArrayList添加不同类型的对象时,也不会报错(例如上面向stuList中加入了一个int值)。
问题2:装箱和拆箱
在上面的例子中,如果我们使用ArrayList存放一组值类型的数据(例如一组int值),存入时,每个值类型的数据都要进行装箱为object类型;取出时,每个object类型的数据又要进行拆箱操作。
可以看到,在使用非泛型集合的时候,用户需要自己进行类型转换,并且可能遇到运行时的类型转换异常;同时,对于值类型的操作 ,非泛型集合会有装箱和拆箱带来的效率问题。
泛型的出现
对于上面的问题,我们可以使用C# 2.0中的泛型集合。
这样一来,我们就通过类型参数(例子中的Student)来限制List可以包含的实例类型,从而避免的强制类型转换。
同时,通过类型参数,编译器可以进行类型检查,当试图往List中存入一个与类型参数不匹配的对象的时候,编译器就是给出错误提示。
List<Student> stuList = new List<Student>();
Student wilber = new Student { Name = "Wilber", Age = , Gender = "Male" };
stuList.Add(wilber);
Student stu = stuList[];
stuList.Add();

泛型中的术语
下面我们看看泛型中的一些概念和术语。
泛型有两种表现形式:泛型类型(包括类、接口、委托和结构,没有泛型枚举)和泛型方法。在泛型类型和泛型方法中都会有类型参数,当通过泛型类型实例化对象或者对泛型方法调用的时候,都需要使用一个真实的类型来代替类型参数。
类型参数是真实类型的占位符,在泛型声明过程中,所有的类型参数放在一对间括号中(<>),通过逗号分隔。
泛型类型
根据类型参数不同的指定类型实参的情况,泛型类型可以分为:
- 如果没有为类型参数提供类型实参,那么声明的就是一个未绑定泛型类型(unbound generic)
- 如果指定了类型实参,该类型就称为已构造类型(constructed type),然而已构造类型又可以是开放类型或封闭类型的
- 包含类型参数的类型就是开放类型(open type)(所有的未绑定的泛型类型都属于开放类型的),
- 每个类型参数都指定了类型实参就是封闭类型(closed type)
类型是对象的蓝图,我们可以通过类型来实例化对象;那么对于泛型来说,未绑定泛型类型是以构造泛型类型的蓝图,已构造泛型类型又是实际对象的蓝图。
下图就是一个简单的例子,Dictionary<TKey, TValue>就是一个泛型类型(未绑定泛型类型,开放类型);通过制定类型参数,可以得到不同的封闭类型;通过不同的封闭类型有可以构造不同的实例。

泛型方法
我们都已经习惯了方法的参数和返回值拥有固定的类型,这里就看看“参数化”的方法。对于泛型方法,可以理解为拥有类型参数的方法。
对于上面例子中Dictionary<TKey, TValue>这个泛型类型,有很多方法可以使用,例如:
- void Add(TKey, key, TValue value)
- bool ContainsValue(TValue value)
- bool ContainsKey(TKey key)
注意,这些方法中没有一个是真正的泛型方法,他们只是使用了泛型类型的类型参数。
真正的泛型方法应该拥有自己的类型参数,当我们使用泛型方法的时候,要给泛型方法的类新参数指定类型实参,接下来看一个泛型方法的例子。
class Program
{
static void Main(string[] args)
{
Console.WriteLine("The bigger one is {0}", GetBiggerOne<int>(,));
Console.WriteLine("The bigger one is {0}", GetBiggerOne<string>("Hello", "World")); Console.Read();
} public static T GetBiggerOne<T>(T itemOne, T itemTwo) where T : IComparable
{
if (itemOne.CompareTo(itemTwo) > )
{
return itemOne;
}
return itemTwo;
}
}
在上面的例子中,我们使用泛型方法来实现一个两个元素比较的例子,我们看到方法"GetBiggerOne"拥有自己的类型参数,当我们看到一个泛型方法时,可以一步步用真实的类型替换泛型方法中的类型参数,这样就会简化我们的分析。
对于泛型的类型约束,将在下面一篇文章介绍。

泛型的优点
根据上面的分析,可以看到泛型有一些的优点:
- 代码重用
- 泛型提供的代码的重用,确切的说应该是 "逻辑和算法的重用"。从前面的泛型方法例子可以看到,通过泛型可以避免为每种特定的类型实现一个比较方法。
- 类型安全
- 泛型类型保证了类型安全,可以在编译期就发现类型不匹配的问题,而不是等到运行时
- 效率
- 避免值类型的装箱和拆箱引起的效率问题(后面会简单介绍为什么泛型可以避免装箱和拆箱)
总结
泛型的出现,给我们带来了很多好处,泛型实现了类型和方法的"参数化"。
基于泛型,我们可以实现代码重用,并且泛型为我们提供了类型安全检查。对于值类型的操作,通过泛型可以避免装箱和拆箱带来的性能损失。
同样C# 2.0 以后,就建议只在代码中使用支持泛型的集合类了(System.Collections.Generic)。
理解C#泛型的更多相关文章
- 理解C#泛型(转)
理解C#泛型 http://www.cnblogs.com/wilber2013/p/4292240.html 泛型中的类型约束和类型推断 http://www.cnblogs.com/wilber2 ...
- Java 干货之深入理解Java泛型
一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类.如果要编写可以应用多中类型的代码,这种刻板的限制对代码得束缚会就会很大. ---<Thinking in Java> ...
- 深入理解C#泛型
前面两篇文章介绍了C#泛型的基本知识和特性,下面我们看看泛型是怎么工作的,了解一下泛型内部机制. 泛型内部机制 泛型拥有类型参数,通过类型参数可以提供"参数化"的类型,事实上,泛型 ...
- 转:理解Java泛型
JDK 5.0 中增加的泛型类型,是 Java 语言中类型安全的一次重要改进.但是,对于初次使用泛型类型的用户来说,泛型的某些方面看起来可能不容易明白,甚至非常奇怪.在本月的“Java 理论和实践”中 ...
- 深入理解java泛型
一. 什么是泛型? 泛 型(Generic type 或者 generics)是对 简单的理解,就是对类型的参数化,比如我们定义一个类属性或者实例属性时,往往要指定具体的类型,如Integer.Per ...
- 理解Java泛型 通配符 ? 以及其使用
什么是泛型: 泛型从字面上理解,是指一个类.接口或方法支持多种类型,使之广泛化.一般化和更加通用.Java中使用Object类来定义类型也 能实现泛型,但缺点是造成原类型信息的丢失,在使用中容易造成C ...
- 如何深入理解Java泛型
一.泛型的作用与定义 1.1泛型的作用 使用泛型能写出更加灵活通用的代码泛型的设计主要参照了C++的模板,旨在能让人写出更加通用化,更加灵活的代码.模板/泛型代码,就好像做雕塑时的模板,有了模板,需要 ...
- 理解C#泛型运作原理
前言 我们都知道泛型在C#的重要性,泛型是OOP语言中三大特征的多态的最重要的体现,几乎泛型撑起了整个.NET框架,在讲泛型之前,我们可以抛出一个问题,我们现在需要一个可扩容的数组类,且满足所有类型 ...
- 深入理解 Java 泛型
随机推荐
- Class to connect postgres with python in psycopg2
For we need to connect the postgres db in project very frequently, so write the follows class: impor ...
- JavaScript的函数重载
java语言中函数的重载和重写可谓是很重要的概念,所以在写js的时候时不时的会想到这种用法,重写先不说,这里只说重载.. <script language="JavaScript&qu ...
- 浅谈export 以及环境变量
简要说一下env,set,export的区别:env命令显示环境变量,set和export显示环境变量和自定变量. export:可以讲自定变量转化为环境变量之前有一个疑惑,我们定义环境变量PATH时 ...
- oracle 高水位线详解
一.什么是水线(High Water Mark)? 所有的oracle段(segments,在此,为了理解方便,建议把segment作为表的一个同义词) 都有一个在段内容纳数据的上限,我们把这个上限称 ...
- 如何用ZBrush快速雕刻LOL中的Lissandra
话说<魔兽>还有1天就上映了,热爱这款游戏的你想必早已按耐不住了吧,别急,再耐心等一下.不过今天我们要讲的,不是魔兽,而是另一款很多人为 之疯狂的游戏—英雄联盟,也就是你们熟悉的LOL啦, ...
- bzoj-3444 3444: 最后的晚餐(组合数学)
题目链接: 3444: 最后的晚餐 Time Limit: 5 Sec Memory Limit: 128 MB Description [问题背景] 高三的学长们就要离开学校,各奔东西了.某班n人 ...
- HDU 5105 Math Problem --数学,求导
官方题解: f(x)=|a∗x3+b∗x2+c∗x+d|, 求最大值.令g(x)=a∗x3+b∗x2+c∗x+d,f(x)的最大值即为g(x)的正最大值,或者是负最小值.a!=0时, g′(x)=3∗ ...
- HDU 1850 Being a Good Boy in Spring Festival
此题先考虑第一种,5 7 9的情况,先手如果想赢,则必定要把异或值变为0,因为随便取,所以此处的异或指的是对堆中的石子数进行异或,而非异或其SG函数. 首先7^9=14,因为要异或为0,则5要变成14 ...
- 字典树(Tire)模板
#include<stdio.h> #include<string.h> #include<stdlib.h> struct node { node *ne[]; ...
- Linux下php安装memcache扩展
安装环境:CentOS 6.4 php扩展memcache的作用是为了支持memcached数据库缓存服务器,下面是安装方法. 1.下载 下载地址:http://pecl.php.net/packag ...