亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C#之泛型詳解

 更新時(shí)間:2022年04月11日 14:46:04   作者:Ruby_Lu  
本文詳細(xì)講解了C#中的泛型,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

泛型不僅是C#的一部分,而且與程序集中的IL代碼緊密地集成。有了泛型,就可以創(chuàng)建獨(dú)立于被包含類(lèi)型的類(lèi)和方法。這樣就可以不必給不同的類(lèi)型編寫(xiě)功能相同的許多方法或類(lèi),只創(chuàng)建一個(gè)方法或類(lèi)即可。
另一個(gè)減少代碼的選項(xiàng)是使用Object類(lèi),因?yàn)镺bject類(lèi)是不安全的。
泛型類(lèi)使用泛型類(lèi)型,并可以根據(jù)需要用特定的類(lèi)型替換泛型類(lèi)型。這就保證了類(lèi)型安全性:如果某個(gè)類(lèi)型不支持泛型類(lèi),編譯器就會(huì)出現(xiàn)錯(cuò)誤。
泛型不僅限于類(lèi),接口和方法也可以使用泛型。
泛型并不是一個(gè)全新的結(jié)構(gòu),在C++中模版就和泛型相似。泛型不僅是C#的一種結(jié)構(gòu),而且是CLR定義的。所以,即使泛型類(lèi)在C#中定義,也可以VB中用一個(gè)特定的類(lèi)型實(shí)例化該泛型。

一.泛型的特性

1.性能

對(duì)值類(lèi)型使用非泛型集合類(lèi),在把值類(lèi)型轉(zhuǎn)換為引用類(lèi)型,和把引用類(lèi)型轉(zhuǎn)換為值類(lèi)型時(shí),需要進(jìn)行裝箱和拆箱操作。雖然操作很容易,但性能損失比較大:

  var list = new ArrayList();
  list.Add(4);//裝箱操作,ArrayList的Add方法參數(shù)是Object
  foreach(int i in list)
  {
    Console.WriteLine(i);//拆箱
  }

System.Collections和System.Collections.Generic是非泛型和泛型集合類(lèi)。System.Collections.Generic中的List<T>類(lèi)不使用對(duì)象,而是在使用時(shí)定義類(lèi)型。

  var list = new List<int>();
  list.Add(4);
  foreach(int i in list)
  {
    Console.WriteLine(i);
  }

2.類(lèi)型安全

使用ArrayList類(lèi)在集合中可以添加任意類(lèi)型。

  var list = new ArrayList();
  list.Add(4);
  list.Add(“66”);

在遍歷時(shí)就會(huì)出現(xiàn)異常,因?yàn)椴皇撬性囟伎梢詮?qiáng)制裝換為INT類(lèi)型:

  foreach(int i in list)
  {
    Console.WriteLine(i);
  }

錯(cuò)誤應(yīng)盡早發(fā)現(xiàn)。使用泛型類(lèi)List<T>,泛型類(lèi)型T定義了允許使用特定的類(lèi)型。有了List<int>的定義,就只能把整數(shù)類(lèi)型添加到集合中。

  var list = new ArrayList();
  list.Add(4);
  list.Add(66);

3.二進(jìn)制代碼的重用

泛型允許更好的重用二進(jìn)制代碼。泛型類(lèi)可以定義一次,并且可以用許多不同的類(lèi)型實(shí)例化。例如List<T>類(lèi)可以用int,string,自定義類(lèi)來(lái)實(shí)例化。
泛型類(lèi)可以在一種語(yǔ)言中定義,在任何其它.NET語(yǔ)言中使用。

4.代碼的擴(kuò)展

因?yàn)榉盒皖?lèi)的定義放在程序集中,所以用特定類(lèi)型實(shí)例化泛型類(lèi)不會(huì)在IL代碼中復(fù)制這些類(lèi)。但是,在JIT編譯器把泛型類(lèi)編譯為本地代碼時(shí),會(huì)給每個(gè)值類(lèi)型創(chuàng)建一個(gè)新類(lèi)。引用類(lèi)型共享一個(gè)本地類(lèi)的所有相同的實(shí)現(xiàn)代碼。這是因?yàn)橐妙?lèi)型在實(shí)例化的泛型類(lèi)中只需要4個(gè)字節(jié)的內(nèi)存地址(32為系統(tǒng)),就可以引用一個(gè)引用類(lèi)型。值類(lèi)型包含在實(shí)例化的泛型類(lèi)的內(nèi)存中,同時(shí)因?yàn)槊總€(gè)值類(lèi)型對(duì)內(nèi)存的要求不同,所以要為每個(gè)值類(lèi)型實(shí)例化一個(gè)新類(lèi)。

5.命名約定

在程序中使用泛型,在區(qū)分泛型類(lèi)型和非泛型類(lèi)型時(shí)使用命名約定就會(huì)有一定幫助。
泛型類(lèi)型的命名規(guī)則:

  • *泛型類(lèi)型的名稱(chēng)用字母T作為前綴。
  • *如果沒(méi)有特殊的要求,泛型類(lèi)型允許用任意類(lèi)代替,且如果只能使用一個(gè)泛型類(lèi)型,就可以用T作為泛型類(lèi)型的名稱(chēng)。
    Public class Person{}
  • *如果泛型類(lèi)型有特定的要求(比如它必須實(shí)現(xiàn)某個(gè)接口或派生自基類(lèi)),或者使用了兩個(gè)或多個(gè)泛型類(lèi)型,就應(yīng)給泛型類(lèi)型使用描述性的名稱(chēng):
    public class SortedList{}

二.使用類(lèi)型

1.先創(chuàng)建一個(gè)非泛型的簡(jiǎn)化鏈表類(lèi)。

public class LinkedListNode
    {
        public LinkedListNode(object value)
        {
            this.Value = value;
        }

        public object Value { get; private set; }//value對(duì)外密封,只可以讀取,設(shè)置值通過(guò)構(gòu)造函數(shù)
        public LinkedListNode Next { get; internal set; }//記錄當(dāng)前節(jié)點(diǎn)的下一節(jié)點(diǎn)
        public LinkedListNode Prev { get; internal set; }//記錄當(dāng)前節(jié)點(diǎn)的上一節(jié)點(diǎn)
    }

public class LinkedList : IEnumerable
    {
        public LinkedListNode First { get; private set; }//記錄鏈表的第一個(gè)節(jié)點(diǎn),外部只可以讀取,設(shè)置值通過(guò)內(nèi)部方法
        public LinkedListNode Last { get; private set; }

        public LinkedListNode AddLast(object node)
        {
            //實(shí)例化一個(gè)LinkedListNode
            var newNode = new LinkedListNode(node);

            //判斷鏈表是否含有元素,如果沒(méi)有就把newNode設(shè)置為鏈表的第一個(gè)元素
            if (First == null)
            {
                First = newNode;
                Last = newNode;
            }
            else
            {
                LinkedListNode previous = Last;//獲取鏈表的最后一個(gè)元素
                Last.Next = newNode;//將newNode賦予最后一個(gè)元素的下一個(gè)元素
                Last = newNode;//重新設(shè)置鏈表的最后一個(gè)元素
                Last.Prev = previous;//newNode成為鏈表的最后一個(gè)元素,將原來(lái)的最后一個(gè)元素設(shè)置為newNode的上一個(gè)元素

                //注意上述順序
            }
            return newNode;
        }

        //實(shí)現(xiàn)IEnumerable接口的GetEnumerator()方法,這樣外部代碼就可以用foreach語(yǔ)句遍歷鏈表
        public IEnumerator GetEnumerator()
        {
            LinkedListNode current = First;
            while (current != null)
            {
                yield return current.Value;//yield創(chuàng)建一個(gè)枚舉器的狀態(tài)機(jī)
                current = current.Next;
            }
        }
    }

客戶(hù)端代碼:

  var list1 = new LinkedList();
  list1.AddLast(2);
  list1.AddLast(4);
  list1.AddLast("22");

  foreach (object i in list1)
  {
    Console.WriteLine(i.ToString());
  }

需要進(jìn)行裝箱拆箱操作。

2.下面編寫(xiě)一個(gè)泛型版本

public class LinkedListNode<T>
    {
        public LinkedListNode(T value)
        {
            this.Value = value;
        }

        public T Value { get; private set; }//value對(duì)外密封,只可以讀取,設(shè)置值通過(guò)構(gòu)造函數(shù)
        public LinkedListNode<T> Next { get; internal set; }//記錄當(dāng)前節(jié)點(diǎn)的下一節(jié)點(diǎn)
        public LinkedListNode<T> Prev { get; internal set; }//記錄當(dāng)前節(jié)點(diǎn)的上一節(jié)點(diǎn)
    }

//該類(lèi)實(shí)現(xiàn)IEnumerable<T>接口,IEnumerable<T>繼承自IEnumerable接口,所以需要實(shí)現(xiàn)GetEnumerator()方法和IEnumerator<T> GetEnumerator()方法
public class LinkedList<T> : IEnumerable<T>
    {
        public LinkedListNode<T> First { get; private set; }//記錄鏈表的第一個(gè)節(jié)點(diǎn),外部只可以讀取,設(shè)置值通過(guò)內(nèi)部方法
        public LinkedListNode<T> Last { get; private set; }

        public LinkedListNode<T> AddLast(T node)
        {
            //實(shí)例化一個(gè)LinkedListNode
            var newNode = new LinkedListNode<T>(node);

            //判斷鏈表是否含有元素,如果沒(méi)有就把newNode設(shè)置為鏈表的第一個(gè)元素
            if (First == null)
            {
                First = newNode;
                Last = newNode;
            }
            else
            {
                LinkedListNode<T> previous = Last;//獲取鏈表的最后一個(gè)元素
                Last.Next = newNode;//將newNode賦予最后一個(gè)元素的下一個(gè)元素
                Last = newNode;//重新設(shè)置鏈表的最后一個(gè)元素
                Last.Prev = previous;//newNode成為鏈表的最后一個(gè)元素,將原來(lái)的最后一個(gè)元素設(shè)置為newNode的上一個(gè)元素

                //注意上述順序
            }
            return newNode;
        }

        //IEnumerable<T>接口繼承IEnumerable
        //實(shí)現(xiàn)IEnumerable<T>接口的GetEnumerator()方法,這樣外部代碼就可以用foreach語(yǔ)句遍歷鏈表
        public IEnumerator<T> GetEnumerator()
        {
            LinkedListNode<T> current = First;
            while (current != null)
            {
                yield return current.Value;//yield創(chuàng)建一個(gè)枚舉器的狀態(tài)機(jī)
                current = current.Next;
            }
        }

        //實(shí)現(xiàn)IEnumerable接口的GetEnumerator()方法
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

客戶(hù)端代碼:

  var list2 = new LinkedList<int>();
  list2.AddLast(2);
  list2.AddLast(4);
  list2.AddLast(44);

  foreach (int i in list2)
  {
    Console.WriteLine(i);
  }

三.泛型類(lèi)的功能

在創(chuàng)建泛型類(lèi)時(shí),有時(shí)需要一些C#關(guān)鍵字。
通過(guò)一個(gè)例子來(lái)介紹這些關(guān)鍵字:

public class DocumentManager<TDocument>
      where TDocument : IDocument
  {
    private readonly Queue<TDocument> documentQueue = new Queue<TDocument>();

    public void AddDocument(TDocument doc)
    {
      lock (this)
      {
        documentQueue.Enqueue(doc);
      }
    }

    public bool IsDocumentAvailable
    {
      get { return documentQueue.Count > 0; }
    }

    public void DisplayAllDocuments()
    {
      foreach (TDocument doc in documentQueue)
      {
        Console.WriteLine(doc.Title);
      }
    }


    public TDocument GetDocument()
    {
      TDocument doc = default(TDocument);
      lock (this)
      {
        doc = documentQueue.Dequeue();
      }
      return doc;
    }

  }

1.默認(rèn)值

在GetDocument()方法中,需要把TDocument指定為null。但是,不能把null賦予泛型類(lèi)型。因?yàn)榉盒皖?lèi)型也可以實(shí)例化為值類(lèi)型,而null只能用于引用類(lèi)型。為了解決這個(gè)問(wèn)題,可以使用default關(guān)鍵字。通過(guò)default,將null賦予引用類(lèi)型,將0賦予值類(lèi)型。

2.約束

如果泛型類(lèi)需要調(diào)用泛型類(lèi)型中的方法,就必須添加約束。

public interface IDocument
  {
    string Title { get; set; }
    string Content { get; set; }
  }

  public class Document : IDocument
  {
    public Document()
    {
    }

    public Document(string title, string content)
    {
      this.Title = title;
      this.Content = content;
    }

    public string Title { get; set; }
    public string Content { get; set; }
  }

在方法中遍歷顯示時(shí),需要使用Document類(lèi)的屬性Title,這就需要約束泛型類(lèi)型TDocument中包含這個(gè)屬性,這里使用泛型類(lèi)型TDocument必須實(shí)現(xiàn)IDocument接口。where子句指定了該約束。

泛型支持的約束類(lèi)型:

約束說(shuō)明
where T:struct對(duì)于結(jié)構(gòu)約束,類(lèi)型T必須是值類(lèi)型
where T:class類(lèi)約束指定類(lèi)型T必須是引用類(lèi)型
where T:IFoo指定類(lèi)型T必須實(shí)現(xiàn)接口IFoo
where T:Foo指定類(lèi)型T必須派生自Foo
where T:new()構(gòu)造函數(shù)約束,指定類(lèi)型T必須有一個(gè)默認(rèn)構(gòu)造函數(shù)
where T1:T2這個(gè)約束指定類(lèi)型T1派生自泛型類(lèi)型T2。該約束也稱(chēng)為裸類(lèi)型約束。

使用泛型類(lèi)型也可以合并多個(gè)約束。where T:IFoo,new(),MyClass

3.繼承

泛型類(lèi)型可以實(shí)現(xiàn)泛型接口,也可以派生自一個(gè)類(lèi)。泛型類(lèi)可以派生自泛型基類(lèi)。要求是必須重復(fù)接口的泛型類(lèi)型,或者必須指定基類(lèi)的類(lèi)型。
派生類(lèi)可以是泛型類(lèi)或非泛型類(lèi)。

4.靜態(tài)成員

泛型類(lèi)的靜態(tài)成員和非泛型類(lèi)的靜態(tài)成員有區(qū)別,泛型類(lèi)的靜態(tài)成員只能在類(lèi)的一個(gè)實(shí)例中共享:

  public class StaticDemo<T>
  {
    public static int x;
  }

客戶(hù)端代碼:

  StaticDemo<string>.x = 4;
  StaticDemo<int>.x = 5;
  //存在兩組靜態(tài)字段。

四.泛型接口

使用泛型可以定義接口,在接口中定義的方法可以帶泛型參數(shù)。

五.泛型結(jié)構(gòu)

泛型結(jié)構(gòu)類(lèi)似于泛型類(lèi),只是沒(méi)有繼承特性。

六.泛型方法

在泛型方法中,泛型類(lèi)型用方法來(lái)定義。泛型方法可以在非泛型類(lèi)中定義。

  void Swap<T>(ref T x,ref T y)
  {
    T temp;
    temp = x;
    x=y;
    y=temp;
  }

  int i=4;
  int j = 5;
  Swap<int>(ref i,ref j);

因?yàn)镃#編譯器會(huì)通過(guò)調(diào)用Swap()方法來(lái)獲取參數(shù)的類(lèi)型,所以不需要把泛型類(lèi)型賦予方法調(diào)用:

  int i=4;
  int j = 5;
  Swap(ref i,ref j);

1.帶約束的泛型方法

  public static class Algorithm
  {

    public static decimal Accumulate<TAccount>(IEnumerable<TAccount> source)
    where TAccount : IAccount
    {
      decimal sum = 0;

      foreach (TAccount a in source)
      {
        sum += a.Balance;
      }
      return sum;
    }

  }

  public interface IAccount
  {
    decimal Balance { get; }
    string Name { get; }
  }


  public class Account : IAccount
  {
    public string Name { get; private set; }
    public decimal Balance { get; private set; }

    public Account(string name, Decimal balance)
    {
      this.Name = name;
      this.Balance = balance;
    }
  }

客戶(hù)端代碼:

  var accounts = new List<Account>()
  {
    new Account("Christian", 1500),
    new Account("Stephanie", 2200),
    new Account("Angela", 1800),
    new Account("Matthias", 2400)
  };

  decimal amount = Algorithm.Accumulate<Account>(accounts);

2.帶委托的泛型方法

  public static class Algorithm
  {
    public static T2 Accumulate<T1, T2>(IEnumerable<T1> source, Func<T1, T2, T2> action)
    {
      T2 sum = default(T2);
      foreach (T1 item in source)
      {
        sum = action(item, sum);
      }
      return sum;
    }

  }

  decimal amount = Algorithm.Accumulate<Account, decimal>(accounts, (item, sum) => sum += item.Balance);

3.泛型方法規(guī)范

泛型方法可以重載,為特定的類(lèi)型定義規(guī)范。這樣適用于帶參數(shù)的方法。

  public class MethodOverloads
  {
    public void Foo<T>(T obj)
    {
      Console.WriteLine("Foo<T>(T obj), obj type: {0}", obj.GetType().Name);
    }

    public void Foo(int x)
    {
      Console.WriteLine("Foo(int x)");
    }

    public void Bar<T>(T obj)
    {
      Foo(obj);
    }
  }

如果傳遞了一個(gè)int,就選擇帶int參數(shù)的方法。對(duì)于其它參數(shù)類(lèi)型,編譯器會(huì)選擇泛型方法。

  var test = new MethodOverloads();
  test.Foo(33);
  test.Foo("abc");

輸出:

Foo(int x)
Foo<T>(T obj), obj type: String

所調(diào)用的方法是在編譯期間定義的,而不是運(yùn)行期間。

test.Bar(44);

輸出:

Foo<T>(T obj), obj type: Int32

Bar()方法選擇了泛型Foo()方法,而不是int參數(shù)重載的方法。因?yàn)榫幾g器是在編譯期間選擇Bar()方法調(diào)用的Foo()方法。由于Bar()方法定義了一個(gè)泛型參數(shù),而且泛型Foo()方法匹配這個(gè)類(lèi)型,所以調(diào)用Foo()方法。

到此這篇關(guān)于C#之泛型的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論