天天看點

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  最近這段時間特忙,公事私事,忙得有時都沒時間打開電腦了,這兩周隻能盡量更新,以後再将章節補回來。

  直接進入主題,通過上一章節,大家明白了怎麼使用模闆類編寫T4模闆,本章進的是一些簡單技巧的應用

  1、首先建立一個Test2.tt模闆

  

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  2、然後修改模闆内容為下面代碼

  這些代碼與上一章最後面的那個差不多,隻是修改了輸出檔案名、命名空間、類名、類屬性(partial)和一個單例擷取函數

1 <#@ template debug="false" hostspecific="True" language="C#" #>
 2 <#@ output extension=".cs" encoding="utf-8" #>
 3 <#@ include file="SQLServer.ttinclude" #>
 4 <#@ include file="MultipleOutputHelper.ttinclude"#> 
 5 
 6 <#
 7     //擷取所有表與視圖
 8     var tables = LoadTables();
 9     //建立多檔案生成實體
10     var manager = Manager.Create(Host, GenerationEnvironment);  
11 
12     //周遊所有表
13     foreach(var tbl in tables){
14         //判斷目前表名是否是禁用名稱(禁用的名稱可以在Settings.ttinclude檔案的ExcludeTables字元串資料中進行添加)
15         if(!ExcludeTables.Contains(tbl.Name))
16         {
17             // 設定輸出的檔案名
18             manager.StartNewFile(tbl.ClassName+"Bll.cs");
19 #> 
20 using System;
21 
22 namespace Solution.Logic {
23 
24     public partial class <#=tbl.CleanName#>Bll {
25         
26         #region 單例模式
27         //定義單例實體
28         private static <#=tbl.Name#>Bll _<#=tbl.Name#>Bll = null;
29 
30         /// <summary>
31         /// 擷取本邏輯類單例
32         /// </summary>
33         /// <returns></returns>
34         public static <#=tbl.Name#>Bll GetInstence() {
35             if (_<#=tbl.Name#>Bll == null) {
36                 _<#=tbl.Name#>Bll = new <#=tbl.Name#>Bll();
37             }
38             return _<#=tbl.Name#>Bll;
39         }
40         #endregion
41         
42     }
43 
44 }
45 
46 
47 <# 
48             // 輸出檔案結束
49             manager.EndBlock();
50         } //if(!ExcludeTables.Contains(tbl.Name)) 判斷結束
51        
52     }// end foreach
53 
54     // 執行編譯,生成檔案
55     manager.Process(true);  
56 #>       

  運作模闆,測試看看效果

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)
從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  從上面添加函數的方法可以看出,對于常用的函數,都可以在模闆中進行添加後,直接生成出來,這樣就減少了程式員很大的工作量了

  3、用上面方法确實可以解決很大部分的問題,但對于一些特殊的函數直接這樣寫就不行了,比如我們想生成一個删除指定外鍵Id的函數,由于有的表有外鍵有的沒有,那麼就要用上一些簡單的判斷來處理了

1 <#@ template debug="false" hostspecific="True" language="C#" #>
 2 <#@ output extension=".cs" encoding="utf-8" #>
 3 <#@ include file="SQLServer.ttinclude" #>
 4 <#@ include file="MultipleOutputHelper.ttinclude"#> 
 5 
 6 <#
 7     //擷取所有表與視圖
 8     var tables = LoadTables();
 9     //建立多檔案生成實體
10     var manager = Manager.Create(Host, GenerationEnvironment);  
11 
12     //周遊所有表
13     foreach(var tbl in tables){
14         //判斷目前表名是否是禁用名稱(禁用的名稱可以在Settings.ttinclude檔案的ExcludeTables字元串資料中進行添加)
15         if(!ExcludeTables.Contains(tbl.Name))
16         {
17             // 設定輸出的檔案名
18             manager.StartNewFile(tbl.ClassName+"Bll.cs");
19 #> 
20 using System;
21 using Solution.DataAccess.DataModel;
22 using Solution.DataAccess.DbHelper;
23 
24 namespace Solution.Logic {
25 
26     public partial class <#=tbl.CleanName#>Bll {
27         
28         #region 單例模式
29         //定義單例實體
30         private static <#=tbl.Name#>Bll _<#=tbl.Name#>Bll = null;
31 
32         /// <summary>
33         /// 擷取本邏輯類單例
34         /// </summary>
35         /// <returns></returns>
36         public static <#=tbl.Name#>Bll GetInstence() {
37             if (_<#=tbl.Name#>Bll == null) {
38                 _<#=tbl.Name#>Bll = new <#=tbl.Name#>Bll();
39             }
40             return _<#=tbl.Name#>Bll;
41         }
42         #endregion
43         
44             
45 <#
46         foreach(var col in tbl.Columns){
47             //判斷字段名稱中是否包含“_Id”這個字元串,且字段類型為int或long的,則生成對應的删除函數
48             if (col.CleanName.IndexOf("_Id") >= 0  && (col.SysType == "int" || col.SysType == "long"))
49             {
50 #>
51         #region 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
52         /// <summary>
53         /// 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
54         /// </summary>
55         /// <param name="id">記錄的主鍵值</param>
56         public void DeleteBy<#=col.CleanName#>(int id) {
57             //删除
58             <#=tbl.Name#>.Delete(x => x.<#=col.CleanName#> == id);
59         }
60 
61         /// <summary>
62         /// 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
63         /// </summary>
64         /// <param name="id">記錄的主鍵值</param>
65         public void DeleteBy<#=col.CleanName#>(int[] id) {
66             if (id == null) return;
67             //将數組轉為逗号分隔的字串
68             var str = string.Join(",", id);
69 
70             //設定Sql語句
71             var sql = "delete from <#=tbl.Name#> where <#=col.CleanName#> in (" + str + ")";
72 
73             //删除
74             var deleteHelper = new DeleteHelper();
75             deleteHelper.Delete(sql);
76         }
77         #endregion
78 
79 <#
80             }
81         }
82 #>
83     }
84 
85 }
86 
87 
88 <# 
89             // 輸出檔案結束
90             manager.EndBlock();
91         } //if(!ExcludeTables.Contains(tbl.Name)) 判斷結束
92        
93     }// end foreach
94 
95     // 執行編譯,生成檔案
96     manager.Process(true);  
97 #>       
從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  4、同理,我們可以通過判斷,生成擷取指定名稱的字段值和生成删除圖檔函數等,這些都可以根據你的需要去生成

1 <#@ template debug="false" hostspecific="True" language="C#" #>
  2 <#@ output extension=".cs" encoding="utf-8" #>
  3 <#@ include file="SQLServer.ttinclude" #>
  4 <#@ include file="MultipleOutputHelper.ttinclude"#> 
  5 
  6 <#
  7     //擷取所有表與視圖
  8     var tables = LoadTables();
  9     //建立多檔案生成實體
 10     var manager = Manager.Create(Host, GenerationEnvironment);  
 11 
 12     //周遊所有表
 13     foreach(var tbl in tables){
 14         //判斷目前表名是否是禁用名稱(禁用的名稱可以在Settings.ttinclude檔案的ExcludeTables字元串資料中進行添加)
 15         if(!ExcludeTables.Contains(tbl.Name))
 16         {
 17             // 設定輸出的檔案名
 18             manager.StartNewFile(tbl.ClassName+"Bll.cs");
 19 #> 
 20 using System;
 21 using Solution.DataAccess.DataModel;
 22 using Solution.DataAccess.DbHelper;
 23 
 24 namespace Solution.Logic {
 25 
 26     public partial class <#=tbl.CleanName#>Bll {
 27         
 28         #region 單例模式
 29         //定義單例實體
 30         private static <#=tbl.Name#>Bll _<#=tbl.Name#>Bll = null;
 31 
 32         /// <summary>
 33         /// 擷取本邏輯類單例
 34         /// </summary>
 35         /// <returns></returns>
 36         public static <#=tbl.Name#>Bll GetInstence() {
 37             if (_<#=tbl.Name#>Bll == null) {
 38                 _<#=tbl.Name#>Bll = new <#=tbl.Name#>Bll();
 39             }
 40             return _<#=tbl.Name#>Bll;
 41         }
 42         #endregion
 43         
 44 <#
 45         foreach(var col in tbl.Columns){
 46             //判斷字段名稱中是否包含“_Id”這個字元串,且字段類型為int或long的,則生成對應的删除函數
 47             if (col.CleanName.IndexOf("_Id") >= 0  && (col.SysType == "int" || col.SysType == "long"))
 48             {
 49 #>
 50         #region 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
 51         /// <summary>
 52         /// 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
 53         /// </summary>
 54         /// <param name="id">記錄的主鍵值</param>
 55         public void DeleteBy<#=col.CleanName#>(int id) {
 56             //删除
 57             <#=tbl.Name#>.Delete(x => x.<#=col.CleanName#> == id);
 58         }
 59 
 60         /// <summary>
 61         /// 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
 62         /// </summary>
 63         /// <param name="id">記錄的主鍵值</param>
 64         public void DeleteBy<#=col.CleanName#>(int[] id) {
 65             if (id == null) return;
 66             //将數組轉為逗号分隔的字串
 67             var str = string.Join(",", id);
 68 
 69             //設定Sql語句
 70             var sql = "delete from <#=tbl.Name#> where <#=col.CleanName#> in (" + str + ")";
 71 
 72             //删除
 73             var deleteHelper = new DeleteHelper();
 74             deleteHelper.Delete(sql);
 75         }
 76         #endregion
 77 
 78 <#
 79             }
 80             //判斷字段名稱中是否包含“Name”這個字元串,且字段類型為string
 81             else if (col.CleanName.IndexOf("Name") >= 0 && col.SysType == "string") 
 82             {
 83 #>
 84         #region 擷取<#=col.CleanName #>字段值
 85         /// <summary>
 86         /// 擷取<#=col.CleanName #>字段值
 87         /// </summary>
 88         /// <param name="pkValue">主鍵Id</param>
 89         /// <returns></returns>
 90         public string Get<#=col.CleanName #>(int pkValue)
 91         {
 92             //從資料庫中查詢
 93             var model = <#=tbl.Name#>.SingleOrDefault(x => x.Id == pkValue);
 94             return model == null ? "" : model.<#=col.CleanName #>;            
 95         }
 96         #endregion
 97 
 98 <#
 99             }
100             //判斷字段名稱中是否包含“Img”這個字元串,且字段類型為string
101             else if (col.CleanName.IndexOf("Img") >= 0 && col.SysType == "string") 
102             {
103 #>
104         #region 删除<#=col.CleanName #>字段存儲的對應圖檔
105         /// <summary>删除<#=col.CleanName #>字段存儲的對應圖檔</summary>
106         /// <param name="pkValue">主鍵Id</param>
107         public void Del<#=col.CleanName #>(int pkValue) {
108             try {
109                 //添加删除語句
110             }
111             catch (Exception e) {
112                 //出現異常,儲存出錯日志資訊
113                 //添加儲存出錯日志語句
114             }
115         }
116         #endregion
117 
118 <#
119 
120             }
121         }
122 #>
123     }
124 
125 }
126 
127 
128 <# 
129             // 輸出檔案結束
130             manager.EndBlock();
131         } //if(!ExcludeTables.Contains(tbl.Name)) 判斷結束
132        
133     }// end foreach
134 
135     // 執行編譯,生成檔案
136     manager.Process(true);  
137 #>       
從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  5、有時候我們會存在一些特殊的需求,有些表或字段要進行過濾操作,這時我們就可以使用一些簡單的過濾判斷處理

  比如我們對于Manager_Id與Manager_Name這兩個字段是不需要生成對應函數的,那麼我們就可以加個過濾處理

  首先在Settings.ttinclude檔案中建立一個字元串數組變量,并指派

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  然後在模闆中的循環語句中添加判斷

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)
從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  對比第4點的圖就可以看到,已經少了兩個函數了

  而表名過濾,在上一章節的内容中已經包含了,請看下圖

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  隻要在Settings.ttinclude檔案中的ExcludeTables變量中添加你想過濾的表名稱就可以了

  當然我們還可以寫出更多的擴充,這些需要發揮你的想象力,生成更多常用函數,使你從複制粘貼中解放出來,當然函數有變動時,也隻需要改一下模闆就可以了,友善快捷

  6、對于常用功能來說,前面的生成方式都可以解決,但有時候有些功能直接生成的方式解決不了,那麼父類(基類)與虛函數的運用可以幫我們解決很多程式調用的問題。

  比如我們使用IIS緩存,在對記錄進行添加、删除與修改操作時,必須同步删除緩存。看到這個需求,可能有的朋友就會說,這很簡單啊,直接生成一個緩存删除函數就可以了。是的,這是一種處理方法,但還會存在很多特殊情況,有些時候,我們在自定義函數中也會用到一些緩存,這些緩存并不存在模闆中,那麼想要模闆裡的程式在清空模闆緩存後,也能自動幫我們清除自定義緩存的話該怎麼實作呢?不可能要讓我們在相關程式調用的方法中手動添加吧,如果調用的地方太多的話,就很容易忘記了。而我們有一種比較好的解決方式,那就是使用父類(基類),在基類中實作一個删除緩存的虛函數,模闆類繼承基類後,可以在那些執行添加、删除與修改的函數中直接調用虛函數,而對于這些個性化的删除操作,我們隻需要使用override修飾符重寫該函數,就可以實作自動删除緩存的功能了。

  具體操作方法請看下面步驟:

  首先建立一個基類

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  建立一個虛函數

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  然後再模闆中讓模闆類繼承基類,并實作添加、修改與删除方法,在方法中調用删除緩存函數

1 <#@ template debug="false" hostspecific="True" language="C#" #>
  2 <#@ output extension=".cs" encoding="utf-8" #>
  3 <#@ include file="SQLServer.ttinclude" #>
  4 <#@ include file="MultipleOutputHelper.ttinclude"#> 
  5 
  6 <#
  7     //擷取所有表與視圖
  8     var tables = LoadTables();
  9     //建立多檔案生成實體
 10     var manager = Manager.Create(Host, GenerationEnvironment);  
 11 
 12     //周遊所有表
 13     foreach(var tbl in tables){
 14         //判斷目前表名是否是禁用名稱(禁用的名稱可以在Settings.ttinclude檔案的ExcludeTables字元串資料中進行添加)
 15         if(!ExcludeTables.Contains(tbl.Name))
 16         {
 17             // 設定輸出的檔案名
 18             manager.StartNewFile(tbl.ClassName+"Bll.cs");
 19 #> 
 20 using System;
 21 using System.Linq.Expressions;
 22 using Solution.DataAccess.DataModel;
 23 using Solution.DataAccess.DbHelper;
 24 
 25 namespace Solution.Logic 
 26 {
 27 
 28     public partial class <#=tbl.CleanName#>Bll : LogicBase
 29     {
 30         
 31         #region 單例模式
 32         //定義單例實體
 33         private static <#=tbl.Name#>Bll _<#=tbl.Name#>Bll = null;
 34 
 35         /// <summary>
 36         /// 擷取本邏輯類單例
 37         /// </summary>
 38         /// <returns></returns>
 39         public static <#=tbl.Name#>Bll GetInstence() 
 40         {
 41             if (_<#=tbl.Name#>Bll == null) 
 42             {
 43                 _<#=tbl.Name#>Bll = new <#=tbl.Name#>Bll();
 44             }
 45             return _<#=tbl.Name#>Bll;
 46         }
 47         #endregion
 48 
 49         #region 添加與編輯<#=tbl.Name#>表記錄
 50         /// <summary>
 51         /// 添加與編輯<#=tbl.Name#>記錄
 52         /// </summary>
 53         /// <param name="model"><#=tbl.Name#>表實體</param>
 54         public void Save(<#=tbl.Name#> model)
 55         {
 56             try {
 57                 //儲存
 58                 model.Save();
 59                 
 60                 //删除緩存
 61                 DelCache();
 62                 
 63                 //添加使用者通路記錄
 64                 //UseLogBll.GetInstence().Save("{0}" + (model.Id == 0 ? "添加" : "編輯") + "<#=tbl.Name#>記錄成功,ID為【" + model.Id + "】");
 65             }
 66             catch (Exception e) {
 67                 //var result = "執行<#=tbl.Name#>Bll.Save()函數出錯!";
 68 
 69                 //出現異常,儲存出錯日志資訊
 70                 //CommonBll.WriteLog(result, e, false);
 71             }
 72         }
 73         #endregion
 74 
 75         #region 删除<#=tbl.Name#>表記錄
 76         /// <summary>
 77         /// 删除<#=tbl.Name#>表記錄
 78         /// </summary>
 79         /// <param name="id">記錄的主鍵值</param>
 80         public void Delete(int id) {
 81             //設定Sql語句
 82             var sql = "delete from <#=tbl.Name#> where Id = " + id;
 83 
 84             //删除
 85             var deleteHelper = new DeleteHelper();
 86             deleteHelper.Delete(sql);
 87             
 88             //删除緩存
 89             DelCache();
 90             
 91             //添加使用者操作記錄
 92             //UseLogBll.GetInstence().Save("{0}删除了<#=tbl.Name#>表id為【" + id + "】的記錄!");
 93         }
 94 
 95         /// <summary>
 96         /// 删除<#=tbl.Name#>表記錄
 97         /// </summary>
 98         /// <param name="id">記錄的主鍵值</param>
 99         public void Delete(int[] id) {
100             if (id == null) return;
101             //将數組轉為逗号分隔的字串
102             var str = string.Join(",", id);
103 
104             //設定Sql語句
105             var sql = "delete from <#=tbl.Name#> where Id in (" + str + ")";
106 
107             //删除
108             var deleteHelper = new DeleteHelper();
109             deleteHelper.Delete(sql);
110             
111             //删除緩存
112             DelCache();
113             
114             //添加使用者操作記錄
115             //UseLogBll.GetInstence().Save("{0}删除了<#=tbl.Name#>表id為【" + str + "】的記錄!");
116         }
117 
118         /// <summary>
119         /// 擷取資料表中的某個值——從資料庫中查詢,如果使用了緩存,删除成功後會清空本表的所有緩存記錄,然後重新加載進緩存
120         /// </summary>
121         /// <param name="expression">條件語句</param>
122         /// <returns></returns>
123         public void Delete(Expression<Func<<#=tbl.Name#>, bool>> expression)
124         {
125             //執行删除
126             <#=tbl.Name#>.Delete(expression);
127             
128             //删除緩存
129             DelCache();
130             
131             //添加使用者操作記錄
132             //UseLogBll.GetInstence().Save(page, "{0}删除了<#=tbl.Name#>表記錄!");
133         }
134         #endregion
135         
136 <#
137         foreach(var col in tbl.Columns)
138         {
139             //進行過濾判斷,指定的字段名稱不做處理
140             if (ExcludeFields.Contains(col.CleanName))
141                 continue;
142 
143             //判斷字段名稱中是否包含“_Id”這個字元串,且字段類型為int或long的,則生成對應的删除函數
144             if (col.CleanName.IndexOf("_Id") >= 0  && (col.SysType == "int" || col.SysType == "long"))
145             {
146 #>
147         #region 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
148         /// <summary>
149         /// 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
150         /// </summary>
151         /// <param name="id">記錄的主鍵值</param>
152         public void DeleteBy<#=col.CleanName#>(int id) 
153         {
154             //删除
155             <#=tbl.Name#>.Delete(x => x.<#=col.CleanName#> == id);
156         }
157 
158         /// <summary>
159         /// 删除<#=tbl.Name#>表指定<#=col.CleanName#>的字段值記錄
160         /// </summary>
161         /// <param name="id">記錄的主鍵值</param>
162         public void DeleteBy<#=col.CleanName#>(int[] id) 
163         {
164             if (id == null) return;
165             //将數組轉為逗号分隔的字串
166             var str = string.Join(",", id);
167 
168             //設定Sql語句
169             var sql = "delete from <#=tbl.Name#> where <#=col.CleanName#> in (" + str + ")";
170 
171             //删除
172             var deleteHelper = new DeleteHelper();
173             deleteHelper.Delete(sql);
174         }
175         #endregion
176 
177 <#
178             }
179             //判斷字段名稱中是否包含“Name”這個字元串,且字段類型為string
180             else if (col.CleanName.IndexOf("Name") >= 0 && col.SysType == "string") 
181             {
182 #>
183         #region 擷取<#=col.CleanName #>字段值
184         /// <summary>
185         /// 擷取<#=col.CleanName #>字段值
186         /// </summary>
187         /// <param name="pkValue">主鍵Id</param>
188         /// <returns></returns>
189         public string Get<#=col.CleanName #>(int pkValue)
190         {
191             //從資料庫中查詢
192             var model = <#=tbl.Name#>.SingleOrDefault(x => x.Id == pkValue);
193             return model == null ? "" : model.<#=col.CleanName #>;            
194         }
195         #endregion
196 
197 <#
198             }
199             //判斷字段名稱中是否包含“Img”這個字元串,且字段類型為string
200             else if (col.CleanName.IndexOf("Img") >= 0 && col.SysType == "string") 
201             {
202 #>
203         #region 删除<#=col.CleanName #>字段存儲的對應圖檔
204         /// <summary>删除<#=col.CleanName #>字段存儲的對應圖檔</summary>
205         /// <param name="pkValue">主鍵Id</param>
206         public void Del<#=col.CleanName #>(int pkValue) 
207         {
208             try 
209             {
210                 //添加删除語句
211             }
212             catch (Exception e) 
213             {
214                 //出現異常,儲存出錯日志資訊
215                 //添加儲存出錯日志語句
216             }
217         }
218         #endregion
219 
220 <#
221 
222             }
223         }
224 #>
225     }
226 
227 }
228 
229 
230 <# 
231             // 輸出檔案結束
232             manager.EndBlock();
233         } //if(!ExcludeTables.Contains(tbl.Name)) 判斷結束
234        
235     }// end foreach
236 
237     // 執行編譯,生成檔案
238     manager.Process(true);  
239 #>       
從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  然後我們建立一個與模版中同名的類

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  實作虛函數

從零開始編寫自己的C#架構(13)——T4模闆在邏輯層中的應用(二)

  這樣模闆函數在執行相關操作時,如果我們重寫了清空緩存這個函數,那麼程式就會自動執行清空緩存函數了,而對于那些不需要該功能的類則沒有任何影響

  這裡要注意的是,我們的模闆類與這個自定義類都有一個統一的修飾符partial

  大家先消化上面内容,才好了解下一章節中模闆調用内容,下章會将寫好的模闆函數全部貼出來,讓大家直接一步到位,生成Web層所需要的絕大部分調用函數,減少這一部分不必要的編碼工作。

 下載下傳位址:

T4模闆在邏輯層中的應用(二).rar 

 版權聲明:

  本文由AllEmpty原創并釋出于部落格園,歡迎轉載,未經本人同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利。如有問題,可以通過[email protected] 聯系我,非常感謝。

  發表本編内容,隻要主為了和大家共同學習共同進步,有興趣的朋友可以加加Q群:327360708 ,大家一起探讨。

  更多内容,敬請觀注部落格:http://www.cnblogs.com/EmptyFS/

隻有将自己置空,才能裝進更多的東西!我是陳煥,資深IT碼農,愛分享愛學習,一位奔跑在求知路上的踐行者。

繼續閱讀