C#中的泛型和泛型集合

来源:http://www.cnblogs.com/kudsu/archive/2017/10/25/7727615.html
-Advertisement-
Play Games

一、什麼是泛型? 泛型是C#語言和公共語言運行庫(CLR)中的一個新功能,它將類型參數的概念引入.NET Framework。類型參數使得設計某些類和方法成為可能,例如,通過使用泛型類型參數T,可以大大簡化類型之間的強制轉換或裝箱操作的過程(下一篇將說明如何解決裝箱、拆箱問題)。說白了,泛型就是通過 ...


一、什麼是泛型?

泛型是C#語言和公共語言運行庫(CLR)中的一個新功能,它將類型參數的概念引入.NET Framework。類型參數使得設計某些類和方法成為可能,例如,通過使用泛型類型參數T,可以大大簡化類型之間的強制轉換或裝箱操作的過程(下一篇將說明如何解決裝箱、拆箱問題)。說白了,泛型就是通過參數化類型來實現在同一份代碼上操作多種數據類型,利用“參數化類型”將類型抽象化,從而實現靈活的復用。

  使用泛型給代碼帶來的5點好處:1、可以做大限度的重用代碼、保護類型的安全以及提高性能。

                  2、可以創建集合類。

                  3、可以創建自己的泛型介面、泛型方法、泛型類、泛型事件和泛型委托。

                  4、可以對泛型類進行約束,以訪問特定數據類型的方法。

                  5、關於泛型數據類型中使用的類型的信息,可在運行時通過反射獲取。

  例子:

using System;

namespace ConsoleApp
{
    class Program
    {
        class Test<T>
        {
            public T obj;
            public Test(T obj)
            {
                this.obj = obj;
            }
        }
        static void Main(string[] args)
        {
            int obj1 = 2;
            var test = new Test<int>(obj1);
            Console.WriteLine("int:" + test.obj);

            string obj2 = "hello world";
            var test1 = new Test<string>(obj2);
            Console.WriteLine("String:" + test1.obj);

            Console.ReadKey();
        }
    }
}

     輸出結果是:

  int:2

  String:hello world

  分析:  

  1、  Test是一個泛型類。T是要實例化的範型類型。如果T被實例化為int型,那麼成員變數obj就是int型的,如果T被實例化為string型,那麼obj就是string類型的。

  2、  根據不同的類型,上面的程式顯示出不同的值。

二、泛型的主約束和次約束是什麼?

   六種類型的約束:

T:結構

類型參數必須是值類型。可以指定除 Nullable 以外的任何值類型。有關更多信息,請參見使用可空類型(C# 編程指南)。

T:類

類型參數必須是引用類型,包括任何類、介面、委托或數組類型。

T:new()

類型參數必須具有無參數的公共構造函數。當與其他約束一起使用時,new() 約束必須最後指定。

T:<基類名>

類型參數必須是指定的基類或派生自指定的基類。

T:<介面名稱>

類型參數必須是指定的介面或實現指定的介面。可以指定多個介面約束。約束介面也可以是泛型的。

T:U

為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。這稱為裸類型約束。

 

  例子:

  1.介面約束

  例如,可以聲明一個泛型類 MyGenericClass,這樣,類型參數 T 就可以實現 IComparable<T> 介面:

public class MyGenericClass<T> where T:IComparable { } 

  2.基類約束。

  指出某個類型必須將指定的類作為基類(或者就是該類本身),才能用作該泛型類型的類型參數。這樣的約束一經使用,就必須出現在該類型參數的所有其他約束之前。

class MyClassy<T, U>
where T : class
where U : struct
{

}

  3.構造函數約束。

  以使用 new 運算符創建類型參數的實例;但類型參數為此必須受構造函數約束 new() 的約束。new() 約束可以讓編譯器知道:提供的任何類型參數都必須具有可訪問的無參數(或預設)構造函數。new() 約束出現在 where 子句的最後。

public class MyGenericClass <T> where T: IComparable, new()
{
         T item = new T();
}

  4.對於多個類型參數,每個類型參數都使用一個 where 子句。

interface MyI { }
class Dictionary<TKey,TVal>
where TKey: IComparable, IEnumerable
where TVal: MyI
{
    public void Add(TKey key, TVal val)
    {

    }
}

  5.還可以將約束附加到泛型方法的類型參數。

public bool MyMethod<T>(T t) where T : IMyInterface { }  

  6. 裸類型約束

  用作約束的泛型類型參數稱為裸類型約束。當具有自己的類型參數的成員函數需要將該參數約束為包含類型的類型參數時,裸類型約束很有用。

class List<T>
{
    void Add<U>(List<U> items) where U : T {}
}

  為什麼要有約束呢?

  當一個泛型參數沒有任何約束時,它可以進行的操作和運算是非常有限的,因為不能對實參做任何類型上的保證,這時候就需要用到泛型的約束。泛型的主要約束和次要約束都是指泛型的實參必須滿足一定的規範。C#編譯器在編譯的過程中可以根據約束來檢查所有泛型類型的實參並確保其滿足約束條件。

  一個泛型參數可以至多擁有一個主要約束,主要約束可以是一個引用類型、class或者struct。如果指定一個引用類型,則實參必須是該類型或者該類型派生類型。class規定實參必須是一個引用類型。struct規定了參數必須是一個之類新。以下代碼是泛型參數主要約束的示例。

using System;

namespace Test
{
    class GenericPrimaryConstraint
    {
        static void Main()
        {
            Console.Read();
        }
    }
    //主要約束限定T繼承自Exception類型
    public class ClassT1<T> where T : Exception
    {
        private T myException;
        public ClassT1(T t)
        {
            myException = t;
        }
        public override string ToString()
        {
            //主要約束保證了myException擁有Source成員
            return myException.Source;
        }
    }
    //主要約束限定T是引用類型
    public class ClassT2<T> where T : class
    {
        private T myT;
        public void Clear()
        { 
            //T是引用類型,可以置null
            myT = null;
        }
    }
    //主要約束限定T是值類型
    public class ClassT3<T> where T : struct
    {
        private T myT;
        public override string ToString()
        {
            //T是值類型,不會發生NullReferenceException異常
            return myT.ToString();
        }
    }
}

  以上代碼,泛型參數具備了主要約束後,就能夠在類型中對其進行一定的操作,否則任何演算法就只能基於一個System.Object類型的成員。

  可以說,主要約束是實參類型的限定,而相對的次要約束,則是指實參實現的介面的限定。對於一個泛型類型,可以有0至無限的次要約束,次要約束規定了參數必須實現所有次要約束中規定的介面。次要約束的語法和主要約束基本一致,區別僅在於提供的不是一個引用類型而是一個或多個介面。

  ps:同時擁有主要約束和次要約束的泛型參數,表示實參必須同時滿足主要約束和次要約束。

  三、什麼是泛型集合?

  字元串可以說是一個字元的集合,和字元串一樣,數據對象也可以是集合的方式存在,所以泛型類對象也可以是集合的方式存在(泛型集合)

  同傳統的集合相比,泛型集合是一種強類型的集合,它解決了類型安全問題,同時避免了集合中每次的裝箱與拆箱的操作,提升了性能。

  泛型集合類型:

  1. List,這是我們應用最多的泛型種類,它對應ArrayList集合。

  2. Dictionary,這也是我們平時運用比較多的泛型種類,對應Hashtable集合。

  3. Collection對應於CollectionBase

  4. ReadOnlyCollection 對應於ReadOnlyCollectionBase,這是一個只讀的集合。

  5. Queue,Stack和SortedList,它們分別對應於與它們同名的非泛型類。

  性能問題

  下麵以ArrayList與List<T>為例說明泛型集合的優點及非泛型集合的缺點。例如,有這麼一段代碼:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Web
{
    public partial class b : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            ArrayList numbers = new ArrayList();
            numbers.Add(1);//裝箱
            numbers.Add(2);//裝箱

            int number = (int)numbers[1];//拆箱
            Label1.Text = number.ToString();
        }
    }
}

這段代碼的背後會發生什麼呢?首先,ArrayList中將所有元素都看成Object類型的,是引用類型。調用Add方法增加兩個整數,在這個過程中,整數1,2被CLR裝箱(boxing)成object類型的,而後二個元素時又被拆箱(unboxing),裝箱與拆箱大體上會發生以下過程

1.      在托管堆中非配一個新的object

2.      基於棧(stack-based)的數據必須移動到剛非配的記憶體區中

3.      當拆箱時,位於堆中的數據又得移動到棧中

4.      堆中無用的數據進行垃圾回收

當涉及大量裝箱與拆箱操作時,必然會影響應用程式的性能。而是用泛型的集合類時就會減少裝箱與拆箱的工作,當存在大量數據時,自然可以提高很多性能。,比如用

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Web
{
    public partial class b : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            List<int> numbers = new List<int>();//找不同
            numbers.Add(1);
            numbers.Add(2);

            int number = numbers[1];//找不同
            Label1.Text = number.ToString();
        }
    }
}

  類型安全問題

  對於ArrayList,下麵的代碼編譯時時不會報錯的。

 ArrayList numbers = new ArrayList();

            numbers.Add(22);

            numbers.Add(35.5);

            numbers.Add(true);
            foreach (object item in numbers)
            {
                Console.WriteLine((int)item);
            }

因為可以將int類型,float等數值類型裝箱成object,因此即使ArrayList增加的數據類型不一致。編譯器也不會提示錯誤,但是運行時,會報錯。

但是如果是用泛型類型比如List<T>,那麼在編譯時就會進行類型檢查。防止運行時錯誤。

 

 

ps:此文章是本人參考網上內容加上自己的理解整合而成,如無意中侵犯了您的權益,請與本人聯繫。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • umask 設定文件創建時的預設模式,對於文件最大值為6,對於目錄最大值為7。 系統不允許創建一個文本文件就賦予可執行許可權,而目錄對應位表示進入或可搜索許可權,故最大值為7 find -perm 八進位表示許可權值前不加橫杠表示所屬主,加了表示所有用戶 -prune 若同時指定-depth選項,則該選項 ...
  • 瞭解I2C匯流排協議; 掌握S3C2410/S3C2440中I2C介面的使用方法 ...
  • 企業面試題2:使用for迴圈在/oldboy目錄下通過隨機小寫10個字母加固定字元串oldboy批量創建10個html文件,名稱例如為: [root@oldboy oldboy]# sh /server/scripts/oldboy.sh [root@oldboy oldboy]# ls coaol ...
  • 1.查詢的模糊匹配 儘量避免在一個複雜查詢裡面使用 LIKE '%parm1%'—— 紅色標識位置的百分號會導致相關列的索引無法使用,最好不要用. 解決辦法: 其實只需要對該腳本略做改進,查詢速度便會提高近百倍。改進方法如下: a、修改前臺程式——把查詢條件的供應商名稱一欄由原來的文本輸入改為下拉列 ...
  • 理解需求:理清業務邏輯,根據業務場景,選擇合適的解決方案 按照開發語言的編程規範開發 對象的序列化:處理ajax請求時,一般都是將後臺要返回的數據序列化成json對象,這樣便於頁面獲取json數據 如果後臺返回給頁面的數據已經是json對象了,但是前臺仍然獲取不到,可以用 $.parseJSON(d ...
  • 先看一下效果,帶介面層的三層架構: BL層: 假設GetStudentList方法里的mStudentDa.GetStudents和mValueService.FindAll不是查詢操作,而是更新操作,當一個失敗另一個需要回滾,就需要在同一個事務里,當一個出現異常就要回滾事務。 特性Transact ...
  • 先建一個空的項目和之前的NancyFx系列一樣的步驟 然後建三個文件夾Models,Module,Views 然後分別安裝一下組件 jQuery Microsoft.AspNet.SignalR Microsoft.Owin Nancy Nancy.Owin 然後往Model類裡面添加CPUHub類 ...
  • 理解托管和非托管代碼的前提之下,要先瞭解CLR(公共語言運行庫) .Net Framework 是由彼此獨立又相關的兩部分組成:CLR 和 類庫, CLR是它為我們提供的服務,類庫是它實現的功能. .NET的大部分特性 垃圾收集,版本控制,線程管理等,都使用了CLR提供的服務當你為.NET Fram ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...