溫馨提示×

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

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

C#泛型的用法及關(guān)鍵字作用

發(fā)布時(shí)間:2021-08-16 18:13:25 來(lái)源:億速云 閱讀:177 作者:chen 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“C#泛型的用法及關(guān)鍵字作用”,在日常操作中,相信很多人在C#泛型的用法及關(guān)鍵字作用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”C#泛型的用法及關(guān)鍵字作用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

一、什么是泛型?

泛型是 2.0 版 C# 語(yǔ)言和公共語(yǔ)言運(yùn)行庫(kù) (CLR) 中的一個(gè)非常重要的新功能。

我們?cè)诰幊坛绦驎r(shí),經(jīng)常會(huì)遇到功能非常相似的模塊,只是它們處理的數(shù)據(jù)不一樣。但我們沒(méi)有辦法,只能分別寫(xiě)多個(gè)方法來(lái)處理不同的數(shù)據(jù)類(lèi)型。這個(gè)時(shí)候,那么問(wèn)題來(lái)了,有沒(méi)有一種辦法,用同一個(gè)方法來(lái)處理傳入不同種類(lèi)型參數(shù)的辦法呢?泛型的出現(xiàn)就是專(zhuān)門(mén)來(lái)解決這個(gè)問(wèn)題的,可以看出,微軟還是很貼心的。

二、為什么要使用泛型?

接下來(lái)我們來(lái)看一段代碼。

public class GenericClass
    {
        public void ShowInt(int n)
        {
            Console.WriteLine("ShowInt print {0},ShowInt Parament Type Is {1}",n,n.GetType());
        }
        public void ShowDateTime(DateTime dt)
        {
            Console.WriteLine("ShowDateTime print {0},ShowDateTime Parament Type Is {1}", dt, dt.GetType());
        }
        public void ShowPeople(People people)
        {
            Console.WriteLine("ShowPeople print {0},ShowPeople Parament Type Is {1}", people, people.GetType());
        }
    }
static void Main(string[] args)
        {
            GenericClass generice = new GenericClass();
            generice.ShowInt(11);
            generice.ShowDateTime(DateTime.Now);
            generice.ShowPeople(new People { Id = 11, Name = "Tom" });

            Console.ReadKey();
        }

顯示結(jié)果:

C#泛型的用法及關(guān)鍵字作用

我們可以看出這三個(gè)方法,除了傳入的參數(shù)不同外,其里面實(shí)現(xiàn)的功能都是一樣的。在1.1版的時(shí)候,還沒(méi)有泛型這個(gè)概念,那么怎么辦呢。就有人想到了OOP三大特性之一的繼承,我們知道,C#語(yǔ)言中,object是所有類(lèi)型的基類(lèi),將上面的代碼進(jìn)行以下優(yōu)化:

public class GenericClass
    {
        public void ShowObj(object obj)
        {
            Console.WriteLine("ShowObj print {0},ShowObj Parament Type Is {1}", obj, obj.GetType());
        }
    }
        static void Main(string[] args)
        {
            Console.WriteLine("*****************object調(diào)用*********************");
            generice.ShowObj(11);
            generice.ShowObj(DateTime.Now);
            generice.ShowObj(new People { Id = 11, Name = "Tom" });

            Console.ReadKey();
        }

顯示結(jié)果:

C#泛型的用法及關(guān)鍵字作用

我們可以看出,目地是達(dá)到了。解決了代碼的可讀性,但是這樣又有個(gè)不好的地方了,我們這樣做實(shí)際上是一個(gè)裝箱拆箱操作,會(huì)損耗性能。

終于,微軟在2.0的時(shí)候發(fā)布了泛型。接下來(lái)我們用泛型方法來(lái)實(shí)現(xiàn)該功能。

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

在使用泛型方法之前,我們先來(lái)了解下有關(guān)于泛型的一些知識(shí)。

在泛型類(lèi)型或方法定義中,類(lèi)型參數(shù)是在其實(shí)例化泛型類(lèi)型的一個(gè)變量時(shí),客戶(hù)端指定的特定類(lèi)型的占位符。 泛型類(lèi)(GenericList<T>)無(wú)法按原樣使用,因?yàn)樗皇钦嬲念?lèi)型;它更像是類(lèi)型的藍(lán)圖。 若要使用GenericList<T>,客戶(hù)端代碼必須通過(guò)指定尖括號(hào)內(nèi)的類(lèi)型參數(shù)來(lái)聲明并實(shí)例化構(gòu)造類(lèi)型。 此特定類(lèi)的類(lèi)型參數(shù)可以是編譯器可識(shí)別的任何類(lèi)型。 可創(chuàng)建任意數(shù)量的構(gòu)造類(lèi)型實(shí)例,其中每個(gè)使用不同的類(lèi)型參數(shù),如下所示:

GenericList<float> list1 = new GenericList<float>();
GenericList<ExampleClass> list2 = new GenericList<ExampleClass>();
GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();

GenericList<T>的每個(gè)實(shí)例中,類(lèi)中出現(xiàn)的每個(gè)T在運(yùn)行時(shí)均會(huì)被替換為類(lèi)型參數(shù)。 通過(guò)這種替換,我們已通過(guò)使用單個(gè)類(lèi)定義創(chuàng)建了三個(gè)單獨(dú)的類(lèi)型安全的有效對(duì)象。

三、泛型約束

定義泛型類(lèi)時(shí),可以對(duì)客戶(hù)端代碼能夠在實(shí)例化類(lèi)時(shí)用于類(lèi)型參數(shù)的幾種類(lèi)型施加限制。 如果客戶(hù)端代碼嘗試使用約束所不允許的類(lèi)型來(lái)實(shí)例化類(lèi),則會(huì)產(chǎn)生編譯時(shí)錯(cuò)誤。 這些限制稱(chēng)為約束。 通過(guò)使用where上下文關(guān)鍵字指定約束。 下表列出了六種類(lèi)型的約束:

C#泛型的用法及關(guān)鍵字作用

where T:結(jié)構(gòu)(類(lèi)型參數(shù)必須是值類(lèi)型??梢灾付ǔ?Nullable 以外的任何值類(lèi)型。)

class MyClass<U>
        where U : struct///約束U參數(shù)必須為“值 類(lèi)型”
 { }

 public void MyMetod<T>(T t)
       where T : struct
 {          
 }

where T:類(lèi)(類(lèi)型參數(shù)必須是引用類(lèi)型;這一點(diǎn)也適用于任何類(lèi)、接口、委托或數(shù)組類(lèi)型。)

class MyClass<U>
        where U : class///約束U參數(shù)必須為“引用類(lèi)型”
 { }

 public void MyMetod<T>(T t)
       where T : class
 {          
 }

where T:new()(類(lèi)型參數(shù)必須具有無(wú)參數(shù)的公共構(gòu)造函數(shù)。當(dāng)與其他約束一起使用時(shí),new() 約束必須最后指定。)

class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

where T:<基類(lèi)名>(類(lèi)型參數(shù)必須是指定的基類(lèi)或派生自指定的基類(lèi)。)

public class Employee{}

public class GenericList<T> where T : Employee

where T:<接口名稱(chēng)>(類(lèi)型參數(shù)必須是指定的接口或?qū)崿F(xiàn)指定的接口。可以指定多個(gè)接口約束。約束接口也可以是泛型的。)

/// <summary>
    /// 接口
    /// </summary>
    interface IMyInterface
    {
    }

    /// <summary>
    /// 定義的一個(gè)字典類(lèi)型
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TVal"></typeparam>
    class Dictionary<TKey, TVal>
        where TKey : IComparable, IEnumerable
        where TVal : IMyInterface
    {
        public void Add(TKey key, TVal val)
        {
        }
    }

where T:U(為 T 提供的類(lèi)型參數(shù)必須是為 U 提供的參數(shù)或派生自為 U 提供的參數(shù)。也就是說(shuō)T和U的參數(shù)必須一樣)

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

以上就是對(duì)六種泛型的簡(jiǎn)單示例,當(dāng)然泛型約束不僅僅適用于類(lèi),接口,對(duì)于泛型方法,泛型委托都同樣適用。

三、泛型方法

public class GenericClass
    {
        public void ShowT<T>(T t)
        {
            Console.WriteLine("ShowT print {0},ShowT Parament Type Is {1}", t, t.GetType());
        }
    }
static void Main(string[] args)
        {
            Console.WriteLine("*****************泛型方法調(diào)用*********************");
            generice.ShowT<int>(11);
            generice.ShowT<DateTime>(DateTime.Now);
            generice.ShowT<People>(new People { Id = 11, Name = "Tom" });

            Console.ReadKey();
        }

顯示結(jié)果:

C#泛型的用法及關(guān)鍵字作用

也是一樣的,現(xiàn)在終于實(shí)現(xiàn)了我們想要達(dá)到的效果了。我們可以看出,無(wú)論是什么方式調(diào)用,最后我們獲取出來(lái)的類(lèi)型都是原始類(lèi)型。我們知道,用object獲取是利用了繼承這一特性,當(dāng)編譯器編譯的時(shí)候,我們傳入的參數(shù)會(huì)進(jìn)行裝箱操作,當(dāng)我們獲取的時(shí)候又要進(jìn)行拆箱操作,這個(gè)方法會(huì)損耗性能 。那么泛型方法實(shí)現(xiàn)的原理又是怎樣的呢?首先,我們要知道,泛型是一個(gè)語(yǔ)法糖,在我們調(diào)用泛型方法,編譯器進(jìn)行編譯時(shí),才會(huì)確定傳入的參數(shù)的類(lèi)型,從而生成副本方法。這個(gè)副本方法與原始方法一法,所以不會(huì)有裝箱拆箱操作,也就沒(méi)有損耗性能這回事了。

四、泛型類(lèi)

泛型類(lèi)封裝不特定于特定數(shù)據(jù)類(lèi)型的操作。

通常,創(chuàng)建泛型類(lèi)是從現(xiàn)有具體類(lèi)開(kāi)始,然后每次逐個(gè)將類(lèi)型更改為類(lèi)型參數(shù),直到泛化和可用性達(dá)到最佳平衡。

創(chuàng)建自己的泛型類(lèi)時(shí),需要考慮以下重要注意事項(xiàng):

  • 要將哪些類(lèi)型泛化為類(lèi)型參數(shù)。

通常,可參數(shù)化的類(lèi)型越多,代碼就越靈活、其可重用性就越高。 但過(guò)度泛化會(huì)造成其他開(kāi)發(fā)人員難以閱讀或理解代碼。

  • 要將何種約束(如有)應(yīng)用到類(lèi)型參數(shù)

其中一個(gè)有用的規(guī)則是,應(yīng)用最大程度的約束,同時(shí)仍可處理必須處理的類(lèi)型。 例如,如果知道泛型類(lèi)僅用于引用類(lèi)型,則請(qǐng)應(yīng)用類(lèi)約束。 這可防止將類(lèi)意外用于值類(lèi)型,并    使你可在 T 上使用 as 運(yùn)算符和檢查 null 值?!     ?/p>

  • 是否將泛型行為分解為基類(lèi)和子類(lèi)。

因?yàn)榉盒皖?lèi)可用作基類(lèi),所以非泛型類(lèi)的相同設(shè)計(jì)注意事項(xiàng)在此也適用。 請(qǐng)參閱本主題后文有關(guān)從泛型基類(lèi)繼承的規(guī)則。

  • 實(shí)現(xiàn)一個(gè)泛型接口還是多個(gè)泛型接口。

class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type 
class NodeOpen<T> : BaseNodeGeneric<T> { }

五、泛型接口

定義一個(gè)泛型接口:

interface IMyGenericInterface<T>
{
}

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

interface IMyGenericInterface<TKey,TValue>
{
}

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

interface IBaseInterface<T> { }

class SampleClass : IBaseInterface<string> { }//如果T有約束,那么string類(lèi)型必須得滿(mǎn)足T的約束

六、泛型委托

委托可以定義它自己的類(lèi)型參數(shù)。 引用泛型委托的代碼可以指定類(lèi)型參數(shù)以創(chuàng)建封閉式構(gòu)造類(lèi)型,就像實(shí)例化泛型類(lèi)或調(diào)用泛型方法一樣,如以下示例中所示:

class Program
    {
        static void Main(string[] args)
        {
            Del<int> m1 = new Del<int>(Notify);
            m1.Invoke(1111);
            Del<string> m2 = new Del<string>(Notify);
            m2.Invoke("字符串");

            Console.ReadKey();
        }

        public delegate void Del<T>(T item);
        public static void Notify(int i) { Console.WriteLine("{0} type is {1}", i,i.GetType()); }
        public static void Notify(string str) { Console.WriteLine("{0} type is {1}", str, str.GetType()); }
       
    }

運(yùn)行結(jié)果:

C#泛型的用法及關(guān)鍵字作用

七、泛型代碼中的默認(rèn)關(guān)鍵字:Default

在泛型類(lèi)和泛型方法中產(chǎn)生的一個(gè)問(wèn)題是,在預(yù)先未知以下情況時(shí),如何將默認(rèn)值分配給參數(shù)化類(lèi)型 T:

  • T 是引用類(lèi)型還是值類(lèi)型。

  • 如果 T 為值類(lèi)型,則它是數(shù)值還是結(jié)構(gòu)。

給定參數(shù)化類(lèi)型 T 的一個(gè)變量 t,只有當(dāng) T 為引用類(lèi)型時(shí),語(yǔ)句 t = null 才有效;只有當(dāng) T 為數(shù)值類(lèi)型而不是結(jié)構(gòu)時(shí),語(yǔ)句 t = 0 才能正常使用。解決方案是使用default關(guān)鍵字,此關(guān)鍵字對(duì)于引用類(lèi)型會(huì)返回空,對(duì)于數(shù)值類(lèi)型會(huì)返回零。對(duì)于結(jié)構(gòu),此關(guān)鍵字將返回初始化為零或空的每個(gè)結(jié)構(gòu)成員,具體取決于這些結(jié)構(gòu)是值類(lèi)型還是引用類(lèi)型。

namespace MyGeneric
{
    class Program
    {
        static void Main(string[] args)
        {
            object obj1=GenericToDefault<string>();
            object obj2 = GenericToDefault<int>();
            object obj3 = GenericToDefault<StructDemo>();
            Console.ReadKey();
        }
        public static T GenericToDefault<T>() 
        {
            return default(T);
        }
    }
    public struct StructDemo
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

運(yùn)行結(jié)果:

C#泛型的用法及關(guān)鍵字作用

C#泛型的用法及關(guān)鍵字作用

C#泛型的用法及關(guān)鍵字作用

到此,關(guān)于“C#泛型的用法及關(guān)鍵字作用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向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