国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

.Net泛型約束

2019-11-14 16:11:46
字體:
供稿:網(wǎng)友

本文內(nèi)容

  • 使用泛型約束的原因
  • 未綁定的類型參數(shù)
  • 作為約束的類型參數(shù)
  • 參考資料

當(dāng)“設(shè)計(jì)模式”出現(xiàn)時(shí),人們提“用接口編程”;后來,有了泛型,人們提“用泛型編程”。什么泛型?比如,單鏈表 LinkedList 場景,每個(gè)節(jié)點(diǎn)包含兩個(gè)字段:值和下一個(gè)節(jié)點(diǎn)的引用,其中,“值”既可以是 int,也可以是 string,甚至是對象,為每個(gè)數(shù)據(jù)類型都寫一個(gè)類,顯然太麻煩,此時(shí)就可以使用泛型 LinkedList <T>T 表示 intstring 類型等等;再如,排序算法中很常見 Swap(ref int a, ref int b) 函數(shù),可以交換兩個(gè) int 類型,當(dāng)然也可以是 string,用泛型也很合適。用 T 代表 intstring,甚至任何類型。

但問題是,實(shí)際項(xiàng)目中用 T 表示任何類型,顯然太粗放。比如,要是用表示動(dòng)物和植物,動(dòng)物和植物可能是接口或基類,顯然動(dòng)物和植物不同,頂多都繼承生物基類或接口,我們倒是希望把 T 限定在動(dòng)物或植物,這樣在定義相應(yīng)的泛型類中就可以使用動(dòng)物或植物的成員——這就是泛型約束。

這就完美了~

所以,實(shí)際項(xiàng)目中T 往往不是任何類型,而是代表某個(gè)類型、某個(gè)基類、某個(gè)接口,說是任何類型,只是泛型表達(dá)自己的理念而已。

下載 Demo

使用泛型約束的原因


如果把 T 限定在某個(gè)基類、某個(gè)接口上,那么泛型類中就可以使用那個(gè)基類或接口中的成員。

如果要檢查泛型列表中的某個(gè)項(xiàng)以確定它是否有效,或者將它與其他某個(gè)項(xiàng)進(jìn)行比較,則編譯器必須在一定程度上保證它需要調(diào)用的運(yùn)算符或方法將受到客戶端代碼可能指定的任何類型參數(shù)的支持。 這種保證是通過對泛型類定義應(yīng)用一個(gè)或多個(gè)約束獲得的。

例如,基類約束告訴編譯器:僅此類型的對象或從此類型派生的對象才可用作類型參數(shù)。 一旦編譯器有了這個(gè)保證,它就能夠允許在泛型類中調(diào)用該類型的方法。約束是使用關(guān)鍵字 where

public class Employee
{
    PRivate string name;
    private int id;
 
    public Employee(string s, int i)
    {
        name = s;
        id = i;
    }
 
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
 
    public int ID
    {
        get { return id; }
        set { id = value; }
    }
}
/// <summary>
/// 員工單鏈表
/// </summary>
/// <typeparam name="T"></typeparam>
public class EmployeeList<T> where T : Employee
{
    /// <summary>
    /// Employee 節(jié)點(diǎn) 
    /// </summary>
    private class Node
    {
        private Node next;
        private T data;
 
        public Node(T t)
        {
            next = null;
            data = t;
        }
 
        public Node Next
        {
            get { return next; }
            set { next = value; }
        }
 
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    }
 
    private Node head;
 
    public EmployeeList()
    {
        head = null;
    }
 
    public void AddHead(T t)
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }
 
    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;
 
        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }
 
    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;
 
        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}

where T : Employee”約束使泛型類可以使用 Employee.Name 屬性,即 current.Data.Name

類型為 T 的所有項(xiàng),都保證是 Employee 對象或從 Employee 繼承的對象。

編譯器除了假設(shè)類型參數(shù)派生自 System.Object 以外,不會(huì)做其他任何假設(shè)。在希望強(qiáng)制兩個(gè)類型參數(shù)之間的繼承關(guān)系的情況下,可對泛型類使用參數(shù)類型約束。

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

約束

說明

T:結(jié)構(gòu)

類型參數(shù)必須是值類型。

T:類

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

T:new()

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

T:<基類名>

類型參數(shù)必須是指定的基類或派生自指定的基類。

T:<接口名稱>

類型參數(shù)必須是指定的接口或?qū)崿F(xiàn)指定的接口。 可以指定多個(gè)接口約束。 約束接口也可以是泛型的。

T:U

為 T 提供的類型參數(shù)必須是為 U 提供的參數(shù)或派生自為 U 提供的參數(shù)。

可以對同一類型參數(shù)應(yīng)用多個(gè)約束,而且約束自身可以是泛型類型,如下所示:

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

這樣就可以增加約束類型及其繼承層次結(jié)構(gòu)中的所有類型所支持的允許操作和方法。 因此,在設(shè)計(jì)泛型類或方法時(shí),如果要對泛型成員執(zhí)行除簡單賦值之外的任何操作或調(diào)用 System.Object 不支持的任何方法,您將需要對該類型參數(shù)應(yīng)用約束。

在應(yīng)用 where T : class 約束時(shí),避免對類型參數(shù)使用 == 和 != 運(yùn)算符,因?yàn)檫@些運(yùn)算符僅測試引用是否相等,而不不是值是否相等。即使在用作參數(shù)的類型中重載這些運(yùn)算符也是如此。下面代碼說明了這一點(diǎn):即使 String 類重載 == 運(yùn)算符,輸出也為 false。

public static void OpTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}
static void Main()
{
    string s1 = "target";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
    string s2 = sb.ToString();
    OpTest<string>(s1, s2);
}

因?yàn)榫幾g器在編譯時(shí)僅知道 T 是引用類型,因此必須使用對所有引用類型都有效的默認(rèn)運(yùn)算符。這就好像對 int 類型和 string 類型的比較,顯然不同。

如果必須測試值是否相等,那么可以使用 where T : IComparable<T> 約束,并在泛型類中實(shí)現(xiàn)該接口。

 

未綁定的類型參數(shù)


沒有約束的類型參數(shù)(如公共類 SampleClass<T>{} 中的 T)稱為未綁定的類型參數(shù)。 未綁定的類型參數(shù)具有以下規(guī)則:

  • 不能使用 != 和 == 運(yùn)算符,因?yàn)闊o法保證具體類型參數(shù)能支持這些運(yùn)算符。
  • 可以在它們與 System.Object 之間轉(zhuǎn)換,或?qū)⑺鼈冿@式轉(zhuǎn)換為任何接口類型。
  • 可以將它們與 null 進(jìn)行比較。將未綁定的參數(shù)與 null 進(jìn)行比較時(shí),如果類型參數(shù)為值類型,則該比較將始終返回 false。

 

作為約束的類型參數(shù)


泛型類有泛型類型參數(shù),泛型類的成員函數(shù)也有自己的泛型參數(shù),但成員函數(shù)的泛型參數(shù)要約束在泛型類型參數(shù)上,此時(shí)就很用,如下示例所示:

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

上面示例中,泛型類型參數(shù) T 在其成員函數(shù) Add 方法中有一個(gè)類型約束 where U : T,其中,Add 方法中使用了泛型 U,而在 List 類中并沒有綁定的類型參數(shù),沒有約束。

類型參數(shù)還可在泛型類定義中用作約束。注意,必須在尖括號(hào)中聲明此類型參數(shù)與任何其他類型的參數(shù):

//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }

 

參考資料


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

 

下載 Demo


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 安国市| 铁岭市| 泗洪县| 营口市| 手游| 六安市| 清水县| 灌南县| 繁峙县| 舟山市| 和政县| 乌兰察布市| 望谟县| 济阳县| 中牟县| 英吉沙县| 哈巴河县| 洞口县| 襄樊市| 东兴市| 平顶山市| 中西区| 南涧| 墨竹工卡县| 南康市| 清丰县| 萨嘎县| 潼南县| 徐州市| 察哈| 南丰县| 呼玛县| 青神县| 正定县| 靖安县| 昌都县| 河北省| 张家港市| 云安县| 红原县| 岳阳市|