C#泛型(Generic)

一.什麼是泛型緩存

泛型(Generic)是C#語言2.0、通用語言運行時(CLR)2.0、.NET Framework2.0推出來的新特性。安全

泛型爲.NET框架引入類型參數(Type Parameters)的概念。類型參數使得設計類和方法時,沒必要肯定一個或多個參具體數。框架

具體的參數類型可延遲到聲明和使用時再肯定。避免了運行時類型轉換或裝箱操做的代價和風險。函數

二.泛型的使用和對比性能

2.1.CommandMethod(普通方法)spa

 1         /// <summary>
 2         /// 打印一個Int值
 3         /// 
 4         /// 由於方法聲明的時候,寫死了參數類型
 5         /// </summary>
 6         /// <param name="iParameter"></param>
 7          public static void ShowInt(int iParameter)
 8         {
 9             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, iParameter.GetType().Name, iParameter);
10 
11         }
12         /// <summary>
13         /// 打印一個string值
14         /// </summary>
15         /// <param name="sParameter"></param>
16         public static void ShowString(string sParameter)
17         {
18             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, sParameter.GetType().Name, sParameter);
19         }
20         /// <summary>
21         /// 打印一個DateTime值
22         /// </summary>
23         /// <param name="dtParameter"></param>
24         public static void ShowDateTime(DateTime dtParameter)
25         {
26             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, dtParameter.GetType().Name, dtParameter);
27         }

2.2.ObjectMethod(基類方法)pwa

 1         /// <summary>
 2         /// 打印一個object值
 3         /// 
 4         /// object引用類型 假如傳個值類型  會有裝箱拆箱  性能損失
 5         ///  類型不安全
 6         /// </summary>
 7         /// <param name="oParameter"></param>
 8         public static void ShowObject (object oParameter)
 9         {
10             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(CommonMethod).Name, oParameter.GetType().Name, oParameter);
11 
12             //Console.WriteLine($"{((People)oParameter).Id}_{((People)oParameter).Name}");
13         }

2.3.GenericMethod(泛型方法)設計

 1         /// <summary>
 2         /// 2.0推出的新語法
 3         /// 一個方法知足不一樣參數類型 作相同的事
 4         /// 
 5         /// 延遲聲明:把參數類型的聲明推遲到調用
 6         /// 不是語法糖,而是由框架升級提供的功能
 7         /// 
 8         /// 沒有寫死參數類型,調用的時候才指定的類型
 9         /// </summary>
10         /// <typeparam name="T">T/S 不要用關鍵字 也不要跟別的類型衝突</typeparam>
11         /// <param name="tParameter"></param>
12         public static void Show<T>(T tParameter)
13         {
14             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
15         }

2.3調用code

 1                 int iValue = 123;
 2                 string sValue = "456";
 3                 DateTime dtValue = DateTime.Now;
 4                 object oValue = "789";
 5                 Console.WriteLine("*********普通方法************");
 6                 CommonMethod.ShowInt(iValue);
 7                 CommonMethod.ShowString(sValue);
 8                 CommonMethod.ShowDateTime(dtValue);
 9                 CommonMethod.ShowObject(oValue);
10 
11                 Console.WriteLine("*********經過Object************");//.NET Framework1.0 1.1
12                 /*爲何能夠傳string、int、datetime、class等的緣由
13                   1. object類型是一切類型的父類
14                   2. 經過繼承,子類擁有父類的一切屬性和行爲;任何父類出現的地方,均可以用子類來代替
15                 */
16                 CommonMethod.ShowObject(iValue);
17                 CommonMethod.ShowObject(sValue);
18                 CommonMethod.ShowObject(dtValue);
19 
20                 Console.WriteLine("***********經過泛型***************");
21                 GenericMethod.Show<int>(iValue);//須要指定類型參數
22                 GenericMethod.Show<string>(sValue);//必須吻合
23                 GenericMethod.Show<DateTime>(dtValue);//能省略自動推算
24                 GenericMethod.Show<object>(oValue);

2.4.性能對比orm

  1     /// <summary>
  2     /// 性能監視類
  3     /// </summary>
  4    public class Monitor
  5     {
  6         public static void Show()
  7         {
  8             int iValue = 12345;
  9             long commonSecond = 0;
 10             long objectSecond = 0;
 11             long genericSecond = 0;
 12 
 13             {
 14                 //提供一組方法和屬性,可用於準確地測量運行時間
 15                 Stopwatch stopwatch = new Stopwatch();
 16                 //開始或繼續測量某個時間間隔的運行時間。
 17                 stopwatch.Start();
 18                 for (int i = 0; i < 100000000; i++)
 19                 {
 20                    ShowInt(iValue);
 21                 }
 22                 // 中止測量某個時間間隔的運行時間。
 23                 stopwatch.Stop();
 24                //獲取當前實例測量得出的總運行時間(以毫秒爲單位)。
 25                 commonSecond = stopwatch.ElapsedMilliseconds;
 26             }
 27             {
 28                 Stopwatch stopwatch = new Stopwatch();
 29                 stopwatch.Start();
 30                 for (int i = 0; i < 100000000; i++)
 31                 {
 32                     ShowObject(iValue);
 33                 }
 34                 stopwatch.Stop();
 35                 objectSecond = stopwatch.ElapsedMilliseconds;
 36             }
 37             {
 38                 Stopwatch stopwatch = new Stopwatch();
 39                 stopwatch.Start();
 40                 for (int i = 0; i < 100000000; i++)
 41                 {
 42                     Show(iValue);
 43                 }
 44                 stopwatch.Stop();
 45                 genericSecond = stopwatch.ElapsedMilliseconds;
 46             }
 47 
 48             Console.WriteLine($"普通方法耗時:{commonSecond} Object方法耗時:{objectSecond} Generic方法耗時:{genericSecond}");
 49         }
 50 
 51         private static void ShowInt(int iParameter)
 52         {
 53 
 54         }
 55 
 56         private static void ShowObject(object oParameter)
 57         {
 58 
 59         }
 60 
 61         private static void Show<T>(T tParameter)
 62         {
 63 
 64         }
 65 
 66         #region Stopwatch詳解
 67         /*
 68              獲取以每秒計時週期數表示的計時器頻率。此字段爲只讀。
 69         public static readonly long Frequency;
 70              指示計時器是否基於高分辨率性能計數器。此字段爲只讀。
 71         public static readonly bool IsHighResolution;
 72              初始化 System.Diagnostics.Stopwatch 類的新實例。
 73         public Stopwatch();
 74              獲取當前實例測量得出的總運行時間。
 75            返回結果:
 76              一個只讀的 System.TimeSpan,用於表示當前實例測量得出的總運行時間。
 77         public TimeSpan Elapsed { get; }
 78              獲取當前實例測量得出的總運行時間(以毫秒爲單位)。
 79            返回結果:
 80              一個只讀長整型,表示當前實例測量得出的總毫秒數。
 81         public long ElapsedMilliseconds { get; }
 82              獲取當前實例測量得出的總運行時間(用計時器計時週期表示)。
 83            返回結果:
 84              一個只讀長整型,表示當前實例測量得出的計時器計時週期的總數。
 85         public long ElapsedTicks { get; }
 86              獲取一個指示 System.Diagnostics.Stopwatch 計時器是否在運行的值。
 87            返回結果:
 88              若是 System.Diagnostics.Stopwatch 實例當前正在運行,而且在對某個時間間隔的運行時間進行測量,則該值爲 true;不然爲 false。
 89         public bool IsRunning { get; }
 90              獲取計時器機制中的當前最小時間單位數。
 91            返回結果:
 92              一個長整型,表示基礎計時器機制中的計時週期計數器值。
 93         public static long GetTimestamp();
 94              對新的 System.Diagnostics.Stopwatch 實例進行初始化,將運行時間屬性設置爲零,而後開始測量運行時間。
 95            返回結果:
 96              剛剛開始測量運行時間的 System.Diagnostics.Stopwatch。
 97         public static Stopwatch StartNew();
 98              中止時間間隔測量,並將運行時間重置爲零。
 99         public void Reset();
100              中止時間間隔測量,將運行時間重置爲零,而後開始測量運行時間。
101         public void Restart();
102              開始或繼續測量某個時間間隔的運行時間。
103         public void Start();
104             中止測量某個時間間隔的運行時間。
105         public void Stop();
106        */
107         #endregion
108     }
109 }

2.6對比結果

 

 

三.泛型的原理 

泛型在編譯的時候,類型是不明確的,類型參數會被系統編譯成佔位符 (~),在運行時,Jit即時編譯會根據程序中調用泛型方法時候給它指定的類型參數替換過來 

四. 泛型類、泛型方法、泛型接口、泛型委託

 1     /// <summary>
 2     /// 泛型類
 3     /// 一個類來知足不一樣的具體類型,來作相同的事
 4     /// </summary>
 5     public class GenericClass<T> 
 6     {
 7        
 8     }
 9 
10     public class GenericClass<T,S> where T:People where S:Hunan
11     {
12 
13     }
14 
15     /// <summary>
16     /// 泛型接口
17     /// 一個接口來知足不一樣的具體類型的接口,來作相同的事
18     /// </summary>
19     public interface IGenericInterface<T> //where T:People
20     {
21 
22     }
23 
24     public class CommonClass:GenericClass<int>//使用泛型必須指定類型
25     {
26 
27     }
28   
29     public class GenericClassChild<E>:GenericClass<E>
30     {
31 
32     }
33 
34     /// <summary>
35     /// 泛型委託
36     /// </summary>
37     /// <typeparam name="T"></typeparam>
38     public delegate void GenericDelegate<T>();

五.泛型約束

泛型定義中的 where 子句指定對用做泛型類型、方法、委託或本地函數中類型參數的參數類型的約束。

 約束可指定接口、基類或要求泛型類型爲引用、值或非託管類型。 它們聲明類型參數必須具有的功能。

 1     /// <summary>
 2     /// 約束類 
 3     /// 泛型:不一樣的參數類型都能進來;任何類型都來進來,沒法準肯定位
 4     /// 沒有約束,也就沒有自由
 5     /// 泛型約束-----基類約束(不能是sealed)
 6     /// 1.可使用基類的一切屬性方法---權利
 7     /// 2.強制保證T必定是People或者People的子類
 8     /// 
 9     /// 
10     /// 爲何不能用int(Int32)?
11     ///「int」不是有效的約束。做爲約束使用的類型必須是接口、非密封類或類型參數 而Int32是密封類(sealed)
12     /// </summary>
13     public class Constraint
14     {
15         public static void Show<T>(T tParameter)
16             where T : People,ISports,IWork,new()//基類約束
17         {
18             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
19 
20             Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
21             tParameter.Hi();
22             tParameter.Pingpang();
23             tParameter.Work();
24         }
25 
26         /// <summary>
27         /// 爲何不用基類
28         /// </summary>
29         /// <param name="tParameter"></param>
30         public static void ShowBase(People tParameter)//由於約束能夠疊加 更靈活 
31         {
32             Console.WriteLine("This is {0},parameter={1},type={2}", typeof(GenericMethod).Name, tParameter.GetType().Name, tParameter.ToString());
33             Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
34             tParameter.Hi();
35         }
36 
37         public static T InterfaceGet<T>(T t)
38             where T : ISports  //接口約束
39         {
40             t.Pingpang();
41             return t;
42         }
43 
44         public static T ClassGet<T>(T t)
45           where T : class  //引用類型約束:保證引用類型
46         {
47             T tNew = null;
48             return t;
49         }
50 
51         public static T StructGet<T>(T t)
52             where T : struct //值類型約束
53         {
54             T tNew = default(T); //會根據T的不一樣 賦予默認值
55             return t;
56         }
57 
58         public static T NewGet<T>(T t)
59          where T : new() //無參數構造函數約束
60         {
61             T tNew = new T();
62             return t;
63         }
64     }

六.協變和逆變

 可變性是以一種類型安全的方式,將一個對象當作另外一個對象來使用。若是不能將一個類型替換爲另外一個類型,那麼這個類型就稱之爲:不變量

協變和逆變是兩個相互對立的概念:

  • 若是某個返回的類型能夠由其派生類型替換,那麼這個類型就是支持協變
  • 若是某個參數類型能夠由其基類替換,那麼這個類型就是支持逆變
  1    /// <summary>
  2     ///  .NET 4.0出現的
  3     /// 只能放在接口或者委託的泛型參數前面
  4     /// out 協變covariant 修飾返回值
  5     /// in 逆變contravariant 修飾傳入參數
  6     /// </summary>
  7     public class CCTest
  8     {
  9         public static void Show()
 10         {
 11             {
 12                 Bird bird1 = new Bird();
 13                 Bird bird2 = new Sparrow();
 14                 Sparrow sparrow = new Sparrow();
 15                 //Sparrow sparrow=new Bird();//不合理 鳥不必定是麻雀
 16             }
 17 
 18             {
 19                 List<Bird> birdList1 = new List<Bird>();
 20                 //兩個不一樣的類型 沒有父子關係
 21                 //List<Bird> birdList2 = new List<Sparrow>();//應該能夠 一堆麻雀是一堆鳥 
 22                 //在.NET Core中須要引用System.Linq;
 23                 List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList();
 24             }
 25 
 26             {
 27                 //協變
 28                 IEnumerable<Bird> birdList1 = new List<Bird>();//接口
 29                 IEnumerable<Bird> birdList2 = new List<Sparrow>();
 30                 Func<Bird> func = new Func<Sparrow>(() => null);//委託
 31                 //自定義
 32                 ICustomerListOut<Bird> customerList1 = new CustomerListOut<Bird>();
 33                 ICustomerListOut<Bird> customerList2 = new CustomerListOut<Bird>();
 34             }
 35 
 36             {
 37                 //逆變
 38                 ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Sparrow>();
 39                 ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Bird>();
 40                 ICustomerListIn<Bird> customerList3 = new CustomerListIn<Bird>();
 41                 customerList3.Show(new Bird());
 42                 customerList3.Show(new Sparrow());
 43                 Action<Sparrow> action = new Action<Bird>((Bird i) => { });
 44             }
 45 
 46             {
 47                 IMyList<Sparrow, Bird> myList1 = new MyList<Sparrow, Bird>();
 48                 IMyList<Sparrow, Bird> myList2 = new MyList<Sparrow, Sparrow>();//協變
 49                 IMyList<Sparrow, Bird> myList3 = new MyList<Bird, Bird>();//逆變
 50                 IMyList<Sparrow, Bird> myList4 = new MyList<Bird, Sparrow>();//逆變+協變
 51             }
 52         }
 53     }
 54 
 55     /// <summary>
 56     /// 鳥類
 57     /// </summary>
 58     public class Bird
 59     {
 60         public int Id { get; set; }
 61     }
 62     /// <summary>
 63     /// 麻雀類
 64     /// </summary>
 65     public class Sparrow : Bird
 66     {
 67         public string Name { get; set; }
 68     }
 69     /// <summary>
 70     /// in 逆變 只能作參數
 71     /// </summary>
 72     /// <typeparam name="T"></typeparam>
 73     public interface ICustomerListIn<in T>
 74     {
 75         void Show(T t);
 76 
 77         //T Get(); 逆變,不能做爲返回值,只能把它當成參數
 78     }
 79 
 80     public class CustomerListIn<T> : ICustomerListIn<T>
 81     {
 82         public void Show(T t)
 83         {
 84 
 85         }
 86 
 87         //public T Get()
 88         //{
 89         //    return default(T);
 90         //}
 91     }
 92     /// <summary>
 93     /// out 協變 只能是返回結果
 94     /// </summary>
 95     /// <typeparam name="T"></typeparam>
 96     public interface ICustomerListOut<out T>
 97     {
 98         T Get();
 99 
100         //void Show(T t); 只能放在返回值,不能放在參數
101     }
102 
103     public class CustomerListOut<T> : ICustomerListOut<T>
104     {
105         public T Get()
106         {
107             return default(T);
108         }
109 
110         //public void Show(T t)
111         //{
112 
113         //}
114     }
115 
116     public interface IMyList<in inT,out outT>
117     {
118         void Show(inT t);
119 
120         outT Get();
121 
122         outT Do(inT t);
123 
124        //out 只能是返回值 in只能是參數
125        
126     }
127 
128     public class MyList<T1, T2> : IMyList<T1, T2>
129     {
130         public T2 Do(T1 t)
131         {
132             Console.WriteLine(t.GetType().Name);
133             Console.WriteLine(typeof(T2).Name);
134             return default(T2);
135         }
136 
137         public T2 Get()
138         {
139             Console.WriteLine(typeof(T2).Name);
140             return default(T2);
141         }
142 
143         public void Show(T1 t)
144         {
145             Console.WriteLine(t.GetType().Name);
146         }
147     }

七.泛型緩存

 1     public class GenericCacheTest
 2     {
 3         public static void Show()
 4         {
 5             for (int i = 0; i < 5; i++)
 6             {
 7                 Console.WriteLine(GenericCache<int>.GetCache());
 8                 Thread.Sleep(10);
 9                 Console.WriteLine(GenericCache<long>.GetCache());
10                 Thread.Sleep(10);
11                 Console.WriteLine(GenericCache<DateTime>.GetCache());
12                 Thread.Sleep(10);
13                 Console.WriteLine(GenericCache<string>.GetCache());
14                 Thread.Sleep(10);
15                 Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
16                 Thread.Sleep(10);
17             }
18         }
19     }
20     /// <summary>
21     /// 字典緩存:靜態屬性常駐內存
22     /// </summary>
23     public class DictionaryCache
24     {
25         private static Dictionary<Type, string> _TypeTimeDictionary = null;
26 
27         static DictionaryCache()
28         {
29             Console.WriteLine("This is Dictionary 靜態構造函數");
30             _TypeTimeDictionary = new Dictionary<Type, string>();
31         }
32 
33         public static string GetCache<T>()
34         {
35             Type type = typeof(Type);
36             if (!_TypeTimeDictionary.ContainsKey(type))
37             {
38                 _TypeTimeDictionary[type] = string.Format("{0}_{1}",typeof(T).FullName,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
39             }
40             return _TypeTimeDictionary[type];
41         }
42     }
43     /// <summary>
44     /// 每一個不一樣的T,都會生成一份不一樣的副本
45     /// 適合不一樣類型,須要緩存一份數據的場景,效率高
46     /// 不能主動釋放
47     /// </summary>
48     /// <typeparam name="T"></typeparam>
49     public class GenericCache<T>
50     {
51         private static string _TypeTime = "";
52         static GenericCache()
53         {
54             Console.WriteLine("This is GenericCache 靜態構造函數");
55             _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff"));
56         }
57 
58         public static string GetCache()
59         {
60             return _TypeTime;
61         }
62     }
63     /*
64      * 爲何不用字典緩存?
65      * 字典緩存是一種哈希分佈的,找數據的時候須要把Key進行哈希 找到內存地址 而後才能找到數據 數據若是過多 範圍就會很大 形成性能浪費
66      * 而泛型緩存,每個都是獨立的 不一樣的實體產生一個不一樣的副本,副本就存在CPU那,已經存在即時編譯器裏面去,若是須要找不一樣的類 直接在內存拿、尋址
67      * 而字典緩存須要去計算去尋址
68      * 
69      * 泛型生命週期:永遠都不釋放
70      */

八.泛型總結(圖片來源網上)

相關文章
相關標籤/搜索