泛型与集合类型

来源:互联网 发布:在数据库的增删改查 编辑:程序博客网 时间:2024/05/16 10:39

泛型与集合类型

 

说起泛型时,就不得不说到泛型集合类型,因为使用反省能够极大的提高集合类型的性能和安全性.

 

为了看看使用泛型能为集合类型带来什么好处,先看看不用泛型时集合类型的表现.最典型的非泛型集合类型就是ArrayList,这里便以它为例作为介绍,考虑一下下面的代码:

            ArrayList list = new ArrayList();            const int listSize = 3;            for (int i = 0; i < listSize; i++)            {                list.Add(i);            }            for (int i = 0; i < listSize; i++)            {                int value = (int)list[i];                Console.WriteLine(value);            }  


如果对.NET的类型系统没有深刻的认识,可能会觉得上面的代码没有任何的问题,楼主一开始就觉得没问题.实际上,上面的代码属于典型的”可以运行”的代码,而非”好的”代码.因为ArrayList类型为了包容任何类型,所以他接受的参数为所有类型的基类Object,object是一个引用类型,int是一个值类型,因此当调用list.Add(),存在一个装箱操作.同理,当从ArrayList中获得元素时,又需要一个拆箱操作:int value=(int)list[i].这两个操作对于.NET来说是相当耗时的,尤其是当集合类型包含的元素比较多或值类型比较大(比如复杂的枚举或者结构)的时候.如果现在对装箱和拆箱还没有太深的理解,可以回头看看前面讲解的东西.

 

通过使用泛型,由于集合中的元素类型在编译时已经确定,就避免了拆装箱的操作,这样便显著提高了集合类型的性能..NET,ArrayList作用相同的泛型类型是List<T>,这里有一个小小的测试:

 

    class Program    {        const int listSize = 500000;        static void Main(string[] args)        {            UserArrayList();            UserGenericList();            Console.Read();        }        private static void UserArrayList()        {            ArrayList list = new ArrayList();            long startTicks = DateTime.Now.Ticks;            for (int i = 0; i < listSize; i++)            {                list.Add(i);            }            for (int i = 0; i < listSize; i++)            {                int value = (int)list[i];            }            long endTicks = DateTime.Now.Ticks;            Console.WriteLine("使用ArrayList,耗时: {0} ticks",endTicks-startTicks);        }        private static void UserGenericList()        {            List<int> list = new List<int>();            long startTicks = DateTime.Now.Ticks;            for (int i = 0; i < listSize; i++)            {                list.Add(i);            }            for (int i = 0; i < listSize; i++)            {                int value = list[i];            }            long endTicks = DateTime.Now.Ticks;            Console.WriteLine("使用List<int>,耗时: {0} ticks",endTicks-startTicks);        }      } 

机器的配置可能不同,输出的结果差别可能很大,如果你的机器的配置比较高,可能两个输出都是0,此时你需要增大一下listSize的值,但是不管怎样,使用List<T>比使用ArrayList提高了差不多2倍的性能.当使用一个大的值类型时,比如枚举或者结构,获得差异还会更大.

 

泛型能够提高的另一个好处就是类型安全,这时啥意思?看一段代码:

 

            ArrayList list = new ArrayList();            int i = 100;            list.Add(i);            string value = (string)list[0];


 

因为类型不匹配,所以这段代码有问题,添加到ArrayList中的是一个int类型,而获取时却想将它转换为string类型.可惜的是,编译器无法知道,因为对它来说,不管是int也好,string也好,他们都是object类型.在编译代码时,编译器提供给开发者的最大帮助之一就是可以检查出错误,也就是常说的编译时错误(Compile time error),.当使用ArrayList的时候,对于上面的问题没变异起无能为力,因为他认为这是合法的,编译可以顺利通过.这种错误有时候隐藏在程序中很难发现,最糟糕的情况是产品已经交付给用户了,而当用户在使用时不巧的执行到这段代码,变回抛出一个异常,这时的错误,成为运行时错误(Runtime error).

 

通过使用泛型集合,这种情况将不复存在,当试图进行类似上面的转换时,根本无法通过编译,这样有助于今早的发现问题:

 

            List<int> list = new List<int>();            int i = 100;            list.Add(i);            string value = (string)list[0];//这里会出现编译错误


 

 

最后给大家说一个使用泛型的小技巧.当程序大量使用泛型类型的时候,或者泛型的类型参数个数比较多的时候,代码可能看着比较散乱,不够简洁.此时可以使用using指令来声明一个代表着泛型类型的集合:

 

    using IntList = List<int>;//一定要写到程序的第一行    class Program    {        const int listSize=50000;        static void Main(string[] args)        {            IntList list = new IntList();            list.Add(100);            Console.Read();        }    } 


 

这样看上去简单多了,但是还有一个问题,就是不能跨文件使用,换而言之,在同一项目统一命名空间下的另一个文件中,无法使用这个IntList集合.此时,可以采用继承泛型类型的方式来解决,例如声明下面这样一个类:

 

public class IntList:List<int>{}


这个类不包含任何的实现,它的所有能力都继承自List<int>,这样在多个文件中,都可以使用IntList,它的使用方式和List<int>没有区别,只是看起来更加简洁一些.

 

 

总结

 

泛型是个很重要的知识,泛型可以避免重复代码,还学习了如何使用类型参数约束以及泛型方法,通过泛型集合List<T>与非泛型结合ArrayList的对比,了解了反省在集合类型中的应用和优势.

 

0 0