• 【基础】泛型的简单理解


    前言

    最近工作不是很忙,抽出时间来看看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

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

  • 相关阅读:
    php mysql与mysqli 区别
    让IT工作者过度劳累的12个坏习惯
    HR不懂爱,他的问题答不上来
    励志小故事
    php中如何把一个数字字符串作为一个变量
    新手应该具备的基本素质
    邮件中的缩写
    强大的网页性能测试工具Speed Tracer
    Windows环境下EditPlus运行Perl程序
    ActionScript3检测当前下载资源的速度
  • 原文地址:https://www.cnblogs.com/xhb-bky-blog/p/5200344.html
Copyright © 2020-2023  润新知