1. 程式人生 > 其它 >看到他我一下子就悟了-- 泛型(1)

看到他我一下子就悟了-- 泛型(1)

1.泛型概念:       本質上,術語”泛型”指的是”引數化型別(parameterized types)”.引數化型別非常重要,因為它們可以在建立類.結構.方法和委託的時候將要操作的資料型別作為參

數進行指定.使用引數化型別的類.結構.方法和委託都可以稱為泛型,如”泛型類”或者”泛型方法”.

       在具體宣告一個變數或者例項化之前,型別引數T只是一個佔位符。等到具體宣告和例項化的時候,編譯器要求程式碼指定型別引數。泛型型別聲明瞭泛型引數佔位符型別,由泛型型別的使用者填寫這些佔位符,並作為泛型的引數提供給泛型型別.

2.泛型約束:約束聲明瞭泛型要求的型別引數的特徵。

    為了宣告一個約束,需要使用where關鍵字,後跟一對”引數:要求”

.其中,”引數”必須是泛型型別中定義的一個引數,而”要求”用於限制類型從

中”派生”的類或介面,或者限制必須存在一個預設構造器,或者限制使用一個引用/值型別約束.

2.1基類約束(where T:base-class-name)

有的時候,你可能需要限制類型從一個特定的類派生.這是用基類約束(base class constraint)做到的.使用基類約束,可以指定某個型別實參

必須繼承的基類.基類約束有兩個重要功能.

  首先,他允許在泛型類中使用由約束指定的基類所定義的成員.例如,可以呼叫基類的方法或者使用基類的屬性.如果沒有基類約束,編譯器就無法知道某

個型別實參擁有哪些成員.通過提供基類約束,編譯器將知道所有的型別實參都擁有由指定的基類所定義的成員.

  基類約束的第二個功能是,確保只適用支援指定基類的型別實參.這意味著對於任意給定的基類約束,型別實參要麼是基類本身,要麼是派生於該基

類.如果試圖使用沒有匹配或者繼承指定的型別實參,就會導致編譯錯誤 例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//泛型:基類約束
//基類約束兩個作用:1.基類約束允許泛型類訪問基類的成員
//                 2.確保只能使用滿足該約束的型別引數,從而實現型別安全 
namespace generic
{
    /*案例描述:假設要建立一個管理電話號碼列表的工具.另外,假定不同組的使用者
     使用的是不同的列表.例如,一個列表用於朋友,令外一個列表用於供應商等.
     */
    /// <summary>
    /// PhoneNumber基類,它用於儲存姓名和姓名相對應的電話號碼
    /// </summary>
    class PhoneNumber
    {
        public string Number { get; set; }
        public string Name { get; set; }
        public PhoneNumber(string n, string num)
        {
            this.Name = n;
            this.Number = num;
        }
    }
    /// <summary>
    /// 朋友電話
    /// </summary>
    class Friend : PhoneNumber
    {
        /// <summary>
        /// 電話號碼是否為工作號碼
        /// </summary>
        public bool IsWorkNumber { get; private set; }
        public Friend(string n, string num, bool wk)
            : base(n, num)
        {
            this.IsWorkNumber = wk;
        }
    }
    /// <summary>
    /// 供應商電話
    /// </summary>
    class Supplier : PhoneNumber
    {
        public Supplier(string n, string num)
            : base(n, num)
        {
        }
    }
/*為了管理電話列表,下面建立一個名為PhoneList的類.由於希望該類能夠
 * 管理任意型別的電話列表,因此將其實現為泛型.另外,由於列表管理的一部分內容是
 * 根據姓名查詢號碼,或者根據號碼查詢姓名,因此要給它新增約束,從而
 * 確保儲存在列表中的物件的型別必須是PhoneNumber派生類的例項
 */
    /// <summary>
    /// 管理任意型別的電話列表
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class PhoneList<T> where T:PhoneNumber
    {
        T[] phList;
        int end;
        public PhoneList()
        {
            phList = new T[10];
            end = 0;
        }
        public bool Add(T newEntry)
        {
            if (end == 10) return false;
            phList[end] = newEntry;
            end++;
            return true;
        }
        public T FindByName(string name)
        {
            for (int i = 0; i < end; i++)
            {
                if (phList[i].Name == name)
                    return phList[i];
            }
            throw new NotFoundException();
        }
        public T FindByNumber(string number)
        {
            for (int i = 0; i < end; i++)
            {
                if (phList[i].Number == number)
                    return phList[i];
            }
            throw new NotFoundException();
        }
    }
    /// <summary>
    /// 此類沒有繼承PhoneNumber,因此不能用於建立PhoneList
    /// </summary>
    class EmailFriend 
    {
    //.....
    }
    /*
     * 這是一個定製異常,雖然該示例只使用預設建構函式,但是出於說明的
     * 目的,NotFoundException實現了Exception定義的所有建構函式
     * 注意:這些建構函式只調用了Exception定義的相等基類建構函式.
     * NotFoundException沒有向Exception新增任何內容,因此不需要
     * 執行任何進一步的操作
     */ 
    class NotFoundException : Exception
    {
        public NotFoundException():base(){}
        public NotFoundException(string str):base( str){}
        public NotFoundException(string str, Exception inner) :
            base(str, inner) { }
        protected NotFoundException(
            System.Runtime.Serialization.SerializationInfo si,
            System.Runtime.Serialization.StreamingContext se) :
            base(si, se) { }
    }
}

怎樣呼叫:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace generic
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClassConstraint();
            Console.Read();
        }

        //基類約束
        public static void BaseClassConstraint()
        {
           //可以通過編譯
            PhoneList<Friend> plist = new PhoneList<Friend>();
            //錯誤新增
            //plist.Add(new Friend() {Name="Tom",Number="555-1234",IsWorkNumber=true });
        //正確新增
            plist.Add(new Friend("Tom", "5555-333", true));
            plist.Add(new Friend("Gary", "5555-332", true));
            plist.Add(new Friend("WangC", "5555-331", false));


            try
            {
                Friend frnd = plist.FindByName("Gary");
                Console.WriteLine(frnd.Name+":"+frnd.Number);

                if(frnd.IsWorkNumber)
                    Console.WriteLine("(work)");
                else
                    Console.WriteLine();
            }
            catch (NotFoundException)
            {
                Console.WriteLine("Not Found");
            }

            //供應商
            PhoneList<Supplier> plist2 = new PhoneList<Supplier>();
            plist2.Add(new Supplier("Global Hardware", "400-123"));
            plist2.Add(new Supplier("Computer", "400-124"));
            plist2.Add(new Supplier("NetWorkCity", "400-125"));

            try
            {
                Supplier sp = plist2.FindByNumber("400-124");
                Console.WriteLine(sp.Name+":"+sp.Number);
            }
            catch (NotFoundException)
            {
                Console.WriteLine("Not Found");
            }

            //沒有繼承的
           // PhoneList<EmailFriend> plist3 = new PhoneList<EmailFriend>();
        }
    }
   
}

 未完待續……