一.why:我們為什么要用泛型?
首先來看一下這個例子:我們列印不同型別的值的寫法
/// <summary> /// 列印一個int值 /// </summary> public static void ShowInt(int iValue) { Console.WriteLine("This is ShowInt,parameter={0},type={1}", iValue, iValue.GetType().Name); } /// <summary> /// 列印一個string型別的值 /// </summary> public static void ShowString(string sValue) { Console.WriteLine("This is ShowString,parameter={0},type={1}", sValue, sValue.GetType().Name); } /// <summary> /// 列印people類 /// </summary> public static void ShowPeople(People people) { Console.WriteLine("This is ShowPeople,parameter={0},type={1}", people, people.GetType().Name); }
/*普通方法,object,泛型呼叫引數里面的方法都來源于次定義*/
int iValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/20201026; string sValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/"泛型Generic"; People people = new People() { Age = 11,Name="測驗" }; /*普通方法*/ Console.WriteLine("--------------------普通方法:-----------------------"); Common.ShowInt(iValue); Common.ShowString(sValue); Common.ShowPeople(people);
結果:
思考1:難道每次需要一個型別,就來定義一個方法,那我們需要很多個不同的型別呢,以上方法有什么區別呢? 怎么才可以只寫一個方法同時呼叫不同的型別呢?
1.引數不一樣,引數型別不一樣,所以沒辦法去呼叫同一個,
思考2:那怎么寫一個方法不同的型別都可以呼叫?在1.0版的時候,還沒有泛型這個概念,那么怎么辦呢?
為什么可以同時傳int,string型別,我們說過:object型別是一切型別的父類,同時面向物件的特性中:
繼承:c#三大特性中的繼承,子類擁有父類的一切屬性和方法;
里氏替換原則:任何父類出現的地方都可以用子類去替換,我們是不是就可以優化為以下寫法;
/// <summary> /// c#三大特性中的繼承,子類擁有父類的一切屬性和方法,里氏替換原則:任何父類出現的地方都可以用子類去替換 /// object型別是一切型別的父類 /// </summary> public static void ShowObject(Object oValue) { Console.WriteLine("This is ShowObject,parameter={0},type={1}", oValue, oValue.GetType().Name); }
/*普通方法,object,泛型呼叫引數里面的方法都來源于次定義*/
int iValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/20201026; string sValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/"泛型Generic"; People people = new People() { Age = 11,Name="測驗" };
Console.WriteLine("--------------------object-----------------------"); Common.ShowObject(iValue); Common.ShowObject(sValue); Common.ShowObject(people);
結果是不是也是一樣:使用Object型別達到了我們的要求,很好的解決了代碼的可復用

那么我前面說過【https://www.cnblogs.com/wangwangwangMax/p/5425361.html】,陣列,物件,string是參考型別,它們的值是存在堆里面的,而所有的值型別都是存盤于堆疊里面,使用object型別,就會存在裝箱拆箱,性能損耗方面的問題,
而微軟在C#2.0的時候推出了泛型,就可以很好的解決上面的問題,那下面我們就寫一個泛型方法來比較一下,
1 寫法結構:方法后面有一個<>尖括號,eg:ShowList<T>(T Value),型別引數T實際上就是一個型別宣告,表示是一個占位符,也可以使用任何非保留字,eg:S 或者 W
/// <summary> /// 宣告一個泛型方法 /// 結構:方法后面有一個<>尖括號ShowList<T>(T Value) /// </summary> /// <typeparam name="T">typeparam:型別引數,T實際上就是一個型別宣告,表示是一個占位符,也可以使用任何非保留字</typeparam> /// <param name="Value">param:引數</param> /// 正因為沒有寫死,才擁有了無限的可能 public static void ShowList<T>(T Value) { Console.WriteLine("This is ShowList,parameter={0},type={1}", Value, Value.GetType().Name); }
/*普通方法,object,泛型呼叫引數里面的方法都來源于次定義*/
int iValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/20201026; string sValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/"泛型Generic"; People people = new People() { Age = 11,Name="測驗" };
Console.WriteLine("--------------------泛型的宣告和使用-----------------------"); Generic.ShowList<int>(iValue); Generic.ShowList<string>(sValue); Generic.ShowList<People>(people);
以上呼叫,我們還可以寫成如下格式:
Generic.ShowList(iValue);
Generic.ShowList(sValue);
Generic.ShowList(people);
如果可以從引數型別推斷,那么就可以省略型別引數,這種由編譯器提供的功能就叫做語法糖
結果:

這個地方,我們在呼叫泛型方法的時候,才指定了具體的型別;
總結: 【泛型說白就是一個東西,完成以前多個東西能完成的東西】
二.那泛型究竟是如何How作業的呢?

泛型原理:宣告的不知道是什么型別,編譯的時候用【占位符-`1、`2】來表示
呼叫的時候再來指定具體的引數型別
思想:【延遲宣告:推遲一切可以推遲的東西,能晚點做的事,就晚點做】
三.那他們三者之間的性能又如何呢?
我們寫一個測驗例子看一下,
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LearnGenericFirst1 { /// <summary> /// 性能測驗 /// </summary> public class Monitor { public static void Show() { Console.WriteLine("--------------------Monitor-----------------------"); int iValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/20201031; string sValue = https://www.cnblogs.com/wangwangwangMax/archive/2020/11/02/"20201031"; long commonSecond = 0; long objectSecond = 0; long genecirSecond = 0; { Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 100_000_000; i++) { ShowInt(iValue); } watch.Stop(); commonSecond = watch.ElapsedMilliseconds; Console.WriteLine("回圈1億次,普通方法需要{0}ms", commonSecond); } { Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 100_000_000; i++) { //intList.Add(i);//public virtual int Add(object value);存在裝箱拆箱操作 ShowObject(iValue);//存在裝箱拆箱操作 } watch.Stop(); objectSecond = watch.ElapsedMilliseconds; Console.WriteLine("回圈1億次,object需要{0}ms", objectSecond); ShowObject(sValue); // ShowObject(sValue);("arrylist還能再添加一個string型別的資料進去,指定int,中途插入一個別的型別進去,處理資料的時候就會出問題,而泛型則不能," + // "相比較更好的確保了宣告型別的呼叫,安全"); } { Stopwatch watch = new Stopwatch(); watch.Start(); List<int> intList = new List<int>();//iValue for (int i = 0; i < 100_000_000; i++) { //intList.Add(i);//public void Add(T item);泛型類下面的泛型方法 Show(iValue); } watch.Stop(); genecirSecond = watch.ElapsedMilliseconds; Console.WriteLine("回圈1億次,泛型需要{0}ms", genecirSecond); } } #region privateMethod private static void ShowInt(int iParameter) { } private static void ShowObject(object iParameter) { } private static void Show<T>(T tParameter) { } #endregion } }普通方法,object與泛型的性能測驗,點擊左邊的+即可查看
Console.WriteLine("--------------------普通方法,object與泛型的性能測驗-----------------------"); Monitor.Show();
結果:

結論:泛型方法的性能跟普通方法一致,是最好的,而且還能一個方法同時滿足多個不同型別,相比object,泛型更安全
四.泛型類 class+類名+<尖括號>
以上我們說了泛型方法,下面說一下泛型類,結構,class+類名+<尖括號>
///一個類,滿足不同型別的需求 public class GenericClass<S, T> { /// <summary> /// Show<S, T,W>如果類里面宣告了型別,方法里面就不用再次宣告,下面列舉兩種寫法的延伸 /// </summary> /// <typeparam name="W">Show<W>S s,T t,W w也可以小寫,s,t都來自泛型類,w則來自于泛型方法</typeparam> public static void Show<W>(S s,T t,W w) { } public static void Show(S s, T t) { } public static void Show() { } }
五.泛型介面,interface+介面名+<尖括號>
/// <summary> /// 泛型介面:沒有public static /// 一個介面滿足不同型別的需求; /// </summary> /// <typeparam name="X"></typeparam> /// <typeparam name="Y"></typeparam> public interface IGenericInterface<X,Y> { void Show<W>(X x, Y y, W w); void Show(X x, Y y); void Show(); //泛型型別的回傳值 X GetT(X x); }
六.泛型委托也是差不多的結構 delegate +回傳值+命名+<尖括號>
/// <summary> /// 泛型委托:一個委托滿足不同型別的需求 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="W"></typeparam> public delegate void DoSomethingHandler<T, W>();
七.泛型類,介面繼承的問題
public class GenericClassChild2 : GenericClass<S, T> { }
思考:參考上面一個列子,在代碼里面,還沒編譯,繼承了一個泛型的父類,這種方式為什么會報錯呢?
子類GenericClassChild2本身沒有一個泛型的概念【非泛型,封閉/普通類:所有型別都是確定的】,它不知道父類【非封閉型別/開放性型別:泛型型別沒指定】的<S, T>是什么型別,在呼叫的時候必須確定型別,
如果父類是個非封閉性型別,那么它無法作為封閉型別的父類,可以把父類指定成具體型別,如下寫法【泛型型別指定,變成了封閉型別】就可以繼承父類了
/// <summary> /// 泛型類的繼承實體1 /// </summary>
public class GenericClassChild2 : GenericClass<int,string> { }
/// <summary> /// 泛型類的繼承實體2 /// </summary> public class GenericClassChild1<S, T> : GenericClass<S, T> { }
/// <summary> /// 泛型類的繼承實體3--父類是個封閉型別【已經指定好了型別就不是泛型類】也就是一個普通型別,而子類是一個泛型,這個可以有 /// </summary> public class GenericClassChild2<T> : GenericClass<int, string> { }
/// <summary> /// 泛型介面的繼承原理和泛型類繼承一樣 /// </summary> public interface IGenericInterfaceChild1<X,Y>: IGenericInterface<X,Y> { } public interface IGenecirInterfaceChild2 : IGenericInterface<int, string> { } public interface IGenecirInterfaceChild2<S,T> : IGenericInterface<int, string> { }
八.泛型約束
泛型一個東西可以當做多個東西來使用,滿足不同的引數型別,正是沒有約束,任意型別都可以傳進來,我們也不知道它是什么,所以我們就會有一些約束的概念,泛型約束一共有⑤種

為什么要使用約束呢?讓我們先來看一段下面的例子:
1 /// <summary> 2 /// People物體 3 /// </summary> 4 public class People 5 { 6 public int Id { get; set; } 7 public int Age { get; set; } 8 public string Name { get; set; } 9 public void SayHi() 10 { 11 Console.WriteLine("Hi, {0} Good,Morning!",this.Name); 12 } 13 } 14 15 /// <summary> 16 /// 中國人物體 17 /// </summary> 18 public class Chinese : People,iSports 19 { 20 public string KangFu { get; set; } 21 public string Characteristic { get; set; } 22 public void Pingpang() 23 { 24 Console.WriteLine("Pingpang"); 25 } 26 public void Majiang() 27 { 28 Console.WriteLine("Majiang"); 29 } 30 } 31 32 public class Japanese : iSports 33 { 34 public int Id { get; set; } 35 public string Name { get; set; } 36 37 public void Pingpang() 38 { 39 Console.WriteLine("打乒乓球"); 40 } 41 } 42 43 /// <summary> 44 /// 介面 45 /// </summary> 46 public interface iSports 47 { 48 void Pingpong(); 49 }需要使用到的物體類
/// <summary> /// ShowObject方法 /// </summary> /// <param name="oValue"></param> public static void ShowObject(Object oValue) { Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenecirConstraint), oValue, oValue.GetType().Name); People people = (People)oValue; Console.WriteLine("This is {0},people.Id={1},people.name={2}", typeof(GenecirConstraint), people.Id, people.Name); people.SayHi(); }
Chinese chinese = new Chinese() { Id = 5, Name = "湖南" }; Japanese japanese = new Japanese() { Id = 6, Name = "福原愛" }; GenecirConstraint.ShowObject(chinese); GenecirConstraint.ShowObject(japanese);

思考1:上面這面代碼為什么會報錯呢?
Object 方法因為可以出入任何型別,沒有限制,如果傳入的型別不匹配,就會發生例外(型別安全問題),就像交通行駛,有了交規,有了紅綠燈,才更安全

2.思考2:why為什么普通方法可以直接呼叫people類里面的屬性和方法,為什么泛型做不到?--因為被限制了,那我怎么讓泛型可以呼叫people類里面的屬性和方法呢?這時候我們就用到了泛型約束
1.泛型類約束:【:where T 類名】
/// <summary> /// 泛型約束:沒有約束就沒有自由 /// 不管是泛型類,泛型介面,泛型委托后面都可以使用泛型約束 /// 結構:Show<S>(S 后面+Where 宣告型別S :【<S>(S Value) where S :】表明這個泛型"S"必須滿足這個People的約束 /// where T:BaseModel 【where:保留關鍵字,用來做泛型約束的; :表示被誰約束】 /// </summary>
/// <summary> /// 這是基類約束,它有兩個特點 /// 第一個特點:可以把T當成一個基類,我們可以直接使用它里面的屬性和方法--有了約束,就有了權力;
///如果只是單純的傳一個People,那么性能上跟上面例子中的普通方法沒什么區別,但是使用泛型會更加靈活 /// </summary> public static void Show<S>(S sValue) where S:People { Console.WriteLine("This is Show,parameter={0},type={1}", sValue, sValue.GetType()); Console.WriteLine("This is Show,people.Id={0},people.name={1}", sValue.Id,sValue.Name); sValue.SayHi(); }
思考:為什么呼叫這個方法傳入一個iValue,sValue它會報錯,而people不會?

Console.WriteLine("--------------------泛型基類約束的呼叫-----------------------"); GenecirConstraint.Show(people); GenecirConstraint.Show<Chinese>(new Chinese() { Id = 1, Name = "重慶", Characteristic = "重慶火鍋" }); //可以直接把型別引數去掉不寫,編譯器會自動推算 GenecirConstraint.Show(new Chinese() { Id = 2, Name = "上海", Characteristic = "上海混沌" });
Console.WriteLine("結論:泛型基類約束的第二大特性,在呼叫的時候,只能傳入它或者它的子類【只能是繼承了它的子類】");
結果:

2.泛型介面約束:【:where T 介面名】
/// <summary> /// 介面約束,只能使用介面里面的方法 /// </summary> /// <typeparam name="S"></typeparam> /// <param name="sValue"></param> public static void Sports<S>(S sValue) where S : iSports { Console.WriteLine("This is Show,parameter={0},parameter={1}", sValue, sValue.GetType().Name); sValue.Pingpang(); }
Console.WriteLine("--------------------泛型介面約束呼叫:-----------------------"); GenecirConstraint.Sports(new Chinese() { Id = 3, Name = "北京", Characteristic = "北京烤鴨" });
//物體類里japanese繼承了iSports所以可以了呼叫此方法 GenecirConstraint.Sports(japanese); //物體類里people類沒有繼承了iSports介面所以不可以了呼叫此方法 //GenecirConstraint.Sports(people);
結果:

public interface iDaily { void Majiang(); }
/// <summary> /// 同時被一個類和介面約束,:基類在前面,介面在后面 /// 只能被一個父類約束,但可以被多個介面約束 /// </summary> /// <typeparam name="S"></typeparam> /// <param name="sValue"></param> public static void ClassInterFaseMeanwhile<S>(S sValue) where S : People, iSports, iDaily { Console.WriteLine("This is Show,parameter={0},parameter={1}", sValue, sValue.GetType().Name); Console.WriteLine("This is Show,people.Id={0},people.name={1}", sValue.Id, sValue.Name); sValue.SayHi(); sValue.Pingpang(); sValue.Majiang(); }
Console.WriteLine("--------------------泛型基類和介面約束同時呼叫:-----------------------"); GenecirConstraint.ClassInterFaseMeanwhile(chinese);
結果:

3.除了泛型類約束,介面約束,還有三種的基本的約束,這三種約束比較特殊,分別是:參考型別約束,值型別約束,無引數建構式約束
/// <summary> ///參考型別約束:【 where T : class ,T一定是一個參考型別】 ///where T : String //密封內約束的不行,因為沒有意義 /// </summary> /// <returns></returns> public static T GetT<T>() where T : class //T tParameter { return null;//參考型別都可以回傳null,如果沒有where T : class這個約束就會報錯,它還有可能會是值型別 } /// <summary> /// 值型別約束:【where T : struct】 /// </summary> public static T GetValuetype<T>(T tParameter) where T : struct { //return 0;//這邊回傳0為什么會報錯呢?值型別不僅是int,還有其它型別,eg:長整型,小數 return default(T);//default是一個關鍵字,它會根據T的型別去獲取一個默認值 } /// <summary> /// 無引數建構式約束【where T : new()】 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public T GetParameterlessConstructor<T>() where T : new() { return new T();// }
/// <summary> /// 幾個約束可以疊加,用逗哈隔開即可 /// 如果有多個引數的情況,約束可以直接寫,不需要用逗號 /// </summary> /// <typeparam name="T"></typeparam> public static T Getconstraint<T,S>() where T : Chinese, iSports, iDaily, new()//,class無意義 where S:class { return new T();// }
九. 協變 逆變
1.什么是協變逆變?產生的背景?它的前提是什么,它能做些什么?

1. 協變逆變:泛型委托,泛型介面,前面又加上了 in , out ,所以稱為了泛型的協變,逆變,讓我們繼續往下面看例子
//定義了一個動物的物體類 public class Animal { public int Id { get; set; } } //定義了一個貓貓的物體類,繼承Animal類 public class Cat:Animal { public string Name { get; set; } }
2.產生的背景:
我們在平時寫代碼的時候,是不是就會遇到這樣的情況,這是普通方法的寫法
{ Animal animal1 = new Animal(); Animal animal2 = new Cat();//父類變數用子類實體化,貓咪當然是動物 Cat cat1 = new Cat(); //Cat cat2 = new Animal();//子類變數不能用父類實體化 //父類出現的地方可以用子類代替,子類出現的地方則不能用父類代替,貓咪屬于動物,動物不一定只有貓咪 }
但是,我們在用泛型寫的時候,卻怎么都不對?Why?

//當然我們可以用這種方式實作 List<Animal> animals3 = new List<Cat>().Select(c => (Animal)c).ToList();
1 /*按照上面普通方法的邏輯,貓屬于動物,可是這種寫法為什么不可以呢?Why不成立呢?*/ 2 //List<Animal>是個類,List<Cat>也是個類,前面說過泛型編譯的時候只是個占位符,呼叫的時候就確定了,前面就確定了是List<Animal>, 3 //而后面又是List<Cat>,二者沒有父子關系,List沒關系 4 //換句話說就是:你是個list實體,我也是個list 實體,我們又不是繼承關系,所以不能 5 //語法不通過,語意上可以,這就是協變逆變產生的背景 7 //我們使用泛型的原因也正是書寫方便
3.協變 IEnumerable<out T> out <out T>協變covariant 修飾回傳值 效果:讓右邊可以用子類 條件:T只能回傳結果,就不能再作為引數
/*--------------------------------------------------協變Out----------------------------------------------------*/ /// <summary> /// out 協變 T 就只能做回傳值 ,不能做引數 /// </summary> /// <typeparam name="T"></typeparam> public interface ICustomerListOut<out T> { //void Show(); //void Show(T t);//協變 T 就只能做回傳值 ,不能做引數 T Get();//泛型型別的回傳值 } /// <summary> /// 這種情況是可以的,泛型類繼承該介面 /// </summary> /// <typeparam name="T"></typeparam> public class CustomerListOut<T> : ICustomerListOut<T> { public T Get() { return default(T); } }
/*--------------------------------------------------協變out----------------------------------------------------*/ { //public interface IEnumerable<out T> : IEnumerable //協變:IEnumerable<out T>【效果:讓右邊可以用子類】條件:T只能回傳結果,就不能再作為引數 //協變作用是防止有了泛型包裝之后,導致[父子]繼承關系失效,進一步的加強了泛型的方便些 IEnumerable<Animal> animals1 = new List<Animal>(); IEnumerable<Animal> animals2 = new List<Cat>(); //我們定義了一個支持協變的介面,那我們也是不是也可以用這個介面定義一個支持協變的cat類 //局限性:out修飾協變后,T只能作為回傳值,而不能作為引數 //左邊是定義的一個支持協變的介面,右邊則是繼承協變介面的泛型類 ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>(); ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>(); }
4.逆變 in <in T>逆變contravariant 修飾傳入引數 逆變:讓右邊可以用父類,In修飾逆變后,T只能當引數,不能作回傳值
1 /*--------------------------------------------------逆變In----------------------------------------------------*/ 2 public interface ICustomerListIn<in T> 3 { 4 //T Get();//逆變T就不能作為回傳值,只能作為引數 5 void Show(T t); 6 } 7 /// <summary> 8 /// 泛型類繼承了一個支持逆變的泛型介面 9 /// </summary> 10 public class CustomerListIn<T>:ICustomerListIn<T> 11 { 12 public void Show(T t) 13 { 14 Console.WriteLine(t); 15 } 16 }
1 /*--------------------------------------------------逆變In----------------------------------------------------*/ 2 { 3 //逆變:讓右邊可以用父類,In修飾逆變后,T只能當引數,不能作回傳值 4 ICustomerListIn<Cat> listIn1 = new CustomerListIn<Cat>(); 5 ICustomerListIn<Cat> listIn2 = new CustomerListIn<Animal>(); 6 listIn1.Show(new Cat()); 7 }
5.協變,逆變一起
1 /*--------------------------------------------------協變Out逆變In一起----------------------------------------------------*/ 2 public interface ITogetherList<in inT,out outT> 3 { 4 void Show(inT t); 5 outT Get(); 6 outT Do(inT t); 7 } 8 public class TogertherList<Y1, Y2> : ITogetherList<Y1, Y2> 9 { 10 public void Show(Y1 y) 11 { 12 Console.WriteLine("This is {0}", y.GetType().Name); 13 } 14 public Y2 Get() 15 { 16 Console.WriteLine(typeof(Y2).Name); 17 return default(Y2); 18 } 19 public Y2 Do(Y1 y) 20 { 21 Console.WriteLine("This is {0}", y.GetType().Name); 22 Console.WriteLine(typeof(Y2).Name); 23 return default(Y2); 24 } 25 }
/*--------------------------------------------------協變Out逆變In一起的例子----------------------------------------------------*/ { ITogetherList<Cat,Animal > list1= new TogertherList<Cat,Animal>(); ITogetherList<Cat, Animal> list2 = new TogertherList<Cat, Cat>();//協變:左邊是父類的時候,協變可以替換為子類 ITogetherList<Cat, Animal> list3 = new TogertherList<Animal, Animal>();//逆變:左邊是子類的時候,協變可以替換為父類 ITogetherList<Cat, Animal> list4 = new TogertherList<Animal, Cat>();//協變+逆變 }
總結: 
1 /// 所謂協變逆變都是只針對泛型的 2 /// 只能放在介面或者委托的泛型引數前面 3 /// public delegate TResult Func<in T, out TResult>(T arg);
十. 泛型快取【說白了就是把資料存一下,下次直接用,常用做法,就是搞一個內部靜態字典,因為字典不容易丟失,并且可以放很多項】
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 namespace LearnGenericFirst1 9 { 10 /// <summary> 11 /// 泛型快取 12 /// 快取是屬于大型網站架構必備的一部分,提升性能的第一步 13 /// 快取:說白了就是把資料存一下,下次直接用,常用做法,就是搞一個內部靜態字典,因為字典不容易丟失,并且可以放很多項 14 /// </summary> 15 public class GenericCacheTest 16 { 17 public static void show() 18 { 19 for (int i = 0; i < 5; i++) 20 { 21 Console.WriteLine(GenericCache<int>.GetCache()); 22 Thread.Sleep(10); 23 Console.WriteLine(GenericCache<long>.GetCache()); 24 Thread.Sleep(10); 25 Console.WriteLine(GenericCache<DateTime>.GetCache()); 26 Thread.Sleep(10); 27 Console.WriteLine(GenericCache<string>.GetCache()); 28 Thread.Sleep(10); 29 Console.WriteLine(GenericCache<GenericCacheTest>.GetCache()); 30 Thread.Sleep(10); 31 } 32 } 33 34 /// <summary> 35 /// 以前的快取【 字典快取】,靜態屬性常駐記憶體 36 /// </summary> 37 /// <typeparam name="T"></typeparam> 38 /// <returns></returns> 39 public class DictionaryCache 40 { 41 private static Dictionary<Type, string> _TypeTimeDictionary = null; 42 /// <summary> 43 /// 由我們的CLR保障第一次啟用的時候會執行的,而且只執行一次 44 /// </summary> 45 static DictionaryCache() 46 { 47 Console.WriteLine("This is DictionaryCache 靜態建構式!"); 48 _TypeTimeDictionary = new Dictionary<Type, string>(); 49 } 50 public static string GetCache<T>() 51 { 52 Type type = typeof(Type); 53 //首先判斷一下字典里面有沒有,有直接從字典里拿,沒有就計算一下,把資料放進去 54 if (!_TypeTimeDictionary.ContainsKey(type)) 55 { 56 _TypeTimeDictionary[type] = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); 57 } 58 return _TypeTimeDictionary[type]; 59 } 60 61 } 62 63 /// <summary> 64 /// GenericCache<T>每個不同的T,CLR都會生成一份不同的副本 65 /// 適合不同型別,需要快取一份資料的場景,效率高 66 /// 靜態的東西是放在記憶體中,且唯一,不會被回收的,但是;如果靜態的東西被放在泛型里面,一切都變化了,會隨著指定的不同型別,編譯器會為它生產不同的副本 67 /// 局限性:泛型快取一個型別,只能存一個值,兩個值則存不了 68 /// </summary> 69 /// <typeparam name="T"></typeparam> 70 public class GenericCache<T> 71 { 72 static GenericCache() 73 { 74 Console.WriteLine("This is DictionaryCache 靜態建構式!"); 75 _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); 76 } 77 private static string _TypeTime = ""; 78 public static string GetCache() 79 { 80 return _TypeTime; 81 } 82 } 83 84 } 85 }
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/200864.html
標籤:.NET技术
上一篇:新手上路請教
下一篇:新手c#問題
