日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

类型参数化-泛型

發(fā)布時(shí)間:2023/12/19 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 类型参数化-泛型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

中間因?yàn)楸容^忙,空了那么多天,都感覺有點(diǎn)罪過了。話不多說,這一篇主要是要講C#2.0提出的一個(gè)新特性,那就是泛型。(現(xiàn)在都C#6.0了。囧囧)

1、什么是泛型?

C#1.0中的委托特性使方法可作為其他方法的參數(shù)來傳遞,而C#2.0中提出的泛型特性則使類型可以被參數(shù)化,從而不必再為不同的類型提供特殊版本的方法實(shí)現(xiàn)。從字面上來的意思來講,泛型代表的就是“通用類型”,它可以代替任意的數(shù)據(jù)類型,使類型參數(shù)化,從而達(dá)到只實(shí)現(xiàn)一個(gè)方法就可以操作多種數(shù)據(jù)類型的目的,將實(shí)現(xiàn)行為與方法操作的數(shù)據(jù)類型分離,實(shí)現(xiàn)了代碼的重用。下面通過.NET類庫中的泛型來說明到底什么是泛型。

clas program

{

  static void Main(string[] args)

  {

    //用int作為實(shí)際參數(shù)來初始化泛型類型

    List<int> intList=new List<int>();

    //從int列表添加元素3

    intList.Add(3);

    

    //用string作為實(shí)際參數(shù)來初始化泛型類型

    List<string> stringList=new List<string>();

    //從string列表添加元素

    stringList.Add("learninghard");

  }

}

在以上代碼中,List<T>是.NET類庫中實(shí)現(xiàn)的泛型參數(shù),T是泛型參數(shù),可以理解為形參。如果想實(shí)例化一個(gè)泛型類型,必須傳入實(shí)際的類型參數(shù),如上面代碼中的int和string,就是實(shí)際的額類型參數(shù)。同時(shí)你也可以自己實(shí)現(xiàn)泛型類型,下面會介紹泛型類型的實(shí)現(xiàn)方法。

?

2、C# 2.0為什么要引入泛型?

這個(gè)就需要用代碼來解釋了。

public class Compare

{

  //比較兩個(gè)整數(shù)大小的方法,方法返回較大的那個(gè)整數(shù)

  public static int compareInt(int i1,int i2)

  {

    if(i1.CompareTo(i2)>0)

    {

      return i1;

    }

    else

    {

      return i2;

    }

  }

}

以上的代碼實(shí)現(xiàn)對于int類型數(shù)據(jù)來說完全是沒有問題的,但是當(dāng)客戶說我想實(shí)現(xiàn)兩個(gè)字符串大小的比較,你就必須再添加一個(gè)比較字符串的大小了。如果客戶說還想添加一個(gè)比較浮點(diǎn)數(shù)的大小,那你肯定又只能添加一個(gè)方法啦。顯然,這種方式是一種比較low的方式,其實(shí)方法的代碼都非常的相似,所以我們完全可以只定義一個(gè)比較方法就能比較所有不同類型的大小。

這時(shí),泛型就派上用場了,就是為這種情況而生,所以,微軟就在C#2.0中提出了泛型的特性。

//Compare<T>為泛型類,T為類型參數(shù)

public class Compare<T> where T:IComparable

{

  //使用泛型實(shí)現(xiàn)的比較方法

  public static T compareGeneric(T t1,T t2)

  {

    if(t1.CompareTo(t2)>0)

    {

      return t1;

    }

    else

    {

      return t2;

    }

  }

}

在以上的代碼中,我們實(shí)現(xiàn)了一個(gè)自定義的泛型類,其中T是泛型的類型參數(shù),compareGeneric是實(shí)現(xiàn)的泛型方法,代碼中的where語句是類型參數(shù)的約束,它用來使類型參數(shù)可以適用于CompareTo方法。有了泛型之后,就不需要針對每種數(shù)據(jù)類型重復(fù)實(shí)現(xiàn)相似的比較方法了。

class Program

{

  static void Main(string[] args)

  {

    //調(diào)用泛型方法

    Console.WriteLine(Compare<int>.compareGeneric(3,4));

    Console.WriteLine(Compare<string>.compareGeneric("abc","a"));

    Console.ReadKey();

  }

}

泛型除了可以實(shí)現(xiàn)代碼重用外,還提供了更好的性能和類型安全特性。

?

3、全面解析泛型

3.1 類型參數(shù)

在前面的泛型代碼中,就是類型參數(shù)。無論調(diào)用類型方法還是初始化泛型實(shí)例,都需要用真實(shí)類型來代替T,可以把T理解為類型的一個(gè)占位符,即告訴編譯器,在調(diào)用泛型時(shí)必須為其指定一個(gè)實(shí)際類型。

根據(jù)泛型類型參數(shù)是否提供實(shí)際類型,又可把泛型分為兩類:未綁定的泛型和已構(gòu)造的泛型。如果沒有為類型參數(shù)提供實(shí)際類型,此時(shí)的泛型稱為未綁定的泛型;而如果已指定了實(shí)際類型作為參數(shù),則此時(shí)的泛型稱為已構(gòu)造的泛型。

已構(gòu)造泛型又可分為開放類型和密封類型。其中,開放類型是指包含類型參數(shù)的泛型,所有未綁定的泛型類型都屬于開放類型;而封閉類型則是指那些已經(jīng)為每一個(gè)類型參數(shù)都傳遞了實(shí)際數(shù)據(jù)類型的泛型。

用以下代碼可以判斷泛型類型是開放還是封閉的。

public class DictionaryStringKey<T>:Dictionary<string,T>{}

class Program

{

  static void Main(string[] args)

  {

    //Dictionary<,>是一個(gè)開放類型,它有兩個(gè)類型參數(shù)

    Type t=typeof(Dictionary<,>);

    Console.WriteLine("是否為開放類型:"+t.ContainsGenericParameters);

    //DictionaryStringKey<>也是一個(gè)開放類型,但它只有一個(gè)類型參數(shù)

    t=typeof(DictionaryStringKey<>);

    Console.WriteLine("是否為開放類型:"+t.ContainGenericParameters);

    //DictionaryStringKey<int>

    t=typeof(DictionaryStringKey<int>);

    Console.WriteLine("是否為開放類型:"+t.ContainGnericParameters);

  }

}

?

3.2 泛型中的靜態(tài)字段和靜態(tài)函數(shù)問題

每個(gè)封閉的泛型類型中都有僅屬于它自己的靜態(tài)數(shù)據(jù)。

//泛型類型,具有一個(gè)類型參數(shù)

public static class TypeWithStaticField<T>

{

  //靜態(tài)字段

  public static string field;

  //靜態(tài)構(gòu)造函數(shù)

  public static void OutField()

  {

    Console.WirteLine(field+":"+typeof(T).Name);

  }

}

//非泛型類

public static class NoGenericTypeWithStaticField

{

  public static string field;

  public static void OutField()

  {

    Console.WriteLine(field);

  }

}

class Program

{

  static void Main(string[] args)

  {

    //使用不同類型實(shí)參來實(shí)例化泛型實(shí)例

    TypeWithStaticField<int>.field="一";

    TypeWithStaticField<string>.field="一";

    TypeWithStaticField<Guid>.field="一";

?

    //對于非泛型類型,此時(shí)field只會一個(gè)值,每次賦值都改變了原來的值

    NoGenericTypeWithStaticField.field="非泛型類靜態(tài)字段一";

    NoGenericTypeWithStaticField.field="非泛型類靜態(tài)字段二";

    NoGenericTypeWithStaticField.field="非泛型類靜態(tài)字段三";

?

    NoGenericTypeWithStaticField.OutField();

    TypeWithStaticField<int>.OutField();

    TypeWithStaticField<string>.OutField();

    TypeWithStaticField<Guid>.OutField();

    Console.ReadKey();

  }

}

結(jié)果如下:

從圖中結(jié)果可以看出,每個(gè)封閉的泛型類型都有屬于它的靜態(tài)字段。這是因?yàn)?#xff0c;在使用實(shí)際類型參數(shù)代替泛型參數(shù)時(shí),編譯器會根據(jù)不同的類型實(shí)參,重新生成類型。

對于編譯器來說,每個(gè)封閉泛型類型都是一個(gè)不一樣的類型,所以它們都有屬于它自己的靜態(tài)字段。靜態(tài)構(gòu)造函數(shù)也一樣,每個(gè)封閉的泛型類型都有一個(gè)靜態(tài)構(gòu)造函數(shù)。

?

3.3 類型參數(shù)的推斷

class Program

{

  static void Main(string[] args)

  {

    int n1=1;

    int n2=2;

    genericMethod(ref n1,ref n2);

    Console.WriteLine("n1的值現(xiàn)在為:"+n1);

    Console.WriteLine("n2的值現(xiàn)在為:"+n2);

  }

?

  //泛型方法

  private static void genericMethod<T>(ref T t1,ref T t2)

  {

    T temp=t1;

    t1=t2;

    t2=temp;

  }

}

在以上的代碼中,編譯器會根據(jù)傳遞的方法實(shí)參來判斷傳入的實(shí)際類型參數(shù)。如果編譯器根據(jù)傳入的參數(shù)不能推斷出實(shí)際參數(shù)類型,就會出現(xiàn)編譯時(shí)錯(cuò)誤。

注意:類型推斷只能用于泛型方法,它對泛型類則并不適用,因?yàn)榫幾g器不能通過泛型類的構(gòu)造函數(shù)推斷出實(shí)際的類型參數(shù)。

?

3.4 類型參數(shù)的約束

不知大家是否注意過,前面的代碼中曾經(jīng)出現(xiàn)過where T:IComparable的代碼,這個(gè)where語句用來使類型參數(shù)繼承于IComparable接口,從而對類型參數(shù)進(jìn)行約束。

前面的比大小中,如果沒有進(jìn)行約束,代碼編譯的時(shí)候就會出現(xiàn)錯(cuò)誤,編譯通不過,所以,對類型參數(shù)進(jìn)行約束是必須的。

C#中有4種約束可以使用,它們的語法類似:約束要放在泛型方法或類型聲明的末尾,并且要使用where關(guān)鍵字。

(1)引用類型約束

表現(xiàn)形式為T:class,它確保傳遞的類型實(shí)參必須是引用類型。

注意,約束的類型參數(shù)和類型本身沒有關(guān)系,即在定義一個(gè)泛型結(jié)構(gòu)體時(shí),泛型類型一樣可以被約束為引用類型。其中有幾個(gè)特殊的引用類型是不能用的:System.Array、System.Delegate、System.MulticastDelegate、System.ValueType、System.Enum、System.Void。

下面代碼定義的泛型類:

public class samplehere<T> where T:Stream

{

  public void Test(T stream)

  {

    strem.Close();

  }

}

從以上的代碼中,類型參數(shù)T設(shè)置了引用類型約束。where T :Stream的意思是告訴編譯器:傳入的類型實(shí)參必須是System.IO.System,或者是從Stream派生的一個(gè)類型。

如果一個(gè)類型參數(shù)沒有指定約束,則默認(rèn)T為System.Object類型。但若你在代碼中顯式地指定了System.Object約束,則編譯器會報(bào)錯(cuò)。

(2)值類型約束

值類型約束的表示形式為T:Struct,它確保傳遞的類型實(shí)參是值類型,包括枚舉。但這里的值類型不包括可空類型。

public class samplevaluetype<T>:struct

{

  public static T Test()

  {

    return new T();

  }

}

在上面的代碼中,new T()是可以通過編譯的,因?yàn)門是一個(gè)值類型,而所有值類型都有一個(gè)公共的無參構(gòu)造函數(shù)。但如果不對T進(jìn)行約束,或約束為引用類型,上面的代碼就會報(bào)錯(cuò)。

(3)構(gòu)造函數(shù)類型約束

構(gòu)造函數(shù)類型約束的表示形似為T:new(),如果類型參數(shù)有多個(gè)約束,則此約束必須最后指定。構(gòu)造函數(shù)類型約束確保指定的類型參數(shù)有一個(gè)公共無參構(gòu)造函數(shù)的非抽象類型。

這里需要注意一點(diǎn):如果同時(shí)指定構(gòu)造器約束和struct約束,C#編譯器會認(rèn)為這是一個(gè)錯(cuò)誤。因?yàn)檫@樣的指定是多余的,所有值類型都隱式地提供了一個(gè)無參公共構(gòu)造函數(shù)。

(4)轉(zhuǎn)換類型約束

轉(zhuǎn)換類型約束的表示形式為T:基類名、T:接口名或T:U。T:基類名確保指定的類型實(shí)參必須是基類或派生自基類的子類;T:接口名確保指定的類型實(shí)參必須是接口或?qū)崿F(xiàn)了該接口的類;而T:U則確保T提供的類型實(shí)參必須是U提供的類型實(shí)參或派生于U提供的類型實(shí)參,即前面一個(gè)類型實(shí)參必須是后面的類型實(shí)參或后面類型的實(shí)參子類。

(5)組合約束

組合約束是將多個(gè)不同種類的約束合并在一起的情況。這里就需要注意,沒有任何一種類型既是引用類型,又是值類型,所以引用約束和值約束不能同時(shí)使用。如果存在多個(gè)轉(zhuǎn)換類型約束,其其中一個(gè)是類,則必須放在接口的前面。不同類型參數(shù)可以有不同的約束,但每種類型參數(shù)必須分別使用一個(gè)單獨(dú)的where關(guān)鍵字。

class Sample<T> where T:class,Disposable,new();

class Sample<T,U> where T:class where U:struct?

?

總結(jié):至此,泛型已經(jīng)講完了,應(yīng)該有一個(gè)全面的認(rèn)識了。不過還是要多敲代碼,不斷實(shí)踐,才能提高。

轉(zhuǎn)載于:https://www.cnblogs.com/Helius/p/5425164.html

總結(jié)

以上是生活随笔為你收集整理的类型参数化-泛型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。