泛型程式設計詳解(一)

小世界的野孩子發表於2019-07-24

前言

  泛型是C#和.Net的一個重要概念,泛型不僅是C#程式語言中的一部分,而且與程式集中的IL(Intermediate Language)程式碼緊密的整合。

  在平時程式設計過程中,常常會出現編寫一些差不多的程式碼塊,不同的僅是處理不同的資料型別。比如一個處理int資料的方法,現在新加了string型別的資料。是不是把之前的方法複製一遍,然後修改型別int為string。當然這樣的方法是沒有錯的,那麼後面又新增了其他的許多型別怎麼辦?還是複製修改嗎?這樣程式碼看上去很冗餘,很複雜。這時候泛型就出現了。下面我們看下為何使用泛型吧。

優點

  下面介紹下泛型的優點,尤其是下面幾個:

    • 效能
    • 型別安全
    • 二進位制程式碼重用

 

  一、效能

    泛型的一個主要優點就是效能,在泛型集合類和非泛型集合類中,對值型別使用非泛型集合類,在把值型別轉換為引用型別和把引用型別轉換為值型別的時候,需要進行裝箱和拆箱的操作(前面的文章中講到了拆箱和裝箱會造成一定的效能損失),當集合資料量大的時候造成的效能損失也就隨之的增大了。

    使用非泛型集合時:

            var list = new ArrayList();
            list.Add(100);//裝箱  int-object
            int i = (int)list[0];//拆箱  object-int
            foreach (int item in list)
            {
                Console.WriteLine(item);//遍歷拆箱輸出
            }        

 

    使用泛型集合類時:

    

            var list = new List<int>();
            list.Add(100);//無裝箱操作   
            int i =  list[0];//無拆箱拆箱   
            foreach (int item in list)
            {
                Console.WriteLine(item);//無拆箱操作
            }    

 

    減少裝箱拆箱操作,節省了效能消耗。這也就是泛型的主要優點了。

  二、型別安全

    泛型另一個優點就是型別安全,這裡我們還是使用非泛型集合類ArrayList()和泛型集合類List<T>來做案例。

    非泛型集合類ArrayList():

    

            var list = new ArrayList();
            list.Add(100);// 新增一個int型別
            list.Add("string");//新增一個string型別
            foreach (int item in list)
            {
                Console.WriteLine(item);//遍歷迴圈輸出
            }

這裡允許輸出和丟擲異常:

System.InvalidCastException:“Unable to cast object of type 'System.String' to type 'System.Int32'.”    

    無法強制把”string”轉換成int型別。

    我們再看泛型集合類:

        var list = new List<int>();
            list.Add(100);// 新增一個int型別
            list.Add("string");//新增一個string型別,編譯器報錯,無法從string轉換到int
            foreach (int item in list)
            {
                Console.WriteLine(item);//遍歷迴圈輸出
            }

    在新增”string”型別的時候編譯器報錯,無法新增。這裡也就杜絕了後續的錯誤。這也就是保證了型別的安全。

 

  三、二進位制程式碼重用

    泛型允許更好的重用二進位制程式碼,泛型型別可以定義一次,並且可以再許多不同的型別例項化,相比C++來說,不用每次訪問原始碼。

    例如上面使用的泛型集合類,using System.Collections.Generic; 中的List<T>類,可以用int,string,自定義類去例項化。

    泛型型別還可以在一種語言定義,然後再其他任何.Net語言中使用。

 

泛型類的功能

  這裡我們可以來了解下建立泛型類了之後,泛型類有哪些功能呢?

    • 預設值
    • 約束
    • 繼承
    • 靜態成員

  一、預設值

    在我們定義了泛型型別之後如何賦值呢?

public class Tclass<T>
     {
        public static T Get()
        {
            T a = default;
            return a;
        }

    }

    因為在泛型中初始給值不好給,你說給null吧,null是給引用型別的,你是給0吧,這又是給值型別的,這時候出現了default,當時引用型別呼叫時就給null,當時值型別時就0。

 

  二、約束

    說到泛型型別的約束時,不得不提關鍵字where,where是用來限制引數的範圍的,如果不符合where條件指定的引數範圍,編譯是不允許通過的。

    這裡泛型型別的約束主要可以分為以下6中

    • Where T: class(型別引數必須是引用型別)
    • Where T:struct(型別引數必須是值型別)
         public class Tclass<T,U>
        where T:class //型別引數為引用型別
        where U:struct  //型別引數為值型別
             {}    

 

    • Where T:<介面名稱>(型別引數必須是指定的介面或者實現指定的介面)
     /// <summary>
    /// 介面
    /// </summary>
    interface Itest
    {
    }
    /// <summary>
    /// 定義一個字典型別
    /// </summary>
    /// <typeparam name="TK"></typeparam>
    /// <typeparam name="TV"></typeparam>
    class Dictionary<TK, TV>
      where TK : IComparable, IEnumerable
       where TV : Itest
    {
        public void Add(TK key, TV val)
        {
        }
    }

 

 

    • Where T:<基類名>(引數必須是指定的基類或者是派生自指定的基類)
    class Ttest { }

    class Tclass<T> where T:Ttest
    {
        
    }

 

    • Where T:new ()(這是一個建構函式的約束,指定引數型別必須有一個預設建構函式,當與其他約束一起使用時必須放在其最後)
  class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
  {
      // ...
  }

 

    • Where T1:T2(這個約束指定型別T1派生自泛型型別T2,也就是說T1的引數型別要和T2一樣)
  public class Tclass<T> where T:IComparable { }

 

 

  三、繼承

    泛型型別的繼承與普通類的繼承相似但不同。

    /// <summary>
    /// 抽象基類,泛型型別
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Ttest<T>
    {
        public abstract T Add(T x, T y);
    }

   /// <summary>
   /// 繼承抽象基類,實現int型別
   /// </summary>
   /// <typeparam name="T"></typeparam>
    class Tclass<T> : Ttest<int>
    {
        public override int Add(int x, int y) => x + y;  

    }

 

  四、靜態成員

    泛型型別的靜態成員需要特殊的關注,泛型類的靜態成員只能在類的一個例項中共享。

    /// <summary>
    /// 泛型型別,靜態欄位x
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Ttest<T>
    {
        public static int x;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Ttest<string>.x = 111;
            Ttest<int>.x = 222;
            Console.WriteLine(Ttest<string>.x);
        }

    }

    上面事例中最後輸出的為111,

總結

  這裡我們主要是介紹了泛型的優點及泛型型別的功能。在我們日常的程式設計中會發現很多地方可以使用泛型。提高程式碼的擴充套件性及重用性。同時也可以減少對object型別的使用,採用泛型型別的使用來替代。較少對效能的消耗。我們下一節主要是對泛型型別的協變及抗變進行一定的理解。

 

    只要認為是對的就去做,堅持去做,不要在乎別人的看法,哪怕是錯,至少你有去做過證明曾經你努力過。

 


 

 

                      c#基礎知識詳解系列

 

 

 

  歡迎大家掃描下方二維碼,和我一起學習更多的C#知識

 

 

 

  

 

 

相關文章