泛型(一)

来源: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...