泛型(一)

来源:http://www.cnblogs.com/zhlziliaoku/archive/2016/02/24/5213282.html
-Advertisement-
Play Games

泛型通常用在集合和集合上運行的方法中; 泛型NET Framework2.0提供一個新的命名空間System。Collections。Generic。 早期集合缺點: System.Collections.ArrayList list = new System.Collections.ArrayLi



泛型通常用在集合和集合上運行的方法中;

泛型NET Framework2.0提供一個新的命名空間System。Collections。Generic。

早期集合缺點:
System.Collections.ArrayList list = new System.Collections.ArrayList();
// Add an integer to the list.
list.Add(3);
// Add a string to the list. This will compile, but may cause an error later.
list.Add("It is raining in Redmond.");

添加到 ArrayList 中的任何引用或值類型都將隱式地向上強制轉換為 Object。如果項是值類型,則必須在將其添加到列表中時進行裝箱操作,在檢索時進行取消裝箱操作。強制轉換以及裝箱和取消裝箱操作都會降低性能;在必須對大型集合進行迴圈訪問的情況下,裝箱和取消裝箱的影響非常明顯。(泛型的優點)

// The .NET Framework 2.0 way to create a list
List<int> list1 = new List<int>();
// No boxing, no casting:
list1.Add(3);
// Compile-time error:
// list1.Add("It is raining in Redmond.");
優點:“對於客戶端代碼,與 ArrayList 相比,使用 List<T> 時添加的唯一語法是聲明和實例化中的類型參數。雖然這稍微增加了些編碼的複雜性,但好處是您可以創建一個比 ArrayList 更安全並且速度更快的列表,特別適用於列表項是值類型的情況.

在泛型方法中,類型參數是客戶端在實例化泛型類型的變數時指定的特定類型的占位符。

類型參數命名準則:

  1. 儘量使用描述性名稱命名,除非單個字母完全可以讓人理解它表示的含義。
  2. 考慮使用T作為單個字母類型參數的類型的類型參數。
  3. 務必將T 作為泛型參數描述性的首碼。如TSession
  4. 考慮在參數名中指示對此類型參數的約束。例如,可以將帶有 ISession 約束的參數命名為 TSession。

類型參數的約束

T:結構  類型參數必須是值類型,可以指定除Nullable以外的任何值類型。

public class MyClass2<T> where T : struct

//這個泛型類只接受值類型的泛型參數

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

 public class MyClass<T> where T:class//這個泛型類只接受引用類型的泛型參數

T:NEW() 類型參數必須具有無參數的公共構造函數,當與其他約束一起使用時,new()約

束必須最後指定。

public class MyClass3<T> where T : new()

T:<基類名>:類型參數必須是指定的基類或派生自指定的基類。

 public class MyClass5<T>where T : Customer

T:<介面名稱,類型參數必須是指定的介面或者實現指定的介面。可以指定多個介面約束,約束介面也可以是泛型的>

public class MyClass4<T> where T : System.IComparable

 

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

在指定一個類型參數時,可以指定類型參數必須滿足的約束條件。

這是通過在指定類型參數時使用where子句來實現的。
class class-name<type-param> where type-param:constraints{}

 在泛型中,要為某個使用泛型的變數初始化值,

可是我們需要考慮的是這個泛型可能是引用類型,也可能是值類型,這時我們可以藉助default來完成初始化複製。

T value = default(T);如果T是引用類型,value = null,如果是T是值類型,value = 0.

泛型類

class Test<T>
{
   public T obj;
   public Test(T obj)
   {
      this.obj = obj;
    }
}

子類繼承泛型類時必須明確指定泛型參數的類型

當子類也是泛型參數是,要註意約束必須和父類的匹配

 

class C<U,V>
class D:C<string,int>
class E<U,V>:C<U,V>
class F<U,V>:C<string,int>

泛型介面

//定義泛型介面
    interface Icomuter<T>
    {
        //定義泛型方法
        T Add(T item1, T item2);
        T Substract(T item1, T item2);
    }
    //繼承泛型介面
    class Student : Icomuter<int>
    {
        //繼承實現泛型方法
        public int Add(int item1, int item2)
        {
            return item1 + item2;
        }
        public int Substract(int item1, int item2)
        {
            return item1 - item2;
        }
    }
 public interface IList< T>   
    {  
     T[] GetElements();  
    }   
    public interface IDictionary< K,V>   
    {  
     void Add(K key, V value);   
    }  
    // 泛型介面的類型參數要麼已實例化  
    // 要麼來源於實現類聲明的類型參數  
    class List< T> : IList< T>, IDictionary< int, T>   
    {  
     public T[] GetElements() { return null; }  
     public void Add(int index, T value)   
     {}  
    } 

泛型方法

泛型方法是使用類型參數聲明的方法

static void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}
//方法的調用
int a = 1, b = 2;
Swap<int>(ref a, ref b);
System.Console.WriteLine(a + " " + b);
//省略類型參數,編譯器將推斷出該參數
Swap(ref a, ref b);
//相同的類型推斷規則也適用於靜態方法以及實例方法。編譯器能夠根據傳入的方法參數推斷類型參數;它無法僅從約束或返回值推斷類型參數

在泛型類中,非泛型方法可以訪問類級別類型參數,如下所示:

class SampleClass<T>
{
    void Swap(ref T lhs, ref T rhs) { }
}

泛型方法是使用類型參數聲明的方法,如下所示:

static void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}
public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap<int>(ref a, ref b);
    System.Console.WriteLine(a + " " + b);
}
View Code

也可以省略類型參數,編譯器將推斷出該參數。下麵對 Swap 的調用等效於前面的調用:

Swap(ref a, ref b);

相同的類型推斷規則也適用於靜態方法以及實例方法。編譯器能夠根據傳入的方法參數推斷類型參數;它無法僅從約束或返回值推斷類型參數。因此,類型推斷不適用於沒有參數的方法。類型推斷在編譯時、編譯器嘗試解析任何重載方法簽名之前進行。編譯器向共用相同名稱的所有泛型方法應用類型推斷邏輯。在重載解析步驟中,編譯器僅包括類型推斷取得成功的那些泛型方法。

在泛型類中,非泛型方法可以訪問類級別類型參數,如下所示:

class SampleClass<T>
{
    void Swap(ref T lhs, ref T rhs) { }
}

如果定義的泛型方法接受與包含類相同的類型參數,編譯器將生成警告 CS0693,因為在方法範圍內,為內部 T 提供的參數將隱藏為外部 T 提供的參數。除了類初始化時提供的類型參數之外,如果需要靈活調用具有類型參數的泛型類方法,請考慮為方法的類型參數提供其他標識符,如下麵示例中的GenericList2<T> 所示。

class GenericList<T>
{
    // CS0693
    void SampleMethod<T>() { }
}

class GenericList2<T>
{
    //No warning
    void SampleMethod<U>() { }
}

使用約束對方法中的類型參數啟用更專門的操作。此版本的 Swap<T> 現在稱為 SwapIfGreater<T>,它只能與實現 IComparable<T> 的類型參數一起使用

void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
{
    T temp;
    if (lhs.CompareTo(rhs) > 0)
    {
        temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}

在 C# 2.0 中,下限為零的一維數組自動實現 IList<T>。這使您可以創建能夠使用相同代碼迴圈訪問數組和其他集合類型的泛型方法。此技術主要對讀取集合中的數據很有用。IList<T> 介面不能用於在數組中添加或移除元素;如果試圖在此上下文中調用 IList<T> 方法(如數組的 RemoveAt),將引發異常。

//下麵的代碼示例演示帶有 IList<T> 輸入參數的單個泛型方法如何同時迴圈訪問列表和數組,本例中為整數數組。
class Program
{
    static void Main()
    {
        int[] arr = { 0, 1, 2, 3, 4 };
        List<int> list = new List<int>();

        for (int x = 5; x < 10; x++)
        {
            list.Add(x);
        }

        ProcessItems<int>(arr);
        ProcessItems<int>(list);
    }

    static void ProcessItems<T>(IList<T> coll)
    {
        foreach (T item in coll)
        {
            System.Console.Write(item.ToString() + " ");
        }
        System.Console.WriteLine();
    }
}
//儘管 ProcessItems 方法無法添加或移除項,但對於 ProcessItems 內部的 T[],IsReadOnly 屬性返回 False,因為該數組本身未聲明 ReadOnly 特性。
View Code

委托 可以定義自己的類型參數。引用泛型委托的代碼可以指定類型參數以創建已關閉的構造類型,就像實例化泛型類或調用泛型方法一樣,如下例所示:

public delegate void Del<T>(T item);
public static void Notify(int i) { }

Del<int> m1 = new Del<int>(Notify);
View Code

C# 2.0 版具有稱為方法組轉換的新功能,此功能適用於具體委托類型和泛型委托類型,並使您可以使用如下簡化的語法寫入上一行:

Del<int> m2 = Notify;
在泛型類內部定義的委托使用泛型類類型參數的方式可以與類方法所使用的方式相同。
class Stack<T>
{
    T[] items;
    int index;

    public delegate void StackDelegate(T[] items);
}
View Code

引用委托的代碼必須指定包含類的類型變數,如下所示:

private static void DoWork(float[] items) { }
public static void TestStack()
{
    Stack<float> s = new Stack<float>();
    Stack<float>.StackDelegate d = DoWork;
}
View Code

根據典型設計模式定義事件時,泛型委托尤其有用,因為發送方參數可以為強類型,不再需要強制轉換成 Object,或反向強制轉換。

delegate void StackEventHandler<T, U>(T sender, U eventArgs);

class Stack<T>
{
    public class StackEventArgs : System.EventArgs { }
    public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent;

    protected virtual void OnStackChanged(StackEventArgs a)
    {
        stackEvent(this, a);
    }
}

class SampleClass
{
    public void HandleStackChange<T>(Stack<T> stack, Stack<T>.StackEventArgs args) { }
}

public static void Test()
{
    Stack<double> s = new Stack<double>();
    SampleClass o = new SampleClass();
    s.stackEvent += o.HandleStackChange;
}
View Code

default:

之所以會用到default關鍵字,是因為需要在不知道類型參數為值類型還是引用類型的情況下,為對象實例賦初值。

在泛型類和泛型方法中產生的一個問題是,在預先未知以下情況時,如何將預設值分配給參數化類型 T:

  • T 是引用類型還是值類型。

    如果 T 為值類型,則它是數值還是結構

 

 


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

-Advertisement-
Play Games
更多相關文章
  • 為了方便用戶部署自己的shadowsocks和ss-panel,花了兩天時間學習和編寫dockerfile,做了一個docker版本,歡迎下載使用。 Docker版本地址 https://hub.docker.com/r/maxidea/ss-panel/ 本Docker鏡像基於官方ubuntu版本
  • 最近寫了兩個小程式都要調用Windows自帶的命令行程式,一個是調用Openfiles.exe查詢正在編輯的共用文檔,一個是調用DiskPart.exe查詢硬碟狀態。兩種命令行程式調用有點不同,記錄一下。 1.用ProcessStartInfo配置參數調用。 這種是在CMD里直接帶參數輸入的,要註意
  • Atitit.Java exe bat 作為windows系統服務程式運行 1. 使用SC命令+srvany.exe (不錯,推薦)+net start1 1.1. First 創建一個java的運行bat1 1.2. 配置srvany 做serv wrapper1 1.3. 使用sc 創建/del
  • 實現科學上網,同時讓git和svn也使用shadowsocks來順暢的獲取源碼。
  • 本文先引入給讀者一個自己研究的機會,下次深入說明一下: 廢話不多說,直接上圖 新建一個mvc的項目 在視圖裡面添加一個移動端視圖 正常訪問一下 Bootstrap自帶的響應式的方式(頁面代碼並沒有改變) 我們來模擬一下移動端訪問: 谷歌或者火狐藉助:user-agent switcher 深入的部分
  • 首先,吐槽一下金數據的API文檔 http://help.jinshuju.net/articles/api-intro.html 寫的很粗糙啊...反正我是沒太看明白 拿表單api舉例,只告訴你了個地址https://jinshuju.net/api/v1/forms/ex27t2,然後呢,然後沒
  • 方法: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 namespace PaiXie.Pos.Admin
  • 主要是想把日期和其它因素考慮進來。 使用RNGCryptoServiceProvider類創建唯一的最多8位數字元串。 private static string GetUniqueKey() { int maxSize = 8; int minSize = 5; char[] chars = ne
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...