1. 程式人生 > >CLR類型設計之泛型(二)

CLR類型設計之泛型(二)

where條件 之前 解釋 columns 文章閱讀 sin 自己的 讀書 spl

在上一篇文章中,介紹了什麽是泛型,以及泛型和非泛型的區別,這篇文章主要講一些泛型的高級用法,泛型方法,泛型泛型接口和泛型委托,協變和逆變泛型類型參數和約束性,泛型的高級用法在平時的業務中用的不多,多用於封裝高級方法和一些底層封裝,前幾天讀了一篇文章,如何選擇網絡上的技術文章,因為現在關於技術的文章可以說非常多,但是時間是有限的,如果花很多時間閱讀了一篇文章卻沒有什麽用,豈不是很浪費時間,所以第一步選擇自己感興趣的文章閱讀,第二要把閱讀過的文章盡可能實現一次,讀書萬遍不如走上一遍,第三盡量不讀翻譯性的文章,這裏其實我覺得不是所有人都能很輕松的看懂官方文檔,所以這點還是仁者見仁,智者見智。為了讓文章盡可能的有深度,所以我覺得以後的博文中應該盡可能的貼出的知識模塊都有所解釋,有所理解。不是在網上復制粘貼以後在給別人看,博文不是筆記,所以要說出自己的見解

說了這麽多,那麽就開始務實的甩開膀子做吧!

泛型方法

既然講到了泛型是為了高級封裝,那麽我們就來封裝一個C#中的ORM吧,在封裝ORM之前還要有一個SQL幫助類,這個網上有很多,感興趣的可以直接到網上找一個,C#中封裝的ORM最好的Entity FromWork,感興趣的可以看下源碼,我們先看下下面的代碼,這是一個典型的泛型方法

技術分享
 1     /// <summary>
 2         /// 查詢獲得一個實體
 3         /// </summary>
 4         /// <typeparam name="T"></typeparam>
5 /// <param name="sql"></param> 6 /// <param name="sqlParameters"></param> 7 /// <param name="transaction"></param> 8 /// <returns></returns> 9 public static T Get<T>(string sql, IList<SqlParameter> sqlParameters = null
, IDbTransaction transaction = null) 10 { 11 DataTable table = SQLServerHelper.Query(sql, sqlParameters, transaction); 12 if (table != null && table.Rows != null && table.Rows.Count > 0) 13 { 14 DataRow row = table.Rows[0]; 15 return ConvertRowToModel<T>(row, table.Columns); 16 } 17 else 18 { 19 Type modeType = typeof(T); 20 return default(T); 21 } 22 23 }
View Code

註釋已經表明了這是用來獲取一個實體的Get<T>()方法中T定義為泛型類型,方法有一個必選參數兩個默認參數,string類型的sql語句,默認IList<SqlParameter> sqlParameters繼承自IList集合的SQL參數。這是參數化SQL的,每一個SQL語句都應該寫成參數化的,還有一個IDbTransaction transaction = null是否開啟事務,有了這樣三個參數就可以定義一個查詢指定SQL語句,是否有Where條件,是否開啟事務的方法,方法繼續執行,第是一行代碼是使用幫助類獲得數據,第14行和第15行是將獲得數據映射成對應的結構體,我們可以看下 ConvertRowToModel<T>(row, table.Columns)的內部實現

技術分享
  1   public static T ConvertRowToModel<T>(DataRow row, DataColumnCollection columns)
  2         {
  3             Type modeType = typeof(T);
  4             Object model = Activator.CreateInstance(modeType);
  5 
  6             foreach (var p in model.GetType().GetProperties())
  7             {
  8                 var propertyName = p.Name.ToUpper();
  9                 var propertyType = p.PropertyType.Name;
 10                 if (columns.Contains(propertyName))
 11                 {
 12                     var value = row[propertyName];
 13 
 14                     if (propertyType.ToUpper().Contains("STRING"))
 15                     {
 16 
 17                         if (Convert.IsDBNull(value))
 18                         {
 19                             value = string.Empty;
 20                         }
 21                         else
 22                         {
 23                             p.SetValue(model, value.ToString(), null);
 24                         }
 25                     }
 26                     else if (propertyType.ToUpper().Contains("INT"))
 27                     {
 28 
 29                         if (Convert.IsDBNull(value))
 30                         {
 31                             value = 0;
 32                         }
 33 
 34                         p.SetValue(model, Int32.Parse(value.ToString()), null);
 35                     }
 36                     else if (propertyType.ToUpper().Contains("SINGLE"))
 37                     {
 38 
 39                         if (Convert.IsDBNull(value))
 40                         {
 41                             value = 0.0f;
 42                         }
 43                         p.SetValue(model, Single.Parse(value.ToString()), null);
 44                     }
 45                     else if (propertyType.ToUpper().Contains("DATETIME"))
 46                     {
 47 
 48                         if (Convert.IsDBNull(value))
 49                         {
 50                             value = DateTime.MinValue;
 51                         }
 52                         p.SetValue(model, DateTime.Parse(value.ToString()), null);
 53                     }
 54                     else if (propertyType.ToUpper().Contains("DOUBLE"))
 55                     {
 56 
 57                         if (Convert.IsDBNull(value))
 58                         {
 59                             value = 0.0d;
 60                         }
 61                         p.SetValue(model, Double.Parse(value.ToString()), null);
 62                     }
 63                     else if (propertyType.ToUpper().Contains("BOOLEAN"))
 64                     {
 65 
 66                         if (Convert.IsDBNull(value))
 67                         {
 68                             value = false;
 69                         }
 70                         if (value.GetType() == typeof(Int32))
 71                         {
 72                             p.SetValue(model, Int32.Parse(value.ToString()) == 1, null);
 73 
 74                         }
 75                         else if (value.GetType() == typeof(String))
 76                         {
 77                             p.SetValue(model, Boolean.Parse(value.ToString()), null);
 78                         }
 79                         else if (value.GetType() == typeof(Boolean))
 80                         {
 81                             p.SetValue(model, (Boolean)(value), null);
 82                         }
 83 
 84                     }
 85                     else if (p.PropertyType.IsEnum)//Enum 
 86                     {
 87                         if (Convert.IsDBNull(value) || string.IsNullOrEmpty(value.ToString()))
 88                         {
 89                             value = "0";
 90                         }
 91 
 92                         p.SetValue(model, int.Parse(value.ToString()), null);
 93                     }
 94                     else if (propertyType.ToUpper().Contains("DECIMAL"))
 95                     {
 96 
 97                         if (Convert.IsDBNull(value))
 98                         {
 99                             value = 0.0f;
100                         }
101                         p.SetValue(model, Decimal.Parse(value.ToString()), null);
102                     }
103 
104                 }
105             }
106             return (T)model;
107         }
View Code

這個方法有點長,但實際上很好理解,並且這個方法也是一個泛型方法,其中的關鍵點在於他會把所有的字段類型轉換成基礎的元類型,轉換成int,string這些最終會在返回給Get<T>()方法,這樣就完成了實體的映射,那麽有了上面兩個方法,就可以編寫簡單的ORM了,如果是增刪查改的話,就改變其中的邏輯,獲得返回的影響行數就可以了,那麽如何調用這個ORM呢,封裝後最主要的是使用。可以用如下方法直接調用

技術分享
 1     public static Student Getmodel(long id) {
 2             StringBuilder sql = new StringBuilder();
 3             List<SqlParameter> args = new List<SqlParameter>();
 4             //根據學生id獲取學生信息
 5             sql.Append("select * from student where [email protected]");
 6             //id參數化後賦值
 7             args.Add(new SqlParameter("@id", id));
 8             //調用封裝ORM所在的CommonDao類  調用剛才的Get方法  
 9             //映射類Student,就會返回和Student類相符合的數據庫字段內容
10             return CommonDao.Get<Student>(sql.ToString(), args);
11         }
View Code

是不是就簡單了很多呢。當然你也可以封裝的更深一些,這裏還是在傳遞sql語句,如果你喜歡Entitie fromwork那種方式,可以把Sql語句也作為固定的寫法,只傳遞where條件後面的參數就可以了,可以看到泛型方法很有用處

但是有的時候定義了泛型方法,卻希望他只能用於某一種特定的類型,上述的例子可以用於所有泛型類型,但是如果我要封裝的不是底層,只是某一個高級方法,只允許某一種類型使用這個方法,那麽該如何做呢?可以使用泛型約束

泛型約束

技術分享
 1  public static T WhereGet<T>(string sql, IList<SqlParameter> sqlParameters = null, IDbTransaction transaction = null)
 2             where T:IList<T>
 3         {
 4             DataTable table = SQLServerHelper.Query(sql, sqlParameters, transaction);
 5             if (table != null && table.Rows != null && table.Rows.Count > 0)
 6             {
 7                 DataRow row = table.Rows[0];
 8                 return ConvertRowToModel<T>(row, table.Columns);
 9             }
10             else
11             {
12                 Type modeType = typeof(T);
13                 return default(T);
14             }
15 
16         }
View Code

CLR類型設計之泛型(二)