泛型

.Net自从2.0版本开始就支持泛型。使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。

性能

            var list = new ArrayList();
            list.Add(20);//装箱--将值类型转换为引用类型

            int num = (int)list[0];//拆箱--将引用类型转换为值类型

            foreach (int i in list)//拆箱
            {
                Console.WriteLine(i);
            }

ArrayList存储对象,所以Add方法会进行装箱操作,当读取ArrayList中的值时,需要将其转换为int类型,需要进行拆箱操作。装箱、拆箱操作很容易使用,但是性能损失较大,特别是遍历许多项时。

此时,我们可以使用List<T>泛型类来减少装箱和拆箱操作,类型参数T定义为int类型

            var list = new List<int>();
            list.Add(20);//不进行装箱操作

            int num = list[0];//不进行拆箱操作

            foreach (int i in list)//不进行拆箱操作
            {
                Console.WriteLine(i);
            }

类型安全

1             var list = new ArrayList();
2             list.Add(20);
3             list.Add("abc");
4             foreach (int i in list)//集合中并非所有元素都可以强制转化为int类型,会出现一个运行异常
5             {
6                 Console.WriteLine(i);
7             }

泛型类List<T>中,参数类型T可以定义允许使用的数据类型

1             var list = new List<int>();//参数类型T设置为int类型,只允许对int类型的数值进行操作
2             list.Add(20);
3             list.Add("abc");//传入参数数据类型与设置的参数类型不符,会导致错误

代码复用性

我们看如下代码,为了实现对不同数据类型的数组进行遍历,我们需要为每种类型都编写一个方法:

 1     class Generic
 2     {
 3         public void Main()
 4         {
 5             int[] arr_int = { 1, 2, 3 };
 6             string[] arr_string = { "abc", "123" };
 7             char[] arr_char = { 'a', 'b', 'c' };
 8             PrintArray(arr_int);
 9             PrintArray(arr_string);
10             PrintArray(arr_char);
11 
12             Console.ReadKey();
13         }
14         //遍历int类型的数组
15         void PrintArray(int[] arr)
16         {
17             foreach (int i in arr)
18             {
19                 Console.WriteLine(i);
20             }
21         }
22         //遍历string类型的数组
23         void PrintArray(string[] arr)
24         {
25             foreach (string str in arr)
26             {
27                 Console.WriteLine(str);
28             }
29         }
30         //遍历char类型的数组
31         void PrintArray(char[] arr)
32         {
33             foreach (char ch in arr)
34             {
35                 Console.WriteLine(ch);
36             }
37         }
38     }

但是如果我们使用泛型的话,代码就简单多了

 1     class Generic
 2     {
 3         public void Main()
 4         {
 5             int[] arr_int = { 1, 2, 3 };
 6             string[] arr_string = { "abc", "123" };
 7             char[] arr_char = { 'a', 'b', 'c' };
 8             //完整的写法
 9             PrintArray<int>(arr_int);
10             PrintArray<string>(arr_string);
11             PrintArray<char>(arr_char);
12             //简便写法
13             PrintArray(arr_int);
14             PrintArray(arr_string);
15             PrintArray(arr_char);
16 
17             Console.ReadKey();
18         }
19         //遍历数组
20         void PrintArray<T>(T[] arr)
21         {
22             foreach (T item in arr)
23             {
24                 Console.WriteLine(item);
25             }
26         }
27     }

命名约定

如果在程序中使用了泛型,遵循泛型的命名规则可以帮助我们区分泛型类型和非泛型类型。

泛型类型的命名规则:

  • 泛型类型的名称用字母T作为前缀;
1     class TClassName<T>
2     {
3         //...
4     }
  • 如果没有特殊的要求,泛型类型允许用任意的类型代替,并且只使用了一个泛型类型,就可以用T作为泛型类型的名称;
  • 如果泛型类型有特定的要求的(例如,他必须实现一个接口或派生自基类),或者使用了两个或多个泛型类型,就应该为泛型类型使用描述性的名称
1     class TClassName<TParam1, Tparam2>
2     {
3         //...
4     }

泛型类的默认值

我们看下边的代码,我们需要将变量value进行初始化,要求如果value是引用类型就初始化为null,如果value是值类型就初始化为0;但是泛型中并不能确定T的类型是值类型或是引用类型,那我们要如何进行初始化呢?为了解决这个问题,我们可以是会用default关键字。default关键字会根据T的类型进行初始化,如果T为引用类型就将其设置为null,如果T为值类型就将其设置为0。

1         public T Test<T>()
2         {
3             T value = default(T);
4 //..Do Something
5 return value;
6 }

 约束

约束 说明
where T:struct 指定类型T必须是值类型
where T:class 指定类型T必须是引用类型
where T:IFoo 指定类型T必须实现接口IFoo
where T:Foo 指定类型T必须派生自基类Foo
where T:new() 构造函数约束,指定类型T必须有一个默认的无参构造函数(构造函数只能对无参的构造函数进行约束)
where T1:T2 类型T1派生自泛型类型T2
posted @ 2017-07-23 14:04  CS讷于言而敏于行  阅读(265)  评论(0编辑  收藏  举报