在重构机房的过程中不断的遇到一些新的名词,其实说新也不新,就像今天要聊的这个“泛型”,其实在之前看C#视频和《大话设计模式》的时候已经有了解到了,但当时只是了解的一些大意,没有真正了解是怎样的。这次重构中总是会听大家谈到“泛型怎样怎样”,然后我就开始尝试使用泛型,在使用泛型之前我用的类型是DataTable类型来接收返回值的,那么我为什么要把DataTable类型改为泛型呢?接下来分享一下我的实践学习心得。
我们先来了解一下这两个的概念DataTable和泛型。
DataTable类型:DataTable是一个很古老的类,再往前不清楚,但是.Net2.0就肯定有了,主要用于数据的封装与存储等等。
使用DataTable类型时有以下几个特点:
(1).必须了解数据库的结构,降低数据库的安全性。
(2).在代码编写过程容易写错,而且编译器不检查。
(3).在程序中进行数据传递的是DataTable,不再是实体,违背了面向对象编程的思想。
(4).DataTable为弱类型,无法直观的看出字段的数据类型。
泛型:泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
(一些强类型编程语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的编程语言只能达到部分目的。)
要知其然,更要知其所以然,所以当我们在接触一个新的东西时要思考,思考为什么要有这个东西呢?或者说,我用它会给我带来什么便捷之处,也就是它有什么好处
对值类型使用非泛型集合类,在把值类型转换为引用类型,和把引用类型转换为值类型时,需要进行装箱和拆箱操作。装箱和拆箱的操作很容易实现,但是性能损失较大。假如使用泛型,就可以避免装箱和拆箱操作。
ArrayList list=new ArrayList();
list.Add(20); //装箱,list存放的是object类型元素,须将值类型转化为引用类型
int i=(int)list[0]; //拆箱,list[0]的类型是object,要赋值就得把引用类型转化为值类型
如果换成泛型编程,就不会有装箱和拆箱的性能损失。
List list=new List();
list.Add(20); //因为指定了用int来实例化,因此不必装箱
int i=list[0]; //同样地,访问时也不需要拆箱
与ArrayList类一样,如果使用对象,可以在这个集合中添加任意类型。
如果使用非泛型编程,如下代码,就有可能在某些情况下会发生异常。
ArrayList list=new ArrayList();
list.Add(20);
list.Add("string");
list.Add(new MyClass());
foreach(int i in list)
{
Console.WriteLine(i); //这里会有个异常,因为并不是集合中的所有元素都可以转化为int
}
List list=new List();
list.Add(20);
lsit.Add("string"); //编译时报错,只能报整数类型添加到集合中
list.Add(new MyClass()); //同上
因为泛型类的定义会放在程序集中,所以用某个类型实例化泛型泛型类不会在IL代码中复制这些类。但是,在JIT编译器把泛型类编译为内部代码时,会给每个值类型创建一个新类。引用类型共享同一个内部类的所有实现代码。这是因为引用类型在实例化的泛型类中只需要4字节的内存单元(32位系统),就可以引用一个引用类型。值类型包含在实例化的泛型类的内存中。而每个值类型对内存的要求都不同,所以要为每个值类型实例化一个新类。
一句话总结:泛型可以提高性能、类型安全和质量,减少重复性的编程任务,简化总体编程模型。
并不是所有的情况下都需要用到泛型,并不是用的越多越好,酌情使用。
使用泛型类有3种情况:
(1)使用泛型API,比如说,一个库函数,需要传入一个List
(2)继承自一个泛型类或者接口。比如实现一个 IComparer
(3)自己定义 API,考虑使用泛型。当不知道客户端需要传入什么类型的时候,有2个选择,一个是用 object,但是 object 是弱类型,另一个选择是使用泛型。看上去泛型更好,其实也不是。一则是泛型的反射更复杂,二来是泛型在C#2 C#3是不支持逆变和协变的,在C#1甚至是不被支持的。出于灵活性和兼容性的考虑不使用泛型。
还有一种说法是:如果你写两套代码,处理流程几乎一模一样,只是针对的数据的类型不一样,就可以考虑“只写一遍”,而不是两遍。实际上当你“只写一遍”,不但少写了一个,而且少写了将来许多个代码。
那么上面讲了这么多到底要怎么用呢?接着往下看。
public List selectUser(Entity.UserInfo UserInfo) //定义一个返回值是泛型的方法
{
SqlHelper sqlHelper = new SqlHelper();
string sql = @"SELECT * FROM [User_Info] WHERE UserID=@UserID ";
SqlParameter[] sqlParams = { new SqlParameter("@userID", UserInfo.UserID) };
DataTable dt = sqlHelper.ExecuteQuery(sql, sqlParams, CommandType.Text);
ConvertHelper ct = new ConvertHelper();
List list = new List();
list = ct.convertToList(dt); //调用转换泛型的方法
return list;
}
上面这段代码中是我调用了一个已经写好了的DataTable转泛型的方法,这个方法网上有很多不同的写法,大家可以自己去查一下,这里就不展示了。
所以说,学习是一个不断反复的过程,一些晦涩难懂的知识点有时候也没有必要过于纠结当时的不理解,我们暂且可以先把它“挂”起来,因为在以后的学习过程中我们会不断的接触同样的东西,然后多角度的了解,深入学习。
从一个师姐的博客里看的的两句话,分享给大家。
1、 每一次和陌生知识的独处,都意味着一种成长。
2、 每一次的蜕变都意味着梦想的加温。