溫馨提示×

溫馨提示×

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

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

C#中泛型指的是什么

發(fā)布時間:2020-08-17 16:42:46 來源:億速云 閱讀:236 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)C#中泛型指的是什么的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。

    在C#開發(fā)中,必不可少的要用到泛型。泛型是.NET2.0版本就有的,它廣泛應(yīng)用于C#框架中容器的使用中。下面我們來詳細(xì)介紹一下。

  一、泛型的主要優(yōu)勢

    1.性能更高。

    2.類型更安全。

    3.代碼更多的重用和擴(kuò)展性。

  二、泛型的基本使用

    泛型的一個主要優(yōu)點是性能,我們來看下面的例子:

static void Main(string[] args)
  {
   //不是泛型的集合類
   ArrayList list = new ArrayList();
   //添加一個值類型 裝箱操作
   list.Add(12);
   //去除第一個元素12 拆箱操作
   int num = (int)list[0];
   Console.WriteLine(num);
   Console.WriteLine("執(zhí)行結(jié)束");
   Console.ReadKey();
  }

元數(shù)據(jù)中ArrayList類的Add方法

//
  // 摘要:
  //  將對象添加到 System.Collections.ArrayList 的結(jié)尾處。
  //
  // 參數(shù):
  // value:
  //  要添加到 System.Collections.ArrayList 末尾的 System.Object。該值可以為 null。
  //
  // 返回結(jié)果:
  //  value 已添加的 System.Collections.ArrayList 索引。
  //
  // 異常:
  // T:System.NotSupportedException:
  //  The System.Collections.ArrayList is read-only.-or- The System.Collections.ArrayList
  //  has a fixed size.
  public virtual int Add(object value);

    相信大家都知道,裝箱拆箱是比較損耗性能的,在執(zhí)行add方法是, 把值類型轉(zhuǎn)換成引用類型(裝箱),取出來時在去拆箱,那怎么樣才能避免這種情況發(fā)生呢?

再來看下面代碼:

//泛型集合類
   List<int> list = new List<int>();
   list.Add(12);
   int num = list[0];
   Console.WriteLine(num);
   Console.WriteLine("執(zhí)行結(jié)束");
   Console.ReadKey();

    這個時候,代碼并沒有發(fā)生裝箱拆箱操作。

元數(shù)據(jù)中List類的Add方法

//
  // 摘要:
  //  將對象添加到 System.Collections.Generic.List`1 的結(jié)尾處。
  //
  // 參數(shù):
  // item:
  //  要添加到 System.Collections.Generic.List`1 末尾的對象。對于引用類型,該值可以為 null。
  public void Add(T item);

    代碼寫到這里時,我們只是創(chuàng)建了一個List泛型集合,你可能還感覺不到泛型優(yōu)勢到底在哪里,你也可能不知道泛型到底是怎么用的。好,下面我們寫個測試還有自己實際應(yīng)用的例子。

測試arraylist和list集合的性能

static void Main(string[] args)
  {
   //不是泛型的集合類
   ArrayList arylist = new ArrayList();
   //添加一個值類型 裝箱操作

   //泛型集合類
   List<int> list = new List<int>();


   //計時類
   Stopwatch watch = new Stopwatch();
   watch.Start();//開始計時
   for (int i = 0; i < 10000000; i++)
   {
    arylist.Add(i);
   }
   watch.Stop();
   Console.WriteLine("Arraylist用時:"+watch.ElapsedMilliseconds);

   Stopwatch watch2 = new Stopwatch();
   watch2.Start();//開始計時
   for (int i = 0; i < 10000000; i++)
   {
    list.Add(i);

   }
   watch2.Stop();
   Console.WriteLine("list用時:" + watch2.ElapsedMilliseconds);
   Console.WriteLine("執(zhí)行結(jié)束");
   Console.ReadKey();
  }

    執(zhí)行結(jié)果:

C#中泛型指的是什么

    以上的例子中,可以看出在計時的過程中代碼寫的重復(fù)了, 怎么解決這個問題呢, 泛型要排上用場了。

    我們想一下,相同的操作(都是循環(huán)添加元素,計算用時)用同一個方法實現(xiàn)不就ok了,只不過這個方法是泛型的(可以接受ArrayList,也可以接受List),下面我們來寫一下這個方法?! ?/p>

//我們用T來代表泛型
  public static long GetTime<T>(T t)
  {
   Stopwatch watch = new Stopwatch();
   watch.Start();//開始計時
   for (int i = 0; i < 10000000; i++)
   {
    t.Add(i);
   }
   watch.Stop();
   return watch.ElapsedMilliseconds;
  }

    但是問題來了, 這里并沒有Add方法讓我們使用啊。 我們的思路是把這個T在調(diào)用時可以當(dāng)作ArrayList類型, 也可以當(dāng)作List類型,這里就設(shè)計到了泛型約束。

  三、泛型約束

    如果使用泛型時, 想要調(diào)用這個泛型類型中的方法, 那么就需要添加約束。泛型約束主要有以下幾種:

約束說明
where T:struct對于結(jié)構(gòu)的約束, T必須是值類型
where T:classT必須是引用類型
where T:ITestT必須實現(xiàn)了ITest接口
where T:TestT必須繼承基類Test
where T:new()T必須有默認(rèn)構(gòu)造函數(shù)
where T:T2T派生自泛型類型T2,也稱為裸類型約束

我們接著上個泛型方法來修改,ArrayList和List都實現(xiàn)了接口IList , 這個時候我們加上這個接口約束;

//我們用T來代表泛型
  public static long GetTime<T>(T t)where T:IList
  {
   Stopwatch watch = new Stopwatch();
   watch.Start();//開始計時
   for (int i = 0; i < 10000000; i++)
   {
    t.Add(i);
   }
   watch.Stop();
   return watch.ElapsedMilliseconds;
  }

調(diào)用結(jié)果:

C#中泛型指的是什么

    代碼寫到這里時,相信你已經(jīng)對泛型有所了解了,但是真要應(yīng)用到自己以后的邏輯編程中時,一定要善于總結(jié):相同類型的相同方法,或者業(yè)務(wù)邏輯相同,只有某個判斷不同時,可以用上泛型,不僅高效還代碼量小。

  四、應(yīng)用場景示范

    在我們的項目開發(fā)中,數(shù)據(jù)庫的增刪改查肯定是少不了的, 在這里我們用泛型來定義增刪改查的泛型類。 之后建立一個用戶表(實際應(yīng)用中對應(yīng)數(shù)據(jù)庫中表結(jié)構(gòu)),數(shù)據(jù)庫中每一個表都可以用泛型類中定義的方法, 不需要每一個都寫增刪改查操作,也是面向?qū)ο缶幊痰囊环N思想:

public class BaseDal<T>where T:class ,new ()
 {
  //以下是增刪查改示范
  public void Query(int id) {
   Console.WriteLine(typeof(T).Name+"查詢方法,id="+id);
  }
  
  public void Update(T t) {

   Console.WriteLine(typeof(T).Name+"更新方法");
  }
  public void Delete(T t)
  {
   Console.WriteLine(typeof(T).Name + "刪除方法");
  }

  public void Add(T t) {
   Console.WriteLine(typeof(T).Name + "添加方法");
  }
 }
public class User
 {
  public int Id { get; set; }
  public string Name { get; set; }
 }

調(diào)用示范

BaseDal<User> dal = new BaseDal<User>();
   var user = new User()
   {
    Id = 0,
    Name = "用戶1"
   };
   dal.Add(user);
   dal.Query(0);
   user.Name = "用戶11";
   dal.Update(user);
   dal.Delete(user);

   Console.ReadKey();

  五、泛型的協(xié)變和抗變

    協(xié)變和抗變主要是對參數(shù)和返回值的類型進(jìn)行轉(zhuǎn)換,在.NET4之后可以通過協(xié)變和抗變?yōu)榉盒徒涌诨蜻@泛型委托添加這個擴(kuò)展。

    現(xiàn)在我們寫倆個類Shape(形狀)、Rectangle(矩形類),Rectangle派生自Shape,寫一個方法public static Rectangle GetRec() ;這個時候你會發(fā)現(xiàn), 方法的泛型類型是抗變的, 就是我返回一個類型為Rectangle但是我可以用Shape來接收, 但泛型在NET4.0之前不支持這個方式, 泛型在NET4.0之后提供了支持泛型接口和泛型委托的協(xié)變和抗變。

普通方法抗變代碼說明

//形狀
 public class Shape
 {
  public double Width { get; set; }
  public double Height { get; set; }

  public override string ToString()
  {
   return string.Format("width:{0},height:{1}",Width,Height);
  }
 }

 //矩形
 public class Rectangle : Shape {

 }

///-----------------------------------方法與調(diào)用

public static Rectangle GetRec() {
   return new Rectangle();
  }
 Shape s = GetRec();
   Console.ReadKey();

    1、泛型接口的協(xié)變

      泛型接口在類型T前加上out關(guān)鍵字,這個時候泛型接口就是協(xié)變的,也就意味著返回類型只能是T。 直接看代碼:

//泛型接口的協(xié)變
 public interface IIndex<out T>
 {
  T GetT(int index);
  int Count { get; }
 }


 public class RecCollection : IIndex<Rectangle>
 {
  private Rectangle[] data = new Rectangle[2] {
   new Rectangle() { Width=10,Height=20 },
   new Rectangle() {Width=20,Height=30 }
  };


  public int Count
  {
   get
   {
    return data.Count();
   }
  }

  public Rectangle GetT(int index)
  {
   return data[index];
  }
 }
//調(diào)用   
  Shape s1 = new RecCollection().GetT(1);
   Console.WriteLine(s1.ToString());

   IIndex<Rectangle> rec = new RecCollection();
   IIndex<Shape> shapes = rec;
   for (int i = 0; i < shapes.Count; i++)
   {
    Console.WriteLine(shapes.GetT(i));
   }
   Console.ReadKey();

以上代碼可以看出, 我們把泛型接口的泛型定義為矩形(Rectangle), 但是在接受的時候可以用基類(Shape) ,在這里實現(xiàn)了協(xié)變。

      2.泛型接口的抗變

        如果泛型類型用in關(guān)鍵字標(biāo)注,那么這個泛型接口就是抗變的,這樣,接口只能把泛型類型T用作其方法的輸入。

//泛型接口的抗變
 public interface IDisplay<in T>
 {
  void Show(T item);
 }

 public class ShapeDisplay : IDisplay<Shape>
 {
  public void Show(Shape item)
  {
   Console.WriteLine(item);
  }
 }
//-------------------------以下調(diào)用------
  Rectangle recs = new Rectangle() { Width=100,Height=200};
  IDisplay<Shape> shapeDisplay = new ShapeDisplay();
  shapeDisplay.Show(recs);

  IDisplay<Rectangle> recDisplay = shapeDisplay;
  recDisplay.Show(recs);
  Console.ReadKey();

以上可以看出泛型也是支持抗變和協(xié)變的。

感謝各位的閱讀!關(guān)于C#中泛型指的是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

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

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

AI