溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

.net中泛型的概述與分析

發(fā)布時(shí)間:2021-03-12 11:22:29 來(lái)源:億速云 閱讀:134 作者:小新 欄目:編程語(yǔ)言

這篇文章主要介紹了.net中泛型的概述與分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

一、泛型概述

     泛型類(lèi)和泛型方法兼復(fù)用性、類(lèi)型安全和高效率于一身,是與之對(duì)應(yīng)的非泛型的類(lèi)和方法所不及。泛型廣泛用于容器(collections)和對(duì)容器操作的方法中。.NET Framework 2.0的類(lèi)庫(kù)提供一個(gè)新的命名空間System.Collections.Generic,其中包含了一些新的基于泛型的容器類(lèi)。

  1. 泛型的可變類(lèi)型參數(shù):通常用T,但也可以用任意非關(guān)鍵字和保留字;

  2. 所有的可變類(lèi)型T在編譯時(shí),都采用占位符的形式,在運(yùn)行時(shí)將由實(shí)際傳入的類(lèi)型來(lái)替換的所有的點(diǎn)位符;

二、泛型的優(yōu)點(diǎn)

針對(duì)早期版本的通用語(yǔ)言運(yùn)行時(shí)和C#語(yǔ)言的局限,泛型提供了一個(gè)解決方案。以前類(lèi)型的泛化(generalization)是靠類(lèi)型與全局基類(lèi)System.Object的相互轉(zhuǎn)換來(lái)實(shí)現(xiàn)。 .NET Framework 基礎(chǔ)類(lèi)庫(kù)的ArrayList容器類(lèi),就是這種局限的一個(gè)例子。ArrayList是一個(gè)很方便的容器類(lèi),使用中無(wú)需更改就可以存儲(chǔ)任何引用類(lèi)型或值類(lèi)型。

ArrayList list = new ArrayList();
list.Add(1);
list.Add(175.50);
list.Add("hello kitty");

double sum = 0;
foreach(int value in list)
{
    sum += value;
}

缺點(diǎn):

便利是有代價(jià)的,這需要把任何一個(gè)加入ArrayList的引用類(lèi)型或值類(lèi)型都隱式地向上轉(zhuǎn)換成System.Object。如果這些元素是值類(lèi)型,那么當(dāng)加入到列表中時(shí),它們必須被裝箱;當(dāng)重新取回它們時(shí),要拆箱。類(lèi)型轉(zhuǎn)換和裝箱、拆箱的操作都降低了性能;在必須迭代(iterate)大容器的情況下,裝箱和拆箱的影響可能十分顯著。另一個(gè)局限是缺乏編譯時(shí)的類(lèi)型檢查,當(dāng)一個(gè)ArrayList把任何類(lèi)型都轉(zhuǎn)換為Object,就無(wú)法在編譯時(shí)預(yù)防客戶(hù)代碼中類(lèi)似sum+=vlaue這樣的錯(cuò)誤;

在System.Collections.Generic命名空間中的泛型List<T>容器里,同樣是把元素加入容器的操作,類(lèi)似這樣:

List<int> listInt = new List<int>();
    listInt.Add(100);
    listInt.Add(200);
    listInt.Add(123.112); //編譯報(bào)錯(cuò)
    listInt.Add("heel");  //編譯報(bào)錯(cuò)

double sum = 0;
    foreach (int value in list)
    {
        sum += value;
    }

與ArrayList相比,在客戶(hù)代碼中唯一增加的List<T>語(yǔ)法是聲明和實(shí)例化中的類(lèi)型參數(shù)。代碼略微復(fù)雜的回報(bào)是,你創(chuàng)建的表不僅比ArrayList更安全,而且明顯地更加快速,尤其當(dāng)表中的元素是值類(lèi)型的時(shí)候。

三、泛型的類(lèi)型參數(shù)

     泛型類(lèi)型或泛型方法的定義中,類(lèi)型參數(shù)是一個(gè)占位符(placeholder),通常為一個(gè)大寫(xiě)字母(也可以使用任意非關(guān)鍵字和保留字的名字),如T。在客戶(hù)代碼聲明、實(shí)例化該類(lèi)型的變量時(shí),把T替換為客戶(hù)代碼所指定的數(shù)據(jù)類(lèi)型。泛型類(lèi),如泛型中給出的List<T>類(lèi),不能用作as-is,原因在于它不是一個(gè)真正的類(lèi)型,而更像是一個(gè)類(lèi)型的藍(lán)圖。要使用MyList<T>,客戶(hù)代碼必須在尖括號(hào)內(nèi)指定一個(gè)類(lèi)型參數(shù),來(lái)聲明并實(shí)例化一個(gè)已構(gòu)造類(lèi)型(constructed type)。這個(gè)特定類(lèi)的類(lèi)型參數(shù)可以是編譯器識(shí)別的任何類(lèi)型??梢詣?chuàng)建任意數(shù)量的已構(gòu)造類(lèi)型實(shí)例,每個(gè)使用不同的類(lèi)型參數(shù),如下:

List<int> listInt = new List<int>();
List<float> listFloat = new List<float>();
List<String> listString = new List<String>();

四、泛型類(lèi)型參數(shù)的約束

泛型提供了下列五種約束:

約束描述
where T : struct參數(shù)類(lèi)型必須為值類(lèi)型
where T : class參數(shù)類(lèi)型必須為引用類(lèi)型
where T : new()參數(shù)類(lèi)型必須有一個(gè)公有的無(wú)參構(gòu)造函數(shù)。當(dāng)與其它約束聯(lián)合使用時(shí),new()約束必須放在最后。
where T : <Base Class Name>參數(shù)類(lèi)型必須為指定的基類(lèi)型或派生自指定基類(lèi)型的子類(lèi)
where T : <Interface Name>參數(shù)類(lèi)型必須為指定的接口或指定接口的實(shí)現(xiàn)??芍付ǘ鄠€(gè)接口的約束。接口約束也可以是泛型的。

無(wú)限制類(lèi)型參數(shù):

  1. 不能使用!=和==對(duì)可變類(lèi)型的實(shí)例進(jìn)行比較,因?yàn)闊o(wú)法保證具體的類(lèi)型參數(shù)支持這些運(yùn)算符;

  2. 它們可以與System.Object相互轉(zhuǎn)換,也可顯式地轉(zhuǎn)換成任何接口類(lèi)型;

  3. 可以與null比較。如果一個(gè)無(wú)限制類(lèi)型參數(shù)與null比較,當(dāng)此類(lèi)型參數(shù)為值類(lèi)型時(shí),比較的結(jié)果總為false。

無(wú)類(lèi)型約束:當(dāng)約束是一個(gè)泛型類(lèi)型參數(shù)時(shí),它就叫無(wú)類(lèi)型約束(Naked type constraints)。

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

在上面的示例中, Add方法的上下文中的T,就是一個(gè)無(wú)類(lèi)型約束;而List類(lèi)的上下文中的T,則是一個(gè)無(wú)限制類(lèi)型參數(shù)。

無(wú)類(lèi)型約束也可以用在泛型類(lèi)的定義中。注意,無(wú)類(lèi)型約束一定也要和其它類(lèi)型參數(shù)一起在尖括號(hào)中聲明:

//naked type constraint

public class MyClass<T,U,V> where T : V

因?yàn)榫幾g器只認(rèn)為無(wú)類(lèi)型約束是從System.Object繼承而來(lái),所以帶有無(wú)類(lèi)型約束的泛型類(lèi)的用途十分有限。當(dāng)你希望強(qiáng)制兩個(gè)類(lèi)型參數(shù)具有繼承關(guān)系時(shí),可對(duì)泛型類(lèi)使用無(wú)類(lèi)型約束。

五、泛型類(lèi)

泛型類(lèi)封裝了不針對(duì)任何特定數(shù)據(jù)類(lèi)型的操作。泛型類(lèi)常用于容器類(lèi),如鏈表、哈希表、棧、隊(duì)列、樹(shù)等等。這些類(lèi)中的操作,如對(duì)容器添加、刪除元素,不論所存儲(chǔ)的數(shù)據(jù)是何種類(lèi)型,都執(zhí)行幾乎同樣的操作。

通常,從一個(gè)已有的具體類(lèi)來(lái)創(chuàng)建泛型類(lèi),并每次把一個(gè)類(lèi)型改為類(lèi)型參數(shù),直至達(dá)到一般性和可用性的最佳平衡。當(dāng)創(chuàng)建你自己的泛型類(lèi)時(shí),需要重點(diǎn)考慮的事項(xiàng)有:

  • 哪些類(lèi)型應(yīng)泛化為類(lèi)型參數(shù)。一般的規(guī)律是,用參數(shù)表示的類(lèi)型越多,代碼的靈活性和復(fù)用性也就越大。過(guò)多的泛化會(huì)導(dǎo)致代碼難以被其它的開(kāi)發(fā)人員理解。

  • 如果有約束,那么類(lèi)型參數(shù)需要什么樣約束。一個(gè)良好的習(xí)慣是,盡可能使用最大的約束,同時(shí)保證可以處理所有需要處理的類(lèi)型。例如,如果你知道你的泛型類(lèi)只打算使用引用類(lèi)型,那么就應(yīng)用這個(gè)類(lèi)的約束。這樣可以防止無(wú)意中使用值類(lèi)型,同時(shí)可以對(duì)T使用as運(yùn)算符,并且檢查空引用;

  • 把泛型行為放在基類(lèi)中還是子類(lèi)中。泛型類(lèi)可以做基類(lèi)。同樣非泛型類(lèi)的設(shè)計(jì)中也應(yīng)考慮這一點(diǎn)。泛型基類(lèi)的繼承規(guī)則;

  • 是否實(shí)現(xiàn)一個(gè)或多個(gè)泛型接口。例如,要設(shè)計(jì)一個(gè)在基于泛型的容器中創(chuàng)建元素的類(lèi),可能需要實(shí)現(xiàn)類(lèi)似IComparable<T>的接口,其中T是該類(lèi)的參數(shù)。

對(duì)于一個(gè)泛型類(lèi)Node<T>,客戶(hù)代碼既可指定一個(gè)類(lèi)型參數(shù)來(lái)創(chuàng)建一個(gè)封閉構(gòu)造類(lèi)型(Node<int>),也可保留類(lèi)型參數(shù)未指定,例如指定一個(gè)泛型基類(lèi)來(lái)創(chuàng)建開(kāi)放構(gòu)造類(lèi)型(Node<T>)。泛型類(lèi)可以繼承自具體類(lèi)、封閉構(gòu)造類(lèi)型或開(kāi)放構(gòu)造類(lèi)型:

// concrete type
class Node<T> : BaseNode
//closed constructed type
class Node<T> : BaseNode<int>
//open constructed type
class Node<T> : BaseNode<T>

非泛型的具體類(lèi)可以繼承自封閉構(gòu)造基類(lèi),但不能繼承自開(kāi)放構(gòu)造基類(lèi)。這是因?yàn)榭蛻?hù)代碼無(wú)法提供基類(lèi)所需的類(lèi)型參數(shù):

//No error.
class Node : BaseNode<int>
//Generates an error.
class Node : BaseNode<T>

泛型的具體類(lèi)可以繼承自開(kāi)放構(gòu)造類(lèi)型。除了與子類(lèi)共用的類(lèi)型參數(shù)外,必須為所有的類(lèi)型參數(shù)指定類(lèi)型:

//Generates an error.
class Node<T> : BaseNode<T, U> {…}
//Okay.
class Node<T> : BaseNode<T, int> {…}

繼承自開(kāi)放結(jié)構(gòu)類(lèi)型的泛型類(lèi),必須指定參數(shù)類(lèi)型和約束:

class NodeItem<T> where T : IComparable<T>, new() {…}
class MyNodeItem<T> : NodeItem<T> where T : IComparable<T>, new() {…}

泛型類(lèi)型可以使用多種類(lèi)型參數(shù)和約束:

class KeyType<K, V> {…}
class SuperKeyType<K, V, U> where U : IComparable<U>, where V : new() {…}

開(kāi)放結(jié)構(gòu)和封閉構(gòu)造類(lèi)型可以用作方法的參數(shù):

void Swap<T>(List<T> list1, List<T> list2) {…}
void Swap(List<int> list1, List<int> list2) {…}

六、泛型接口

當(dāng)一個(gè)接口被指定為類(lèi)型參數(shù)的約束時(shí),只有實(shí)現(xiàn)該接口的類(lèi)型可被用作類(lèi)型參數(shù)。

可以在一個(gè)類(lèi)型指定多個(gè)接口作為約束,如下:

class Stack<T> where T : IComparable<T>, IMyStack1<T>{}

一個(gè)接口可以定義多個(gè)類(lèi)型參數(shù),如下:

IDictionary<K,V>

接口和類(lèi)的繼承規(guī)則相同:

//Okay.
IMyInterface: IBaseInterface<int>
//Okay.
IMyInterface<T> : IBaseInterface<T>
//Okay.
IMyInterface<T>: IBaseInterface<int>
//Error.
IMyInterface<T> : IBaseInterface2<T, U>

具體類(lèi)可以實(shí)現(xiàn)封閉構(gòu)造接口,如下:

class MyClass : IBaseInterface<string>

泛型類(lèi)可以實(shí)現(xiàn)泛型接口或封閉構(gòu)造接口,只要類(lèi)的參數(shù)列表提供了接口需要的所有參數(shù),如下:

//Okay.
class MyClass<T> : IBaseInterface<T>
//Okay.
class MyClass<T> : IBaseInterface<T, string>

泛型類(lèi)、泛型結(jié)構(gòu),泛型接口都具有同樣方法重載的規(guī)則。

七、泛型方法

泛型方法是聲名了類(lèi)型參數(shù)的方法,如下:

void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}

下面的示例代碼顯示了一個(gè)以int作為類(lèi)型參數(shù),來(lái)調(diào)用方法的例子:

int a = 1;
int b = 2;
//…
Swap<int>(a, b);

也可以忽略類(lèi)型參數(shù),編譯器會(huì)去推斷它。下面調(diào)用Swap的代碼與上面的例子等價(jià):

Swap(a, b);

靜態(tài)方法和實(shí)例方法有著同樣的類(lèi)型推斷規(guī)則。編譯器能夠根據(jù)傳入的方法參數(shù)來(lái)推斷類(lèi)型參數(shù);而無(wú)法單獨(dú)根據(jù)約束或返回值來(lái)判斷。因此類(lèi)型推斷對(duì)沒(méi)有參數(shù)的方法是無(wú)效的。類(lèi)型推斷發(fā)生在編譯的時(shí)候,且在編譯器解析重載方法標(biāo)志之前。編譯器對(duì)所有同名的泛型方法應(yīng)用類(lèi)型推斷邏輯。在決定(resolution)重載的階段,編譯器只包含那些類(lèi)型推斷成功的泛型類(lèi)。

在泛型類(lèi)中,非泛型方法能訪問(wèn)所在類(lèi)中的類(lèi)型參數(shù):

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

在泛型類(lèi)中,定義一個(gè)泛型方法,和其所在的類(lèi)具有相同的類(lèi)型參數(shù);試圖這樣做,編譯器會(huì)產(chǎn)生警告CS0693。

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

warning CS0693: 類(lèi)型參數(shù)“T”與外部類(lèi)型“List<T>”中的類(lèi)型參數(shù)同名

在泛型類(lèi)中,定義一個(gè)泛型方法,可定義一個(gè)泛型類(lèi)中未定義的類(lèi)型參數(shù):(不常用,一般配合約束使用)

class List<T>
{
    void Swap<U>(ref T lhs, ref T rhs) {  }   //不常用

void Add<U>(List<U> items) where U : T{}  //常用
}

泛型方法通過(guò)多個(gè)類(lèi)型參數(shù)來(lái)重載。例如,下面的這些方法可以放在同一個(gè)類(lèi)中:

void DoSomething() { }
void DoSomething<T>() { }
void DoSomething<T, U>() { }

八、泛型中的default關(guān)鍵字

在泛型類(lèi)和泛型方法中會(huì)出現(xiàn)的一個(gè)問(wèn)題是,如何把缺省值賦給參數(shù)化類(lèi)型,此時(shí)無(wú)法預(yù)先知道以下兩點(diǎn):

  1. T將是值類(lèi)型還是引用類(lèi)型

  2. 如果T是值類(lèi)型,那么T將是數(shù)值還是結(jié)構(gòu)

對(duì)于一個(gè)參數(shù)化類(lèi)型T的變量t,僅當(dāng)T是引用類(lèi)型時(shí),t = null語(yǔ)句才是合法的; t = 0只對(duì)數(shù)值的有效,而對(duì)結(jié)構(gòu)則不行。這個(gè)問(wèn)題的解決辦法是用default關(guān)鍵字,它對(duì)引用類(lèi)型返回空,對(duì)值類(lèi)型的數(shù)值型返回零。而對(duì)于結(jié)構(gòu),它將返回結(jié)構(gòu)每個(gè)成員,并根據(jù)成員是值類(lèi)型還是引用類(lèi)型,返回零或空。

class GenericClass<T>
{
    T GetElement()
    {
        return default(T);
    }
}

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“.net中泛型的概述與分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI