提及泛型咱們簡直太熟悉了,在unity中最直觀的就是dictionary的使用了,咱們只是知道dictionary是一個鍵值對,可是並不知道這個爲何用?在有的時候爲何每每是更重要的。接下來咱們來看看一下使用泛型與沒有使用泛型的時間消耗。編程
//用泛型的方法 private static void TestGeneric() { Stopwatch stopwatch = new Stopwatch(); List<int> list1 = new List<int>(); stopwatch.Start(); for (int i = 0; i < 10000000; i++) { list1.Add(i); } stopwatch.Stop(); TimeSpan timeSpan = stopwatch.Elapsed; string elapedTime = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, timeSpan.Milliseconds ); Console.WriteLine("用泛型的運行時間:" + elapedTime); } //沒有使用泛型的方法 private static void TestNoGeneric() { Stopwatch stopwatch = new Stopwatch(); ArrayList arrayList = new ArrayList(); stopwatch.Start(); for (int i = 0; i < 10000000; i++) { arrayList.Add(i); } stopwatch.Stop(); TimeSpan timeSpan = stopwatch.Elapsed; string elapedTime = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, timeSpan.Milliseconds); Console.WriteLine("沒有使用泛型運行時間:" + elapedTime); } static void Main(string[] args) { TestGeneric(); TestNoGeneric(); Console.ReadKey(); }
圖片爲:c#
結果是一目瞭然,一樣是向集合中添加1000萬的數據,可是用的時間倒是相差這麼多,這是什麼緣由呢?咱們知道方法仍是用通常的方法來表示比較好,因此咱們在沒有泛型的狀況下一般會使用object類型了,可是這個object類型一點很差,咱們在前面知道用這個基類,會有大量的裝箱與拆箱操做,這個就是爲何沒有泛型的集合會有這麼長的時間了。對於C#2的最重要的特性就是泛型的加入了,泛型的做用就是實現代碼的重用,減小裝箱與拆箱的過程,泛型是避免損失的有效方法;並且能夠見掃CPU的損耗。或者咱們能夠這樣理解泛型就是強制你把類型固定,可是這個固定只是發生在你須要它的時候,通常狀況下仍是通常方法的。函數
說真的,dictionary真的沒有什麼好說的,接下來我麼來看看泛型的其餘特性。spa
1在前面是隻是說了,咱們爲何要使用泛型,接下來咱們來看看泛型的定義:pwa
public class Compare<T> where T : IComparable { public static T compareGeneric(T t1, T t2) { if (t1.CompareTo(t2) > 0) return t1; else return t2; } } class Program { static void Main(string[] args) { Console.WriteLine(Compare<int>.compareGeneric(21, 45)); Console.ReadKey(); } }
這個寫法也是很簡單的,對類型進行泛型的定義,而後全部T的類型就是int類型了。3d
2 泛型的類型參數code
泛型分爲開放泛型與封閉泛型。其中開放類型是指包含類型參數的泛型,可是沒綁定類型;封閉類型是指已經爲每個類型參數都傳遞了一數據類型。這個怎麼說呢?仍是dictionary類了,當你沒有使用的時候能夠近似的看做是開放類型,當你使用了就是封閉類型了。orm
//泛型參數 public class DictionaryString<T> : Dictionary<string, T> { } public class Program { static void Main(string[] args) { Type t = typeof(Dictionary<,>); Console.WriteLine("是否爲開放類型:" + t.ContainsGenericParameters); t = typeof(DictionaryString<int>); Console.WriteLine("是否爲開放類型:" + t.ContainsGenericParameters); Console.ReadKey(); } }
這個結果和咱們說的同樣,就是你沒有進行具體的類型賦值,這個就是開放類型;若是你進行了具體的類型賦值的話,這個就是封閉類型。咱們在用的時候仍是封閉類型用的比較多一點,可是咱們仍是要知道這個究竟是什麼一回事情。對象
接下來還有靜態數據類型與泛型的關係。靜態數據類型是屬於類型的,若是定義了一個靜態字段x,則無論以後建立了多少個改該類的實例,也無論派生多個實例,都只存在一個。泛型並不是如此,每一個封閉泛型都具備本身的靜態數據。這個部分咱們只是作一個簡單的說明,其實咱們也是知道的在C#中靜態變量或者方法只是存在一個的,它一旦進行就會值存在一個。blog
3泛型方法和判斷泛型聲明
咱們必定不能把泛型想的太複雜了,泛型方法歸根到底就是方法,它只是把方法通常化了。有的時候,咱們須要將泛型T的做用範圍縮小,直接將其應用在方法上,這樣的話能縮小範圍。這就是泛型方法。
public class GeneMethod { public GeneMethod() { } public static T Max<T>(T value) { Console.WriteLine("value={0}", value); return value; } } class Program { static void Main(string[] args) { Console.WriteLine(GeneMethod.Max<int>(24)); Console.ReadKey(); } }
經過上面的代碼咱們知道泛型方法就是方法,只是將參數所有泛型化,你須要作的就是將泛型參數全被實參化。
4類型參數約束
咱們已經知道泛型方法的寫法了,可是咱們卻發現了一個問題,他就是咱們將這個參數泛型化,咱們就是在特定的地方將這個參數實參話,可是老是有一點很差的,由於咱們知道參數是有不少的類型的,好比說類、枚舉、結構體等,正若有一句話說的同樣,改編不是胡編,戲說不是胡說。咱們須要給泛型加一個限制。這個限制就是類型限制了。
限制的類型分類有不少,可是咱們只說兩種,由於多說無益,其餘也用不到。接口類型約束與構造函數約束。
咱們在前面的比較例子上已經寫過了 where T:IComparable,其中where語句就是用來使類型繼承與IComparable接口,從而對其進行約束。這個就是接口類型約束。
where T:new()這個即時構造函數約束。new()構造函數約束容許開發人員實例化一個泛型類型的對象。new()約束要求類型參數必須提供一個無參數的共有構造函數。使用new()約束時,能夠經過調用該無參數的構造函數來建立對象。
public class SingleTon<T> where T:new () { private static T _instance; public static T Instance { get { if(_instance==null) { _instance = new T(); } return _instance; } } public static T GetInstance() { if (_instance == null) { _instance = new T(); } return _instance; } public void DebugLog() { Console.WriteLine("Hello World"); } } public class Person : SingleTon<Person> { public void PersonPring() { Console.WriteLine("你好"); } } class Program { static void Main(string[] args) { Person.Instance.DebugLog(); Person.GetInstance().PersonPring(); Console.ReadKey(); } }
在上面的代碼中咱們能夠看到singleton是一個泛型類型,這個類型並無指定類型,這個就須要你從新要建立一個類去實參它,而後咱們就會在main方法中去調用它。有一點要注意的是,咱們能夠在泛型類型寫一個沒有參數的構造函數,也能夠不用寫,若是你有參構造函數的話,就必定須要一個無參構造函數了,這個是new中定義的。
5 泛型委託
泛型委託就是將委託泛型化。這樣說也是很簡單的,由於你不管是什麼樣的委託,它都是委託,他都會遵照委託的定義。咱們仍是先看一下泛型委託的代碼吧。
public delegate void Mydelegate<T>(T value); class Program { public static void Method1(int num) { Console.WriteLine("Method1:"+num); } public static void Method2(Mydelegate<string> d) { d("hello world"); } public static void print(string str) { Console.WriteLine("str:" + str); } static void Main(string[] args) { //方法一 Mydelegate<int> d; d = new Mydelegate<int>(Program.Method1); d(1234); //方法二 Program.Method2(Program.print); Console.ReadKey(); } }
從上面的寫法上來看,泛型委託只是將委託參數泛型化。在你使用的話,須要將泛型參數實參化。咱們在上面寫了兩種的方式,一個是將委託先進行實例化,接着在在指定方法,用委託去引用一個方法,接着咱們直接在這個委託中寫參數就好了,由於咱們知道委託的實質就是間接完成某些操做,咱們將參數給這個委託變量的話,它會去找與它進行關聯的方法,而後將本身獲得的參數再給這個方法的,最後由這個方法進行操做的完成。其實咱們在這裏已經知道委託的好處了,咱們在編程的時候將相同的方法寫在一塊兒,而後咱們在別的地方進行調用的話,只須要用委託進行調用了,這樣作的話就會減小代碼的耦合性。咱們在上面的代碼中咱們還看見了第二種方法這個是直接用類+靜態方法的形式來完成的,這樣作的好處是什麼呢?咱們能夠直接用類+靜態方法的形式去調用方法,這樣咱們就不用去構造類了,咱們直接在這個方法中組申明一個委託變量,這樣的話咱們咱們就能夠將一個函數看成一個參數來傳遞,其實也是這樣的話,委託就將方法進行傳遞的咱們在這個方法中獲得來自委託傳遞的參數,就是這樣方法,咱們就能夠在這裏去添加參數,參數會經過委託去傳遞給原來那個關聯的方法,這樣就能夠完成相關的操做了。仍是那句話,委託就是間接去執行操做。由於委託是引用傳遞的方法,因此一處動到處動,這個與值傳遞的方式是不同的。接下來是結果:
6 泛型的重載
其實這個一點都不奇怪,由於普通的方法均可以進行重載,那泛型方法必定是能夠進行重載的,他們的原理都是同樣的,函數名能夠同樣,可是參數的格式不同就好了,這個在泛型方法的重載也是相同的。咱們接下來仍是來看看例子吧:
public class TestClass<T,V> { public T Add(T value1, V value2) { Console.WriteLine("執行了第一個重載方法"); return value1; } public T Add(V value1,T value2) { Console.WriteLine("執行了第二個重載方法"); return value2; } public int Add(int a, int b) { Console.WriteLine("執行了第三個重載方法"); return a + b; } } class Program { static void Main(string[] args) { TestClass<int, int> test = new TestClass<int, int>(); Console.WriteLine(test.Add(12, 35)); Console.ReadKey(); } }
結果爲:
從上面的結果咱們能夠看到雖然方法的名字都是同樣的,可是知道其中的參數不同就好了,系統就能夠進行判斷了,可是咱們若是參數的形式都同樣的話,系統就不能分辨了,由於這樣的話,代碼中就會有兩個相同的方法了,重載方法的本質並非相同的方法,相同的方法就會有錯誤了,因此咱們,只要參數不一致,重載泛型就可使用了。
最後來個小潔,泛型是c#2中很重要的一個特性,它是將方法通常化,可是又增強了約束,看似自相矛盾的做用,倒是泛型的本質。在準備使自由,在使用時約束,這個是時間上的聯繫。
好了咱們的泛型部分就到這裏了,接下來世可空類型---NULL。