C# -- 泛型(1)

来源:互联网 发布:野风 林忆莲 知乎 编辑:程序博客网 时间:2024/05/22 01:29

简介:

  先看看泛型的概念--“通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用”。

  很多初学者在刚开始接触泛型的时候会比较难理解 “泛型” 在这里先把 “泛型”当作一个形容词 这样比较方便理解 因为很多东西都可以是泛型的 比如--

泛型的类” ,“泛型的方法”,“泛型的接口”,“泛型的委托” 等...很多时候我们使用泛型可以极大减少代码重复使程序更加清爽,也可以避免不必要的‘装箱’ ‘拆箱’过程。

 

 

<泛型的引入|为什么要有泛型?>

 

  在程序设计的过程中我们常常会遇到这样的情况:为了实现某一个功能我们一开始把方法写好,但后来我们发现同样的功能需要我们再写一次但是这次方法的参数类型和上次不一样了,这个时候按照敏捷软件开发的思想,不要过早的进行抽象和应对变化,当变化第一次出现时,使用最快的方法解决它,但变化第二次出现的时,在进行更好的架构设计,这样的目的是为了避免过度设计,因为有可能第二次变化永远也不会出现。考虑到功能一样,所这里我们通常会直接复制原方法的代码,然后修改一下参数类型即可快速解决;这样做确实没错,但是有的时候不仅出现了第二次变化 还出现了第三次...或者是更多次变化,继续使用CV大法修改方法的签名将会导致大量重复代码的出现,于是我们就会想,要是存在一个可以传递任何数据类型的方法那多好,即把这个方法的实现当成模板 把方法的签名抽象出来,于是我们引入了泛型。

 

下面我们来看一下具体的例子:

 

1.1使用CV大法

---------------输入多个 int类型,进行冒泡排序让它们依次重小到大输出,代码如下:

复制代码
    public class SortHelper    {        public void BubbleSort(int[] arr)        {            int length = arr.Length;            for (int i = 0; i < length-1; i++)            {                for (int j = 0; j < length-1-i; j++)                {                    if (arr[j]>arr[j+1])                    {                        int temp=arr[j];                        arr[j] = arr[j + 1];                        arr[j + 1] = temp;                    }                }            }        }    }
复制代码

 

测试:

复制代码
static void Main(string[] args)        {            SortHelper sorter = new SortHelper();            int[] a = { 4,5,1,3,2,8,5,0,2};            sorter.BubbleSort(a);
       //输出省略
}
复制代码

 

输出为:0,1,2,2,3,4,5,5,8

---------------输入多个 Byte类型,进行冒泡排序让它们依次重小到大输出,代码如下:

这个时候我只要复制一下原来的方法改一下签名就可以了

复制代码
    public class SortHelper    {        public void BubbleSort(byte[] arr)        {            int length = arr.Length;            for (int i = 0; i < length-1; i++)            {                for (int j = 0; j < length-1-i; j++)                {                    if (arr[j]>arr[j+1])                    {                        byte temp = arr[j];                        arr[j] = arr[j + 1];                        arr[j + 1] = temp;                    }                }            }        }    }
复制代码

 

这样做虽然可以,但是往后若要 处理N次各种其他 数据类时就 就要大量重复复制 严重影响代码的简洁度,而且当功能要扩展时 ,每个方法都要修改,维护起来非常不方便。

 

1.2使用泛型(泛型类):

我们自然而然的会这样想了如果可以把方法中的 参数类型 用一个 ”占位符“ 表示 每次 传入 什么类型 他就变成什么类型,这样就可以将这个方法当成一个模板用了(有点像Web编程中在Html中使用占位符)。

这里我们用 “T” 来便是这个特殊的参数类型,于是代码就变成了这样:

复制代码
    public class SortHelper    {        public void BubbleSort(T[] arr)        {            int length = arr.Length;            for (int i = 0; i < length-1; i++)            {                for (int j = 0; j < length-1-i; j++)                {                    if (arr[j]>arr[j+1])                    {                        T temp = arr[j];                        arr[j] = arr[j + 1];                        arr[j + 1] = temp;                    }                }            }        }    }
复制代码

 

这里 T 代表 ”类型的类型“ 和 int ,string ...等数据类型相似,T 就是类型本身。让人兴奋的是真的有像 “T” 这样的特别存在,在.NET中叫做类型参数. 下面我们看看规范的代码--

这里我们把BubbleSort定义成泛型类 定义泛型类的一种方法是在类后面加上“<T>” 

复制代码
    //定义泛型类SortHelper 这里“where T:IComparable” 是给类型参数T一个限制 -- 参数类型必须实现IComparable接口,否则无法通过编译    public class SortHelper<T> where T:IComparable    {        public void BubbleSort(T[] arr)        {            int length = arr.Length;            for (int i = 0; i < length-1; i++)            {                for (int j = 0; j < length-1-i; j++)                {                    if (arr[j].CompareTo(arr[j+1])>0)                    {                        T temp = arr[j];                        arr[j] = arr[j + 1];                        arr[j + 1] = temp;                    }                }            }        }    }
复制代码

 

测试:

复制代码
        static void Main(string[] args)        {            SortHelper<byte> sorter = new SortHelper<byte>();            byte[] a = { 4,5,1,3,2,8,5,0,2};            sorter.BubbleSort(a);            SortHelper<int> sorter1 = new SortHelper<int>();            int[] b = { 4, 5, 1, 3, 2, 8, 5, 0, 2 };            sorter1.BubbleSort(b);       //输出省略        }
复制代码

 

输出为:

0,1,2,2,3,4,5,5,8

0,1,2,2,3,4,5,5,8

 

---------------输入多个 自定义类型的实例,进行冒泡排序让它们依次重小到大输出,代码如下:

下面我们来模拟一下宠物店卖的猫 按价格排序

猫类:

复制代码
    public class cat:IComparable    {        public string name;        public int price;        public int CompareTo(object obj)        {            cat catT = (cat)obj;            return this.price.CompareTo(catT.price);        }        public cat(string name, int price)        {            this.price = price;            this.name = name;        }    }
复制代码

 

测试:

复制代码
        static void Main(string[] args)        {            SortHelper<cat> sorter2 = new SortHelper<cat>();            cat cat1=new cat("猫1",1000);            cat cat2=new cat("猫2",1400);            cat cat3=new cat("猫3",400);            cat[] c = { cat1, cat2, cat3 };            sorter2.BubbleSort(c);            //输出            for (int i = 0; i < c.Length; i++)            {                Console.WriteLine("Name:"+c[i].name+" Price:"+c[i].price);            }        }    
复制代码

 

结果如图:

 

 

 

 

 

*泛型与集合类型(ArrayList)

 

 概要:通过泛型可以大大提高集合类型的的性能恶化安全性。

下面我们来看一个例子

2.1 非泛型的集合类

先是 往集合里 存放 3 个数据 

复制代码
            ArrayList list = new ArrayList();            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);            }
复制代码

 

 

测试:

输出

0

1

2

有经验的读者在这里可能会注意到了,这样子写虽然能运行通过,但是这里当 list每次调用Add方法时就做了一次 ” 装箱 “ 操作,接着每次取数据时对list的元素进行一次强制转换 (int)list[i] 同时也做了一次 “ 拆箱 ”操作,这两个操作对.NET来说是比较耗时的,当操作的次数越多效果就越明显;

 

2.2下面我们将 listSize 设置成 1000000 然后用 开始和结束DateTime.Now 来获取消耗的时间:

复制代码
            ArrayList list = new ArrayList();            int listSize = 1000000;            long StarTime = 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 EndTime = DateTime.Now.Ticks;            Console.WriteLine("使用ArrayList,耗时:{0} Ticks", EndTime - StarTime);
复制代码

 

 

测试:

结果

 

2.3 使用泛型集合类型(泛型数组)

复制代码
            List<int> list = new List<int>();            int listSize = 1000000;            long StarTime = 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 EndTime = DateTime.Now.Ticks;            Console.WriteLine("使用List<int>,耗时:{0} Ticks", EndTime - StarTime);
复制代码

 

测试:

 比较上述2次执行的结果我们会发现,使用 ArrayList 的耗时是使用 List<int> 的2倍多 ,随着次数的增大差距会越来越明显!

 

总结:

看到这里相信大家明白为什么要引入泛型了吧,通过使用泛型-

1.可以避免同种功能代码的大幅度重复出现使我们的代码更加简洁/可读性更高

2.方便扩展维护,灵活度高

3.避免隐式的装箱拆箱,提高程序运行速度

 

0 0