• 泛型


      泛型是.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 }

    泛型结构

    //不是很常用,暂时不总结。
     
  • 相关阅读:
    数据库(一)数据库优点,常用数据库,创建数据库,设计创建数据表,数据类型,数据表操作,完整性约束(非空,主键,唯一,默认,外键)
    mybatis 中mapper文件 if判断 <if test="validCoupon == '1'">类似问题
    如何解决JavaScript中UUID作为方法参数在方法中无法传递而数字却正常传递的问题
    js 将子页面得到的数据返回并赋值给父页面
    在IE下面报错"缺少函数",函数明明是有的,其他浏览器下正常
    javaPOI把excel转换成html 中去掉序号列
    linux 常用命令(个人用)
    mysql 统计 group
    es 复制索引
    elasticsearch 判断某个字段是否含有大写字母
  • 原文地址:https://www.cnblogs.com/mcgrady/p/7007685.html
Copyright © 2020-2023  润新知