泛型概念:

泛型是公共语言运行时和编程语言提供的一种机制,它支持另外1种形式的代码重用,也就是算法重用.

泛型包含泛型类,泛型接口,泛型委托,泛型方法,并非只包含简单的泛型类.在C#中,其他例如,属性,索引器,事件,操作符方法,构造器本身不能有类型参数T.

T(类型参数),它的目的在于指明它操作的是一个未指定的数据类型,在源代码中能够使用数据类型的地方都可以使用它.

 

为什么要使用泛型

通过使用泛型,可以极大地提高代码的重用度,同时还可以获得强类型的支持,提升了应用程序的性能,避免了隐式的装箱、拆箱,以及运行时的类型转换错误。

 

提高代码复用

冒泡算法

不用泛型的情况下,如果是整数类型排序,下面代码中“?“就应为int类型,但如果这时候我们有需要给byte类型排序,就要复制一遍代码,然后把int改为byte。这显然不能体现代码的复用。

public class SortHelper {
    public void BubbleSort(?[] array) {
        int length = array.Length;
        for (int i = 0; i <= length - 2; i++) {
            for (int j = length - 1; j >= 1; j--) {
                // 对两个元素进行交换
                if (array[j] < array[j - 1]) {
                    temp = array[j];
                    array[j] = array[j - 1];
                    array[j - 1] = temp;
                }
           }
    }
}

做网站为了提高代码复用,我们通常使用模板它的具体做法是:每次生成静态页面时,先加载模板,模板中含有一些用特殊字符标记的占位符,然后从数据库读取数据,使用读出的数据将模板中的占位符替换掉。

将上面的方法视为一个模板,将方法所操作的类型视为一个占位符,由于是一个占位符,因此可以代表任何的类型,这和静态页面生成时模板的占位符可以用来代表来自数据库中的任何数据道理是一样的。

public class SortHelper<T> {
    public void BubbleSort(T[] array){
        // 冒泡排序方法实现体
    }
}

在类型名称的后面加了一个尖括号,使用这个尖括号来传递占位符,也就是类型参数。

接下来,看一下如何使用泛型。

当需要为一个int类型的数组排序时:

 sorter = new SortHelper<int>();
int[] array = { 8, 1, 4, 7, 3 };
sorter.BubbleSort(array);
当需要为一个byte类型的数组排序时:

sorter = new SortHelper<byte>();
byte [] array = { 8, 1, 4, 7, 3 };
sorter.BubbleSort(array);

性能对比

拆装箱

1.   装箱在值类型向引用类型转换时发生

2.   拆箱在引用类型向值类型转换时发生

代码演示

 
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        const int ListSize = 500000;
        static void Main(string[] args)
        {
            UseArrayList();
            UseGenericList();
            Console.ReadKey();
        }
        private static void UseArrayList()
        {
            ArrayList list = new ArrayList();
            long startTicks = DateTime.Now.Ticks;
            for (int i = 0; i < ListSize; i++)
            {
                list.Add(i);//i为int类型,list为引用类型, 存在一个装箱操作
            }
            for (int i = 0; i < ListSize; i++)
            {
                int value = (int)list[i];//list[i]为引用类型,value为int类型。 进行一个拆箱操作
            }
            long endTicks = DateTime.Now.Ticks;
            Console.WriteLine("使用ArrayList,耗时:{0} ticks", endTicks - startTicks);
        }
        private static void UseGenericList()
        {
            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);
        }
    }
}
运行效果

类型安全

    
    
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];   // 编译错误

通过以上这些对比,可以看出泛型给我们编程提供了很大的方便,当然这些只是我查到的一些资料加上自己
的整理和实践,如有不足之处请大家批评指正。


Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐