zoukankan      html  css  js  c++  java
  • 【基础】泛型的简单理解

    前言

    最近工作不是很忙,抽出时间来看看C#中基础的东西,也算是“温故而知新”了,于是就看到了泛型这块儿,看了园子里其他园友的文章,讲的都很到位。这篇文章本着简单、容易理解为前提,记录下我自己对泛型的认识,方便以后查看。

    泛型是什么

    泛型是一种开放式类型,它的出现保证了我们可以创建类型安全的集合。

    泛型的应用场景

    <1>当一种逻辑适用于多种数据类型时,可以采用泛型,简化代码,提高代码的复用性。

    <2>避免不必要的装箱(Boxing)和拆箱(Unboxing)操作。

    <3>避免不必要的类型强制转换。

    下面通过一个自定义的栈,来对上面的应用场景进行解释。

    首先定义一个栈,该栈有一个含参构造函数,有三个方法,元素的入栈Push、元素的出栈Pop、元素的输出Out。代码如下:

     1 /// <summary>
     2 /// 自定义栈
     3 /// </summary>
     4 /// <typeparam name="?"></typeparam>
     5 class Stack
     6 {
     7     private int[] arr;
     8     private int length = 0;
     9     private int topElement;
    10 
    11     /// <summary>
    12     /// 设置或获取栈顶元素
    13     /// </summary>
    14     public int TopElement
    15     {
    16         get
    17         {
    18             this.topElement= arr[length-1];
    19             return this.topElement;
    20         }
    21         set
    22         {
    23             this.topElement = value;
    24         }
    25     }
    26 
    27     /// <summary>
    28     /// 栈中已存在元素数量
    29     /// </summary>
    30     public int Length
    31     {
    32         get { return this.length; }
    33     }
    34 
    35     /// <summary>
    36     /// 构造函数
    37     /// </summary>
    38     /// <param name="m"></param>
    39     public Stack(int m)
    40     {
    41         this.arr = new int[m];
    42     }
    43 
    44     /// <summary>
    45     /// 将栈顶元素出栈
    46     /// </summary>
    47     /// <returns></returns>
    48     public int Pop()
    49     {
    50         if (this.length <= 0)
    51             return -1;
    52         else
    53         {
    54             int popElement = this.topElement;
    55             arr[this.length-1] = 0;
    56             this.length = this.length - 1;
    57             return popElement;
    58         }
    59     }
    60 
    61     /// <summary>
    62     /// 将元素入栈
    63     /// </summary>
    64     /// <param name="item">目标元素</param>
    65     public void Push(int item)
    66     {
    67         if (length >= this.arr.Length) return;
    68         this.arr[length] = item;
    69         this.topElement = this.arr[length];
    70         this.length = this.length + 1;
    71     }
    72 
    73     /// <summary>
    74     /// 输出栈内所有元素
    75     /// </summary>
    76     /// <param name="stack">目标栈</param>
    77     public void Out(Stack stack)
    78     {
    79         if (stack.arr.Length <= 0) return;
    80         foreach (int item in arr)
    81         {
    82             Console.WriteLine(item);            
    83         }
    84         Console.ReadLine();
    85     }
    86 }

    以上代码可以正常运行,实现了一个栈的基本功能,但是我想把这个逻辑放在string类型上也通用,怎么办?有人说那好办,把int替换成string就好咯,代码如下:

     1 /// <summary>
     2 /// 自定义栈
     3 /// </summary>
     4 /// <typeparam name="?"></typeparam>
     5 class StringStack
     6 {
     7     private string[] arr;
     8     private int length = 0;
     9     private string topElement;
    10 
    11     /// <summary>
    12     /// 设置或获取栈顶元素
    13     /// </summary>
    14     public string TopElement
    15     {
    16         get
    17         {
    18             this.topElement = arr[length - 1];
    19             return this.topElement;
    20         }
    21         set
    22         {
    23             this.topElement = value;
    24         }
    25     }
    26 
    27     /// <summary>
    28     /// 栈中已存在元素数量
    29     /// </summary>
    30     public int Length
    31     {
    32         get { return this.length; }
    33     }
    34 
    35     /// <summary>
    36     /// 构造函数
    37     /// </summary>
    38     /// <param name="m"></param>
    39     public StringStack(int m)
    40     {
    41         this.arr = new string[m];
    42     }
    43 
    44     /// <summary>
    45     /// 将栈顶元素出栈
    46     /// </summary>
    47     /// <returns></returns>
    48     public string Pop()
    49     {
    50         if (this.length <= 0)
    51             return "Empty";
    52         else
    53         {
    54             string popElement = this.topElement;
    55             arr[this.length - 1] = string.Empty;
    56             this.length = this.length - 1;
    57             return popElement;
    58         }
    59     }
    60 
    61     /// <summary>
    62     /// 将元素入栈
    63     /// </summary>
    64     /// <param name="item">目标元素</param>
    65     public void Push(string item)
    66     {
    67         if (length >= this.arr.Length) return;
    68         this.arr[length] = item;
    69         this.topElement = this.arr[length];
    70         this.length = this.length + 1;
    71     }
    72 
    73     /// <summary>
    74     /// 输出栈内所有元素
    75     /// </summary>
    76     /// <param name="stack">目标栈</param>
    77     public void Out(StringStack stack)
    78     {
    79         if (stack.arr.Length <= 0) return;
    80         foreach (string item in arr)
    81         {
    82             Console.WriteLine(item);
    83         }
    84         Console.ReadLine();
    85     }
    86 }

    经过测试发现,上面的栈也可以正常工作了,但是问题又来了,我想把这个栈的逻辑再应用到long类型上怎么办?聪明的人不会在同一个问题上绊倒三次,既然有那么多类型需要用到这个栈的逻辑,那我索性就给他来个Object,这下总可以了吧~于是Object类型的栈横空出世,代码如下:

     1 /// <summary>
     2 /// 自定义栈
     3 /// </summary>
     4 /// <typeparam name="?"></typeparam>
     5 class ObjectStack
     6 {
     7     private object[] arr;
     8     private int length = 0;
     9     private object topElement;
    10 
    11     /// <summary>
    12     /// 设置或获取栈顶元素
    13     /// </summary>
    14     public object TopElement
    15     {
    16         get
    17         {
    18             this.topElement = arr[length - 1];
    19             return this.topElement;
    20         }
    21         set
    22         {
    23             this.topElement = value;
    24         }
    25     }
    26 
    27     /// <summary>
    28     /// 栈中已存在元素数量
    29     /// </summary>
    30     public int Length
    31     {
    32         get { return this.length; }
    33     }
    34 
    35     /// <summary>
    36     /// 构造函数
    37     /// </summary>
    38     /// <param name="m"></param>
    39     public ObjectStack(int m)
    40     {
    41         this.arr = new object[m];
    42     }
    43 
    44     /// <summary>
    45     /// 将栈顶元素出栈
    46     /// </summary>
    47     /// <returns></returns>
    48     public object Pop()
    49     {
    50         if (this.length <= 0)
    51             return null;
    52         else
    53         {
    54             object popElement = this.topElement;
    55             arr[this.length - 1] = null;
    56             this.length = this.length - 1;
    57             return popElement;
    58         }
    59     }
    60 
    61     /// <summary>
    62     /// 将元素入栈
    63     /// </summary>
    64     /// <param name="item">目标元素</param>
    65     public void Push(object item)
    66     {
    67         if (length >= this.arr.Length) return;
    68         this.arr[length] = item;
    69         this.topElement = this.arr[length];
    70         this.length = this.length + 1;
    71     }
    72 
    73     /// <summary>
    74     /// 输出栈内所有元素
    75     /// </summary>
    76     /// <param name="stack">目标栈</param>
    77     public void Out(ObjectStack stack)
    78     {
    79         if (stack.arr.Length <= 0) return;
    80         foreach (object item in arr)
    81         {
    82             Console.WriteLine(item);
    83         }
    84         Console.ReadLine();
    85     }
    86 }

    经过重构之后,这个栈逻辑可以适用于任何类型,这是毫无疑问的,但是还有一个问题,来看调用的代码:

     1 static void Main(string[] args)
     2 {
     3     ObjectStack ostd = new ObjectStack(3);
     4     ostd.Push(true);
     5     ostd.Push("http://www.cnblogs.com/xhb-bky-blog/");
     6     ostd.Push(12);
     7 
     8     int a = (int)ostd.Pop();
     9     string b = ostd.Pop().ToString();
    10     bool c = (bool)ostd.Pop();
    11     Console.ReadLine();
    12 }
    13         

    在调用代码中,我们居然看到了装箱和拆箱操作,而装箱操作和拆箱操作是要额外耗费cpu和内存资源的。再来看下面一段代码:

     1         static void Main(string[] args)
     2         {            
     3             ObjectStack ostd = new ObjectStack(3);
     4             Good good1 = new Good("pencil", 5);
     5             Good good2 = new Good("eraser", 2);
     6             Good good3 = new Good("pencilbox", 15);
     7             
     8             ostd.Push(good1);
     9             ostd.Push(good2);
    10             ostd.Push(good3);
    11 
    12             Good mygood = (Good)ostd.Pop();//注意
    13             Console.ReadLine();
    14         }

    上面的代码中发生了类型强制转换,这表明该段代码是不安全的,虽然可以通过编译,但是如果类型对应不上,就会把错误推迟到运行期。为了解决上面的三个问题,泛型应运而生,来看一下泛型是如何解决这些问题的,看代码:

     1 /// <summary>
     2 /// 自定义栈
     3 /// </summary>
     4 /// <typeparam name="T"></typeparam>
     5 class Stack<T> where T:IComparable
     6 {
     7     private T[] arr;
     8     private int length = 0;
     9     private T topElement;
    10 
    11     /// <summary>
    12     /// 设置或获取栈顶元素
    13     /// </summary>
    14     public T TopElement
    15     {
    16         get
    17         {
    18             this.topElement = arr[length - 1];
    19             return this.topElement;
    20         }
    21         set
    22         {
    23             this.topElement = value;
    24         }
    25     }
    26 
    27     /// <summary>
    28     /// 栈中已存在元素数量
    29     /// </summary>
    30     public int Length
    31     {
    32         get { return this.length; }
    33     }
    34 
    35     /// <summary>
    36     /// 构造函数
    37     /// </summary>
    38     /// <param name="m"></param>
    39     public Stack(int m)
    40     {
    41         this.arr = new T[m];
    42     }
    43 
    44     /// <summary>
    45     /// 将栈顶元素出栈
    46     /// </summary>
    47     /// <returns></returns>
    48     public T Pop()
    49     {
    50         //T t = new T();//必须声明new
    51         if (this.length <= 0)
    52             return default(T);
    53         else
    54         {
    55             T popElement = this.topElement;
    56             arr[this.length - 1] = default(T);
    57             this.length = this.length - 1;
    58             return popElement;
    59         }
    60     }
    61 
    62     /// <summary>
    63     /// 将元素入栈
    64     /// </summary>
    65     /// <param name="item">目标元素</param>
    66     public void Push(T item)
    67     {
    68         if (length >= this.arr.Length) return;
    69         this.arr[length] = item;
    70         this.topElement = this.arr[length];
    71         this.length = this.length + 1;
    72     }
    73 
    74     /// <summary>
    75     /// 输出栈内所有元素
    76     /// </summary>
    77     /// <param name="stack">目标栈</param>
    78     public void Out(Stack<T> stack)
    79     {
    80         if (stack.arr.Length <= 0) return;
    81         foreach (T item in arr)
    82         {
    83             Console.WriteLine(item);
    84         }
    85         Console.ReadLine();
    86     }
    87 }

    在泛型中,类名后面的Stack<T>中的T表示它操作的是一个未指定的数据类型,也称为开放式类型,因为T是一个不明确的类型,在实例化的时候才能知道T的实际类型是什么,实例化之后也就变成了封闭式类型,如Stack<String>,因为Stack<String>已经明确了T是String类型的。另外一点,Stack<String>和Stack<T>没有任何继承上的关系,而是两种完全独立的类型。下面来看下泛型的神奇之处吧:

    static void Main(string[] args)
    {
        Stack<int> std = new Stack<int>(2);
        std.Push(21);
        std.Push("Hello");//编译不通过,保证了类型安全
        int a = std.Pop();//未发生拆装箱
    
        Stack<Good> stdg = new Stack<Good>(2);
        Good sell = new Good("box", 10);
        stdg.Push(sell);
        Good buy = stdg.Pop();//未发生强制类型转换
        Console.ReadLine();
    }

    总结

    以上是泛型的简单应用,实际应用中,泛型还有很多其他的特性,如泛型的约束、泛型的方法、接口、委托以及继承等。要理解这些特性,需要把基础打好,熟练掌握泛型的应用场景,才能事半功倍。

     作者:悠扬的牧笛

     博客地址:http://www.cnblogs.com/xhb-bky-blog/p/4167108.html

     声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 相关阅读:
    个人冲刺二(7)
    个人冲刺二(6)
    个人冲刺二(5)
    个人冲刺二(4)
    对称二叉树 · symmetric binary tree
    108 Convert Sorted Array to Binary Search Tree数组变成高度平衡的二叉树
    530.Minimum Absolute Difference in BST 二叉搜索树中的最小差的绝对值
    pp 集成工程师 mism师兄问一问
    17. Merge Two Binary Trees 融合二叉树
    270. Closest Binary Search Tree Value 二叉搜索树中,距离目标值最近的节点
  • 原文地址:https://www.cnblogs.com/xhb-bky-blog/p/5200344.html
Copyright © 2011-2022 走看看