泛型是.net 2.0就有的特性,泛型在我们的平常的开发过程中使用得也非常多,为了更深刻地理解泛型,这篇文章就来总结一下。

什么是泛型

可以用下面2点来概括:
1,首先泛型也是一种类型(可以通过IL代码看出来)。
2,泛型提供了类型参数化的能力,允许用不同的类型进行实例化,可以简单地理解为:泛型是类型的模板。
 

为什么要使用泛型

下面通过一个demo来演示不使用泛型和使用泛型的实现,进而理解为什么要使用泛型。

 不使用泛型:

 1 /// <summary>
 2 /// 普通方法,使用ArrayList
 3 /// </summary>
 4 public static void MyNoGenericMethod()
 5 {
 6     ArrayList arrayList = new ArrayList();
 7     arrayList.Add(1);//产生装箱
 8     arrayList.Add(2);//产生装箱
 9     arrayList.Add("3");//编译时没有提示错误
10 
11     var result = 0;
12     foreach (int item in arrayList)
13     {
14         result += item;//运行时错误
15     }
16 }

 

使用泛型:

 1 /// <summary>
 2 /// 泛型,使用List<T>
 3 /// </summary>
 4 /// <returns></returns>
 5 public static void MyGenericMethod()
 6 {
 7     List<int> list = new List<int>();
 8     list.Add(1);
 9     list.Add(2);
10     //list.Add("3");//编译时就提示错误
11 
12     var result = 0;
13     foreach (int item in list)
14     {
15         result += item;
16     }
17 }

 

从上面这个例子可以看到,使用泛型一是保证了类型安全,二是提高了性能,因为避免了装箱和拆箱。

 

我们再来看一个例子,实现了一个int类型的栈,如下代码:

 1 /// <summary>
 2 /// int类型的Stack
 3 /// </summary>
 4 public class MyIntStack
 5 {
 6     int stackPointer = 0;
 7     int[] stackArray;
 8 
 9     /// <summary>
10     /// 入栈
11     /// </summary>
12     /// <param name="x"></param>
13     public void Push(int x)
14     { 
15         
16     }
17 
18     /// <summary>
19     /// 出栈
20     /// </summary>
21     /// <returns></returns>
22     public int Pop()
23     { 
24         
25     }
26 }

 

假如要将数据类型改成float,就需要将代码copy一份并且将数据类型改成float,这样代码就重复了,时间长了很难维护,那有没有一种方法不需要copy代码呢?

假如引入了泛型,就不需要重复copy代码了,如下代码:

 1 /// <summary>
 2 /// 泛型的stack
 3 /// </summary>
 4 /// <typeparam name="T"></typeparam>
 5 public class MyStack<T>
 6 {
 7     int stackPointer = 0;
 8     T[] stackArray;
 9 
10     /// <summary>
11     /// 入栈
12     /// </summary>
13     /// <param name="x"></param>
14     public void Push(T x)
15     {
16 
17     }
18 
19     /// <summary>
20     /// 出栈
21     /// </summary>
22     /// <returns></returns>
23     public T Pop()
24     {
25 
26     }
27 }

 

这样,下次再换成double类型都没有问题,所以这里体现了泛型带来的,代码复用的好处。

所以,综合上面两个例子,我们可以总结出,使用泛型可以带来以下好处:

1,代码(算法)复用。

2,类型安全。

3,提高性能,避免了装箱和拆箱。

4,扩展性。

泛型的本质

反编译上面示例的IL代码,如下图:

从上图的IL代码可以看出:

1,泛型其实也是一个类(class)。

2,泛型类型生成IL代码后跟普通类型的区别是,使用了占位符'<T>'。 

泛型类型 

声明泛型类型跟声明普通类型差不多,不同的地方在于,泛型在类名后面放一对尖括号,并且使用了类型参数。
泛型类型的创建过程如下图。

 

声明泛型类型:
 1 /// <summary>
 2 /// 声明泛型类
 3 /// </summary>
 4 /// <typeparam name="T1"></typeparam>
 5 /// <typeparam name="T2"></typeparam>
 6 public class MyGenericClass<TRequest, TResponse>
 7     where TRequest : class
 8     where TResponse : class
 9 {
10 
11 }

 创建构造类型和实例:

1 //创建构造类型和实例
2 var item = new MyGenericClass<Request, Response>();

 

完整代码:
 1 namespace ConsoleApplication11
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //创建构造类型和实例
 8             var item = new MyGenericClass<Request, Response>();
 9 
10         }
11     }
12 
13     /// <summary>
14     /// 声明泛型类
15     /// </summary>
16     /// <typeparam name="T1"></typeparam>
17     /// <typeparam name="T2"></typeparam>
18     public class MyGenericClass<TRequest, TResponse>
19         where TRequest : class
20         where TResponse : class
21     {
22 
23     }
24 
25     public class Request
26     { 
27         
28     }
29 
30     public class Response
31     { 
32         
33     }
34 }
View Code

 

泛型方法

泛型方法可以在泛型和非泛型类以及结构和接口中声明,如下图。

1,声明泛型方法

声明泛型方法要注意两点,封闭在圆括号里的方法参数,和封闭在尖括号里的类型参数,并且可以在方法参数后面放可选的约束子句。

如下代码:

1 /// <summary>
2 /// 泛型方法
3 /// </summary>
4 /// <typeparam name="T">类型参数</typeparam>
5 /// <param name="input">方法参数</param>
6 public static void MyGenericMethod<T>(T input) where T : class
7 { 
8     
9 }

 

2,调用泛型方法

1 var rq= new Request();
2 MyGenericMethod<Request>(rq);//原始调用

 

3,推断类型

因为编译器可以从我们传入的方法参数中推断出类型参数,所以有了推断类型我们的调用更简单,如下代码:

1 MyGenericMethod(rq);//推断类型

 

完整代码如下:

 1 namespace ConsoleApplication13
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //泛型方法
 8             var rq= new Request();
 9             MyGenericMethod<Request>(rq);//原始调用
10 
11             MyGenericMethod(rq);//推断类型
12 
13         }
14 
15         /// <summary>
16         /// 泛型方法
17         /// </summary>
18         /// <typeparam name="T">类型参数</typeparam>
19         /// <param name="input">方法参数</param>
20         public static void MyGenericMethod<T>(T input) where T : class
21         { 
22             
23         }
24 
25     }
26 
27     public class Request
28     { 
29         
30     }
31 }
View Code

泛型接口

泛型接口的声明跟普通接口的声明差不多,不同的地方在于,泛型接口需要在接口名称之后的尖括号中有类型参数。
1,声明泛型接口
如下代码:
1 /// <summary>
2 /// 泛型接口
3 /// </summary>
4 /// <typeparam name="T">类型参数</typeparam>
5 public interface IMyGenericInterface<T>
6 {
7     void ReturnIt(T input);
8 
9 }

 2,实现泛型接口

 1 /// <summary>
 2 /// 泛型接口实现
 3 /// </summary>
 4 /// <typeparam name="T"></typeparam>
 5 public class MyGenericInterfaceImpl<T> : IMyGenericInterface<T> where T:class
 6 {
 7     public void ReturnIt(T input)
 8     { 
 9         
10     }
11 }

 3,完整调用

 1 namespace ConsoleApplication14
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var rq = new Request();
 8             var m = new MyGenericInterfaceImpl<Request>();
 9             m.ReturnIt(rq);
10 
11         }
12     }
13 
14     /// <summary>
15     /// 泛型接口
16     /// </summary>
17     /// <typeparam name="T">类型参数</typeparam>
18     public interface IMyGenericInterface<T>
19     {
20         void ReturnIt(T input);
21 
22     }
23 
24     /// <summary>
25     /// 泛型接口实现
26     /// </summary>
27     /// <typeparam name="T"></typeparam>
28     public class MyGenericInterfaceImpl<T> : IMyGenericInterface<T> where T:class
29     {
30         public void ReturnIt(T input)
31         { 
32             
33         }
34     }
35 
36     public class Request
37     { 
38         
39     }
40 }
View Code 

泛型委托

泛型委托的声明跟普通委托的声明也差不多,不同的点在于,泛型委托包括委托形参和类型参数。
1,声明泛型委托
如下代码:
 1 namespace ConsoleApplication15
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //调用泛型委托
 8             var myGenericDel = new MyGenericDel<string>(Simple.PrintString);
 9             myGenericDel += Simple.PrintUpperString;
10 
11             myGenericDel("Hello generic delegate!");
12         }
13     }
14 
15     /// <summary>
16     /// 声明泛型委托
17     /// </summary>
18     /// <typeparam name="T"></typeparam>
19     /// <param name="input"></param>
20     public delegate void MyGenericDel<T>(T input);
21 
22     public class Simple
23     {
24         /// <summary>
25         /// 声明方法匹配委托
26         /// </summary>
27         /// <param name="input"></param>
28         public static void PrintString(string input)
29         {
30             Console.WriteLine(input);
31         }
32 
33         /// <summary>
34         /// 声明方法匹配委托
35         /// </summary>
36         /// <param name="input"></param>
37         public static void PrintUpperString(string input)
38         {
39             Console.WriteLine(input.ToUpper());
40         }
41 
42     }
43 }

 

2,Linq与泛型委托
说起泛型委托,就不得不提Linq中的泛型委托,.net 3.5为我们带来了Linq和lambda表达式。
这三个泛型委托在linq中经常用到。
 
1)Action委托
action表示无返回值的泛型委托。
如下代码:
1 /// <summary>
2 /// Action泛型委托
3 /// </summary>
4 /// <typeparam name="T"></typeparam>
5 /// <param name="action"></param>
6 public static void TestAction<T>(Action<T> action, T input)
7 {
8     action(input);
9 }

 2)Func委托

func表示有返回值的泛型委托。
如下代码:
1 //Func委托
2 var result = list.Where(p => p < 10).ToList();

 3)Predicate委托 

predicate表示返回bool值的泛型委托。
如下代码:
1 //Predicate委托
2 var result2 = list.Find(p => p == 100);

 

完整代码:

 1 namespace ConsoleApplication16
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //Linq与泛型委托
 8             var list = Enumerable.Range(0, 1000).ToList();
 9 
10             //Action委托
11             TestAction<int>(p => Console.WriteLine(p), 10);
12 
13             //Func委托
14             var result = list.Where(p => p < 10).ToList();
15 
16             //Predicate委托
17             var result2 = list.Find(p => p == 100);
18 
19             Console.ReadKey();
20         }
21 
22         /// <summary>
23         /// Action泛型委托
24         /// </summary>
25         /// <typeparam name="T"></typeparam>
26         /// <param name="action"></param>
27         public static void TestAction<T>(Action<T> action, T input)
28         {
29             action(input);
30         }
31 
32     }
33 }

泛型结构

//不是很常用,暂时不总结。