引言:
前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解,那就让我们一起进入本专题的学习的。
一、泛型的是什么
泛型的英文解释为generic,当然我们查询这个单词时,更多的解释是通用的意思,然而有些人会认为明明是通用类型,怎么成泛型了的,其实这两者并不冲突的,泛型本来代表的就是通用类型,只是微软可能有一个比较官方的此来形容自己引入的特性而已,既然泛型是通用的, 那么泛型类型就是通用类型的,即泛型就是一个模子。在生活中,我们经常会看到模子,像我们平常生活中用的桶子就是一个模子,我们可以用桶子装水,也可以用来装油,牛奶等等,然而把这些都装进桶子里面之后,它们都会具有桶的形状(水,牛奶和油本来是没有形的),即具有模子的特征。同样,泛型也是像桶子一样的模子,我们可以用int类型,string类型,类去实例化泛型,实例化之后int,string类型都会具有泛型类型的特征(就是说可以使用泛型类型中定义的方法,如List<T>泛型,如果用int去初始化它后,List<int>的实例就可以用List<T>泛型中定义的所有方法,用string去初始化它也一样,和我们生活中的用桶装水,牛奶,油等非常类似)
二、C# 2.0为什么要引入泛型
大家通过第一部分知道了什么是泛型,然而C#2.0中为什么要引入泛型的?这答案当然是泛型有很多好处的。下面通过一个例子来说明C# 2.0中为什么要引入泛型,然后再介绍下泛型所带来的好处有哪些。
当我们要写一个比较两个整数大小的方法时,我们可能很快会写出下面的代码:
public class Compare { // 返回两个整数中大的那一项 public static int Compareint(int int1, int int2) { if (int1.CompareTo(int2) > 0) { return int1; } return int2; } }
然而需求改变为又要实现比较两个字符串的大小的方法时,我们又不得不在类中实现一个比较字符串的方法:
// 返回两个字符串中大的一项 public static string Comparestring(string str1, string str2) { if (str1.CompareTo(str2) > 0) { return str1; } return str2; }
如果需求又改为要实现比较两个对象之间的大小时,这时候我们又得实现比较两个对象大小的方法,然而我们中需求中可以看出,需求中只是比较的类型不一样的,其实现方式是完全一样的,这时候我们就想有没有一种类型是通用的,我们可以把任何类型当做参数传入到这个类型中去实例化为具体类型的比较,正是有了这个想法,同时微软在C#2.0中也想到了这个问题,所以就导致了C#2.0中添加了泛型这个新的特性,泛型就是——通用类型,有了泛型之后就可以很好的帮助我们刚才遇到的问题的,这样就解决了我们的第一个疑问——为什么要引入泛型。下面是泛型的实现方法:
public class Compare<T> where T : IComparable//约束... { public static T CompareGeneric(T t1, T t2) { if (t1.CompareTo(t2) > 0) { return t1; } else { return t2; } } }
这样我们就不需要针对每个类型实现一个比较方法,我们可以通过下面的方式在主函数中进行调用的:
public class Program { static void Main(string[] args) { Console.WriteLine(Compare<int>.CompareGeneric(3, 4)); Console.WriteLine(Compare<string>.CompareGeneric("abc", "a")); Console.Read(); } }
通过上面的代码大家肯定可以理解C# 2.0中为什么要引入泛型的,然而泛型可以给我们带什么好处的呢?从上面的例子可以看出,泛型可以帮助我们实现代码的重用,大家很清楚——面向对象中的继承也可以实现代码的重用,然而泛型提供的代码的重用,确切的说应该是 “算法的重用”(我理解的算法的重用是我们在实现一个方法中,我们只要去考虑如何去实现算法,而不需要考虑算法操作的数据类型的不同,这样的算法实现更好的重用,泛型就是提供这样的一个机制)。
然而泛型除了实现代码的重用的好处外,还有可以提供更好的性能和类型安全,下面通过下面一段代码来解释下为什么有这两个好处的。
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; namespace GeneralDemo { public class Program { static void Main(string[] args) { Stopwatch stopwatch = new Stopwatch(); // 非泛型数组 ArrayList arraylist = new ArrayList(); // 泛型数组 List<int> genericlist= new List<int>(); // 开始计时 stopwatch.Start(); for (int i = 1; i < 10000000; i++) { genericlist.Add(i); ////arraylist.Add(i); } // 结束计时 stopwatch.Stop(); // 输出所用的时间 TimeSpan ts = stopwatch.Elapsed; string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds/10); Console.WriteLine("运行的时间: " + elapsedTime); Console.Read(); } } }
当我们把 arraylist.Add(i);这行代码注释掉来测试向泛型数组中加入数据的运行时间,下面是我机器的上运行的一个截图:
当我们把 genericlist.Add(i);这行代码注释掉来测试向一个非泛型数组中加入数据的运行时间,下面附上我机器上的运行的时间截图:
从两个结果中就可以明显看出 向泛型数组中的加入数据的效率远高于非泛型数组。有图有真相,这样就充分说明泛型的另一个好处——高性能,然而泛型类型也保证了类型安全(大家都知道,C#是一个强类型的语言的,强类型指的是在每定义一个变量都需要指定变量的类型),当我们向这个泛型genericlist数组中添加string类型的值时,此时就会造成编译器报错 “无法从“string”转换为’int‘ 。
三、小结
本专题主要和大家分享了C# 2.0中为什么会引入委托,以及委托的好处,相信通过上面的介绍大家可以对委托有一个简单的认识以及对于泛型所带来的好处也有一个全面的认识,对于泛型的高性能本专题并没有给出原因,这个内容将会在下面一个专题向大家介绍。