C# 泛型編程之泛型類、泛型方法、泛型約束

泛型方法html

    在C#2.0中,方法能夠定義特定於其執行範圍的泛型參數,以下所示:編程

複製代碼
    public class MyClass<T>
    {
        //指定MyMethod方法用以執行類型爲X的參數
        public void MyMethod<X>(X x) 
        {
            //
        }

        //此方法也可不指定方法參數
        public void MyMethod<X>() 
        {
            //
        }
    }   
複製代碼

    即便包含類不適用泛型參數,你也能夠定義方法特定的泛型參數,以下所示:數組

複製代碼
    public class MyClass
    {
        //指定MyMethod方法用以執行類型爲X的參數
        public void MyMethod<X>(X x) 
        {
            //
        }

        //此方法也可不指定方法參數
        public void MyMethod<X>() 
        {
            //
        }
    }
複製代碼
    注意:屬性和索引器不能指定本身的泛型參數,它們只能使用所屬類中定義的泛型參數進行操做。
    在調用泛型方法的時候,你能夠提供要在調用場所使用的類型,以下所示:
//調用泛型方法
MyClass myClass = new MyClass();
myClass.MyMethod<int>(3);
    泛型推理:在調用泛型方法時,C#編譯器足夠聰明,基於傳入的參數類型來推斷出正確的類型,而且它容許徹底省略類型規範,以下所示:
//泛型推理機制調用泛型方法
MyClass myClass = new MyClass();
myClass.MyMethod(3);
    注意:泛型方法沒法只根據返回值的類型推斷出類型,代碼以下:
複製代碼
     public GenericMethodDemo()
     {        
        MyClass myClass = new MyClass();
        /****************************************************
        沒法從用法中推理出方法「GenericMethodDemo.MyClass.MyMethod<T>()」的類型參數。
        請嘗試顯式指定類型參數。
        ***************************************************/
        int number = myClass.MyMethod();
     }

    public class MyClass
    {
        public T MyMethod<T>() 
        {
            //
        }
    }
複製代碼
    泛型方法中泛型參數的約束,以下:
複製代碼
    public class MyClass
    {
        
        public void MyMethod<X>(X x) where X:IComparable<X>
        {
            //
        }
    }
複製代碼
    您沒法爲類級別的泛型參數提供方法級別的約束。類級別泛型參數的全部約束都必須在類做用範圍中定義,代碼以下所示
複製代碼
    public class MyClass<T>
    {
        
        public void MyMethod<X>(X x,T t) where X:IComparable<X> where T:IComparer<T>
        {
            //
        }
    }
複製代碼
 
而下面的代碼是正確的
複製代碼
    public class MyClass<T> where T:IComparable<T>
    {
        
        public void MyMethod<X>(X x,T t) where X:IComparable<X> 
        {
            //
        }
    }
複製代碼
    泛型參數虛方法的重寫:子類方法必須從新定義該方法特定的泛型參數,代碼以下
複製代碼
    public class MyBaseClass
    {
        public virtual void SomeMethod<T>(T t)
        {
            //
        }
    }
    public class MyClass :MyBaseClass
    {
        public override void SomeMethod<X>(X x)
        {
            
        }
    }
複製代碼
 
同時子類中的泛型方法不能重複基類泛型方法的約束,這一點和泛型類中的虛方法重寫是有區別的,代碼以下
複製代碼
    public class MyBaseClass
    {
        public virtual void SomeMethod<T>(T t) where T:new()
        {
            //
        }
    }
    public class MyClass :MyBaseClass
    {
        //正確寫法
        public override void SomeMethod<X>(X x)
        {
            
        }

        ////錯誤 重寫和顯式接口實現方法的約束是從基方法繼承的,所以不能直接指定這些約束
        //public override void SomeMethod<X>(X x) where X:new()
        //{

        //}
    }
複製代碼
    子類方法調用虛擬方法的基類實現:它必須指定要代替泛型基礎方法類型所使用的類型實參。你能夠本身顯式的指定它,也能夠依靠類型推理(若是可能的話)代碼以下:
複製代碼
    public class MyBaseClass
    {
        public virtual void SomeMethod<T>(T t) where T:new()
        {
            //
        }
    }
    public class MyClass :MyBaseClass
    {
        //正確寫法
        public override void SomeMethod<X>(X x)
        {
            base.SomeMethod<X>(x);
            base.SomeMethod(x);
        }
    }
複製代碼
泛型委託
    在某個類中定義的委託能夠使用該類的泛型參數,代碼以下
複製代碼
    public class MyClass<T>
    {
        public delegate void GenericDelegate(T t);
        public void SomeMethod(T t)
        {
 
        }
    }
    public GenericMethodDemo()
    {
        MyClass<int> obj = new MyClass<int>();
        MyClass<int>.GenericDelegate del;
        del = new MyClass<int>.GenericDelegate(obj.SomeMethod);
        del(3);
    }
複製代碼
    委託推理:C#2.0使你能夠將方法引用的直接分配轉變爲委託變量。將上面的代碼改造以下
複製代碼
public class MyClass<T>
    {
        public delegate void GenericDelegate(T t);
        public void SomeMethod(T t)
        {
 
        }
    }
    public GenericMethodDemo()
    {
        MyClass<int> obj = new MyClass<int>();
        MyClass<int>.GenericDelegate del;

        //委託推理
      del = obj.SomeMethod;
        del(3);
     }    
複製代碼
    泛型委託的約束:委託級別的約束只在聲明委託變量和實例化委託時使用,相似於在類型和方法的做用範圍中實施的其餘任何約束。
泛型和反射
    在Net2.0當中,擴展了反射以支持泛型參數。類型Type如今能夠表示帶有特定類型的實參(或綁定類型)或未指定類型的泛型(或稱未綁定類型)。像C#1.1中那樣,您能夠經過使用typeof運算符或經過調用每一個類型支持的GetType()來得到任何類型的Type。代碼以下:
 LinkedList<int> list = new LinkedList<int>();
 Type type1 = typeof(LinkedList<int>);
 Type type2 = list.GetType();
 Response.Write(type1 == type2);
 
     typeof和GetType()也能夠對泛型參數進行操做,以下
複製代碼
public class MyClass<T>
{
    public void SomeMethod(T t)
    {
        Type type = typeof(T);
        HttpContext.Current.Response.Write(type==t.GetType());
    }
}
複製代碼
    typeof還能夠對未綁定的泛型進行操做,代碼以下
複製代碼
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Type unboundType = typeof(MyClass<>);
            Response.Write(unboundType.ToString());
        }
    }

    public class MyClass<T>
    {
        public void SomeMethod(T t)
        {
            Type type = typeof(T);
            HttpContext.Current.Response.Write(type==t.GetType());
        }
    }
複製代碼
 
    請注意"<>"的用法。要對帶有多個類型參數的未綁定泛型類進行操做,請在"<>"中使用","
    Type類中添加了新的方法和屬性,用於提供有關該類型的泛型方面的反射信息,見MSDN。
 
 

.net泛型約束  

 

所謂泛型,即經過參數化類型來實如今同一份代碼上操做多種數據類型。泛型編程是一種編程範式,它利用「參數化類型」將類型抽象化,從而實現更爲靈活的複用。ide

在定義泛型類時,能夠對客戶端代碼可以在實例化類時用於類型參數的類型種類施加限制。若是客戶端代碼嘗試使用某個約束所不容許的類型來實例化類,則會產生編譯時錯誤。這些限制稱爲約束。約束是使用 where 上下文關鍵字指定的。函數

下表列出了五種類型的約束:spa

 

約束 說明

T:struct.net

類型參數必須是值類型。能夠指定除 Nullable 之外的任何值類型。code

T:classhtm

類型參數必須是引用類型,包括任何類、接口、委託或數組類型。blog

T:new()

類型參數必須具備無參數的公共構造函數。當與其餘約束一塊兒使用時,new() 約束必須最後指定。

T:<基類名>

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

T:<接口名稱>

類型參數必須是指定的接口或實現指定的接口。能夠指定多個接口約束。約束接口也能夠是泛型的。

T:U

爲 T 提供的類型參數必須是爲 U 提供的參數或派生自爲 U 提供的參數。這稱爲裸類型約束.

 ---------------------------------------

一.派生約束

1.常見的

public class MyClass5<T> where T :IComparable { }

2.約束放在類的實際派生以後

public class B { }

public class MyClass6<T> : B where T : IComparable { }

3.能夠繼承一個基類和多個接口,且基類在接口前面

public class B { }

public class MyClass7<T> where T : B, IComparable, ICloneable { }

二.構造函數約束

1.常見的

public class MyClass8<T> where T :  new() { }

2.能夠將構造函數約束和派生約束組合起來,前提是構造函數約束出如今約束列表的最後

public class MyClass8<T> where T : IComparable, new() { }

三.值約束

1.常見的

public class MyClass9<T> where T : struct { }

2.與接口約束同時使用,在最前面(不能與基類約束,構造函數約束一塊兒使用)

public class MyClass11<T> where T : struct, IComparable { }

四.引用約束

1.常見的

public class MyClass10<T> where T : class { }

五.多個泛型參數

 public class MyClass12<T, U> where T : IComparable  where U : class { }

六.繼承和泛型

public class B<T>{ }

1. 在從泛型基類派生時,能夠提供類型實參,而不是基類泛型參數

    public class SubClass11 : B<int>
    { }

2.若是子類是泛型,而非具體的類型實參,則能夠使用子類泛型參數做爲泛型基類的指定類型

    public class SubClass12<R> : B<R>
    { }

3.在子類重複基類的約束(在使用子類泛型參數時,必須在子類級別重複在基類級別規定的任何約束)
    public class B<T> where T : ISomeInterface { }
    public class SubClass2<T> : B<T> where T : ISomeInterface { }

4.構造函數約束
    public class B<T> where T : new()
    {
        public T SomeMethod()
        {
            return new T();
        }
    }
    public class SubClass3<T> : B<T> where T : new(){ }

七.泛型方法(C#2.0泛型機制支持在"方法聲名上包含類型參數",這就是泛型方法)

1.泛型方法既能夠包含在泛型類型中,又能夠包含在非泛型類型中

public class MyClass5
    {

        public void MyMethod<T>(T t){ }
    }

2.泛型方法的聲明與調用

public class MyClass5
    {
        public void MyMethod<T>(T t){ }
    }
    public class App5
    {
        public void CallMethod()
        {
            MyClass5 myclass5 = new MyClass5();
            myclass5.MyMethod<int>(3);
        }
    }

3.泛型方法的重載

 //第一組重載
 void MyMethod1<T>(T t, int i){ }

 void MyMethod1<U>(U u, int i){ }

//第二組重載
 void MyMethod2<T>(int i){ }
 void MyMethod2(int i){ }

//第三組重載,假設有兩個泛型參數
 void MyMethod3<T>(T t) where T : A { }
void MyMethod3<T>(T t) where T : B { }

//第四組重載

public class MyClass8<T,U>
    {
        public T MyMothed(T a, U b)
        {
            return a;
        }
        public T MyMothed(U a, T b)
        {
            return b;
        }
        public int MyMothed(int a, int b)
        {
            return a + b;
        }
    }

4.泛型方法的覆寫

(1)public class MyBaseClass1
    {
        public virtual void MyMothed<T>(T t) where T : new() { }
    }
    public class MySubClass1:MyBaseClass1
    {
        public override void MyMothed<T>(T t) //不能重複任何約束
        { }
    }

(2)public class MyBaseClass2
    {
        public virtual void MyMothed<T>(T t)
        { }
    }
    public class MySubClass2 : MyBaseClass2
    {
        public override void MyMothed<T>(T t) //從新定義泛型參數T
        { }
    }

八.虛擬方法

public class BaseClass4<T>
    {
        public virtual T SomeMethod()
        {
            return default(T);
        }
    }
    public class SubClass4 : BaseClass4<int> //使用實參繼承的時候方法要使用實參的類型
    {
        public override int SomeMethod()
        {
            return 0;
        }
    }

    public class SubClass5<T> : BaseClass4<T> //使用泛型繼承時,方法也是泛型
    {
        public override T SomeMethod()
        {
            return default(T);
        }
    }

九.編譯器只容許將泛型參數隱式強制轉換到 Object 或約束指定的類型

class MyClass<T> where T : BaseClass, ISomeInterface
    {
        void SomeMethod(T t)
        {
            ISomeInterface obj1 = t;
            BaseClass obj2 = t;
            object obj3 = t;
        }
    }

變通方法:使用臨時的 Object 變量,將泛型參數強制轉換到其餘任何類型

class MyClass2<T>
    {
        void SomeMethod(T t)
        {
            object temp = t;
            BaseClass obj = (BaseClass)temp;
        }
    }

十.編譯器容許您將泛型參數顯式強制轉換到其餘任何接口,但不能將其轉換到類

class MyClass1<T>
    {
        void SomeMethod(T t)
        {
            ISomeInterface obj1 = (ISomeInterface)t;  
            //BaseClass obj2 = (BaseClass)t;           //不能經過編譯
        }
    }

 

十一.使用臨時的 Object 變量,將泛型參數強制轉換到其餘任何類型

class MyClass2<T>
    {
        void SomeMethod(T t)
        {
            object temp = t;
            BaseClass obj = (BaseClass)temp;
        }
    }

十二.使用is和as運算符

public class MyClass3<T>    {        public void SomeMethod(T t)        {            if (t is int) { }            if (t is LinkedList<int>) { }            string str = t as string;            if (str != null) { }            LinkedList<int> list = t as LinkedList<int>;            if (list != null) { }        }    }

相關文章
相關標籤/搜索