基礎拾遺:
前言:
泛型是具有占位符(類型參數)的類、結構、接口和方法,這些占位符是類、結構、接口和方法所存儲或使用的一個或多個類型的占位符。類型參數使得設計類和方法時,不必確定一個或多個具體參數,具體參數可延遲到客戶代碼中聲明、實現。使用泛型類型可以最大限度地重用代碼、保護類型的安全以及提高性能。就像我們寫一個類MyList<T>,客戶代碼可以這樣調用:MyList<int>, MyList<string>或 MyList<MyClass>。方便我們設計更加通用的類型,也避免了容器操作中的裝箱和拆箱操作。
PS:為什么要避免裝箱和拆箱?
我們知道值類型存放在堆棧上,引用類型存放在堆 上。
(1)裝箱:CLR需要做額外的工作把堆棧上的值類型移動到堆上,這個操作就被稱為裝箱.
(2)拆箱:裝箱操作的反操作,把堆中的對象復制到堆棧中,並且返回其值。
裝箱和拆箱都意味着堆和堆棧空間的一系列操作,毫無疑問,這些操作的性能代價是很大的,尤其對於堆上空間的操作,速度相對於堆棧的操作慢得多,並且可能引發垃圾回收,這些都將大規模地影響系統的性能。
1.泛型的約束
在指定一個類型參數時,可以指定類型參數必須滿足的約束條件,約束是使用 where 上下文關鍵字指定的。
下表列出了五種類型的約束:
約束 | 說明 |
---|---|
T:struct |
類型參數必須是值類型。可以指定除 Nullable 以外的任何值類型。 |
T:class |
類型參數必須是引用類型,包括任何類、接口、委托或數組類型。 |
T:new() |
類型參數必須具有無參數的公共構造函數。當與其他約束一起使用時,new() 約束必須最后指定。 |
T:<基類名> |
類型參數必須是指定的基類或派生自指定的基類。 |
T:<接口名稱> |
類型參數必須是指定的接口或實現指定的接口。可以指定多個接口約束。約束接口也可以是泛型的。 |
T:U |
為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。這稱為裸類型約束. |
1.1.派生約束
基本形式 where T:base-class-name
1.1.1.常見的
public class MyClass<T> where T :IComparable { }
1.1.2.約束放在類的實際派生之后
public class B { } public class MyClass<T> : B where T : IComparable { }
1.1.3.可以繼承一個基類和多個接口,且基類在接口前面
基本形式 where T:interface-name
public class B { } public class MyClass<T> where T : B, IComparable, ICloneable { }
1.2.構造函數約束
基本形式: where T : new()
1.2.1.常見的
public class MyClass<T> where T : new() { }
1.2.2.可以將構造函數約束和派生約束組合起來,前提是構造函數約束出現在約束列表的最后
public class MyClass<T> where T : IComparable, new() { }
1.3.值約束
基本形式:where T : struct
1.3.1.常見的
public class MyClass<T> where T : struct { }
1.3.2.與接口約束同時使用,在最前面(不能與基類約束,構造函數約束一起使用)
public class MyClass<T> where T : struct, IComparable { }
1.4.引用約束
基本形式:where T : class
1.4.1.常見的
public class MyClass<T> where T : class { }
1.5.多個泛型參數
基本形式:where T : class where u:class
public class MyClass<T, U> where T : IComparable where U : class { }
1.6.繼承和泛型
public class B<T>{ }
1.6.1. 在從泛型基類派生時,可以提供類型實參,而不是基類泛型參數
public class SubClass : B<int>{ }
1.6.2.如果子類是泛型,而非具體的類型實參,則可以使用子類泛型參數作為泛型基類的指定類型
public class SubClass<R> : B<R>{ }
1.6.3.在子類重復基類的約束(在使用子類泛型參數時,必須在子類級別重復在基類級別規定的任何約束)
public class B<T> where T : ISomeInterface { } public class SubClass2<T> : B<T> where T : ISomeInterface { }
1.6.4.構造函數約束
public class B<T> where T : new() { public T SomeMethod() { return new T(); } } public class SubClass3<T> : B<T> where T : new(){ }
2.泛型繼承
1、泛型類繼承中,父類的類型參數已被實例化,這種情況下子類不一定必須是泛型類;
2、父類的類型參數沒有被實例化,但來源於子類,也就是說父類和子類都是泛型類,並且二者有相同的類型參數;

/如果這樣寫的話,顯然會報找不到類型T,S的錯誤 public class TestChild : Test< T, S> { } //正確的寫法應該是 public class TestChild : Test< string, int>{ } public class TestChild< T, S> : Test< T, S> { } public class TestChild< T, S> : Test< String, int> { }
3.關鍵字default
我們在不知道參數是值類型還是引用類型;是值類型的時候不知道他是結構還是數值的情況下定義了T,如果需要返回泛型類型的默認值則會用到這個關鍵字。這個時候就要用到default了。
1.T是值類型而非結構的則defaultT) 數值類型返回0,字符串返回空
2.T 是非引用類型是結構時候返回初始化為零或空的每個結構成員
3.引用類型返回NULL

class DefaultTest<T> { public T Main() { T t = null; return t; } }
如上如果類型為int則會異常,用 return default(T),就可以避免這種問題。
4.泛型方法
4.1.泛型方法是使用類型參數聲明的方法
void Method<T>(T t){}
也可以寫成
void Method<T>(t){}
4.2.泛型方法也有相應的約束
(1) public void MyMethod<X>(X x) where X:IComparable<X>
(2) public class MyClass<T> where T:IComparable<T> {
public void MyMethod<X>(X x,T t) where X:IComparable<X>
注:實例(2)在類上已經有相應T的約束,在方法中就不能在給T加新的約束了。
4.3.泛型虛方法
泛型虛方法在重寫的時候,一定要重新定義泛型,並且也不能重復基類的虛方法約束。
如下:

public class BaseClass { public virtual void Method<T>(T t) { // } } public class Class :BaseClass { public override void Method<X>(X x) { // } }
5.泛型接口
5.1.泛型接口實

interface IPerson<T> { void add( T t); } class PersonManager : IPerson<Person> { #region IPerson<Person> 成員 public void add( Person t ) { // }

//一個接口可定義多個類型參數 interface IDictionary<K, V> { // }
5.2.多重接口可作為單個類型上的約束
class Stack<T> where T : System.IComparable<T>, IEnumerable<T>
5.3.泛型接口繼承也遵循類之間的規則

interface IMonth<T> { } interface IJanuary : IMonth<int> { } //No error interface IFebruary<T> : IMonth<int> { } //No error interface IMarch<T> : IMonth<T> { } //No error //interface IApril<T> : IMonth<T, U> {} //Error
6.泛型數組
下限為零的一維數組自動實現 IList<T>。 這使您可以創建能夠使用相同代碼循環訪問數組和其他集合類型的泛型方法。 此技術主要對讀取集合中的數據很有用。 IList<T> 接口不能用於在數組中添加或移除元素。 如果嘗試對此上下文中的數組調用 IList<T> 方法(例如 RemoveAt),則將引發異常。
7.泛型委托
ps:委托是什么?
使用委托可以將方法作為參數進行傳遞。委托是一種特殊類型的對象,其特殊之處在於委托中包含的只是一個活多個方法的地址,而不是數據。定義方式如:public delegate void MethodDelegate();稍后的文章會有詳細介紹。
http://www.cnblogs.com/kmonkeywyl/p/5626432.html這篇文章敘述了我在封裝此類庫的時候泛型委托就起了很大作用。

public delegate IHttpActionResult apiaction(); public delegate IHttpActionResult apiaction_l(long args); public delegate IHttpActionResult apiaction_ll(long args1, long args2); public delegate IHttpActionResult apiaction_li(long args1, int arg2); public delegate IHttpActionResult apiaction_ls(long args1, string args2); public delegate IHttpActionResult apiaction_i(int args1); public delegate IHttpActionResult apiaction_ii(int args1, int args2); public delegate IHttpActionResult apiaction_is(int args1, string args2); public delegate IHttpActionResult apiaction_il(int args1, long args2); public delegate IHttpActionResult apiaction_si(string args1, int args2); public delegate IHttpActionResult apiaction_ss(string args1, string args2); public delegate IHttpActionResult apiaction_sl(string args1, long args2); public delegate IHttpActionResult apiaction_sss(string args1, string args2, string args3); public delegate IHttpActionResult apiaction_o<treq>(treq data) where treq : class,new();
7.1.首先介紹兩個特殊的泛型委托Action<T>和Fun<TResult>
Action<T>只能委托必須是無返回值的方法
Fun<TResult>只是委托必須有返回值的方法
針對於Action<T>和Fun<TResult>會在后邊委托篇章里介紹。這邊只需知道泛型委托有兩個特殊的他們即可。
7.2.由於泛型的引入,所以一些內建(Built-in)的類、接口、委托都有了各自的泛型版本。
EventHandler也不例外,它有了自己的泛型版本:EventHandler<T>。
定義如下:

[Serializable] public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs;
第二個參數的類型由EventArgs變成了TEventArgs,而TEventArgs具體是什么,則由調用方決定。假設IntEventArgs和StringEventArgs都繼承於System.EventArgs,那么:
1.EventHandler<IntEventArgs>指代這樣一類函數:這些函數沒有返回值,有兩個參數,第一個參數是object類型,第二個參數是IntEventArgs類型
2.EventHandler<StringEventArgs>指代這樣一類函數:這些函數沒有返回值,有兩個參數,第一個參數是object類型,第二個參數是StringEventArgs類型
其實EventHandler<IntEventArgs>和EventHandler<StringEventArgs>是兩個完全不同的委托,它們所指代的函數都分別有着不同的簽名形式。請參見下面的示例:

class IntEventArgs : System.EventArgs { public int IntValue { get; set; } public IntEventArgs() { } public IntEventArgs(int value) { this.IntValue = value; } } class StringEventArgs : System.EventArgs { public string StringValue { get; set; } public StringEventArgs() { } public StringEventArgs(string value) { this.StringValue = value; } } class Program { static void PrintInt(object sender, IntEventArgs e) { Console.WriteLine(e.IntValue); } static void PrintString(object sender, StringEventArgs e) { Console.WriteLine(e.StringValue); } static void Main(string[] args) { EventHandler<IntEventArgs> ihandler = new EventHandler<IntEventArgs>(PrintInt); EventHandler<StringEventArgs> shandler = new EventHandler<StringEventArgs>(PrintString); ihandler(null, new IntEventArgs(100)); shandler(null, new StringEventArgs("Hello World")); } }