上一篇介紹了數據類型轉換的一些狀況,能夠看出,若是不進行封裝,有可能致使比較混亂的代碼。本文經過TDD方式把數據類型轉換公共操做類開發出來,並提供源碼下載。git
咱們在 應用程序框架實戰十一:建立VS解決方案與程序集 一文已經建立了解決方案,包含一個類庫項目和一個單元測試項目。單元測試將使用.Net自帶的 MsTest,另外經過Resharper工具來觀察測試結果。程序員
首先考慮咱們指望的API長成什麼樣子。基於TDD開發,其中一個做用是幫助程序員設計指望的API,這稱爲意圖導向編程。編程
由於數據類型轉換是Convert,因此咱們先在單元測試項目中建立一個ConvertTest的類文件。 框架
類建立好之後,我先隨便建立一個方法Test,以迅速展開工做。測試的方法名Test,我是隨便起的,由於如今還不清楚API是什麼樣,我一會再回過頭來改。ide
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Util.Tests { /// <summary>
/// 類型轉換公共操做類測試 /// </summary>
[TestClass] public class ConvertTest { [TestMethod] public void Test() { } } }
爲了照顧尚未使用單元測試的朋友,我在這裏簡單介紹一下MsTest。MsTest是.Net仿照JUnit打造的一個單元測試框架。在單元測試類上須要添加一個TestClass特性,在測試方法上添加TestMethod特性,用來識別哪些類的操做須要測試。還有一些其它特性,在用到的時候我再介紹。工具
如今先來實現一個最簡單的功能,把字符串」1」轉換爲整數1。性能
[TestMethod] public void Test() { Assert.AreEqual( 1, Util.ConvertHelper.ToInt( "1" ) ); }
我把經常使用公共操做類儘可能放到頂級命名空間Util,這樣我就能夠經過編寫Util.來彈出代碼提示,這樣我連經常使用類也不用記了。單元測試
使用ConvertHelper是一個常規命名,大多數開發人員能夠理解它是一個類型轉換的公共操做類。我也這樣用了多年,不事後面我發現Util.ConvertHelper有點囉嗦,因此我簡化成Util.Convert,但Convert又和系統重名了,因此我如今使用Util.Conv,你不必定要按個人這個命名,你可使用ConvertHelper這樣的命名以提升代碼清晰度。測試
System.Convert使用ToInt32來精確表示int是一個32位的數字,不過咱們的公共操做類不用這樣精確,ToInt就能夠了,若是要封裝ToInt64呢,我就用ToLong,這樣比較符合個人習慣。ui
如今代碼被簡化成了下面的代碼。
Assert.AreEqual( 1, Util.Conv.ToInt( "1" ) );
Assert在測試中用來斷言,斷言就是比較實際計算出來的值是否和預期一致,Assert包含大量比較方法,AreEqual使用頻率最高,用來比較預期值(左邊)與實際值(右邊)是否值相等,還有一個AreSame方法用來比較是否引用相等。
因爲Conv類還未建立,因此顯示一個紅色警告。 如今在Util類庫項目中建立一個Conv類。
建立了Conv類之後,單元測試代碼檢測到Conv,但ToInt方法未建立,因此紅色警告轉移到ToInt方法。
如今用鼠標左鍵單擊紅色ToInit方法,Resharper在左側顯示一個紅色的燈泡。
單擊紅色燈泡提示,選擇第一項」Create Method ‘Conv.ToInt’」。
Resharper會在Conv類中自動建立一個ToInt方法。
public class Conv { public static int ToInt( string s ) { throw new NotImplementedException(); } }
方法體拋出一個未實現的異常,這正是咱們想要的。TDD的口訣是「紅、綠、重構」,第一步須要先保證方法執行失敗,顯示紅色警告。至於未何須要測試先行,以及首先執行失敗,牽扯TDD開發價值觀,請你們參考相關資料。
準備工做已經就緒,如今能夠運行測試了。安裝了Resharper之後,在添加了TestClass特性的左側,會看見兩個重疊在一塊兒的圓形圖標。另外,在TestMethod特性左側,有一個黑白相間的圓形圖標。
單擊Test方法左側的圖標,而後點擊Run按鈕。若是單擊TestClass特性左側的圖標,會運行該類全部測試。
測試開始運行,並顯示紅色警告,提示未實現的異常,第一步完成。
爲了實現功能,如今來添加ToInt方法的代碼。
public static int ToInt( string s ) { int result; int.TryParse( s, out result ); return result; }
再次運行測試,已經可以成功經過,第二步完成。
第三步是進行重構,如今看哪些地方能夠重構。參數s看起來有點不爽,改爲data,並添加XML註釋。
/// <summary>
/// 轉換爲整型 /// </summary>
/// <param name="data">數據</param>
public static int ToInt( string data ) { int result; int.TryParse( data, out result ); return result; }
另外重構一下測試,爲了更容易找到相關測試,通常測試文件名使用類名+Test,如今測試文件名改爲ConvTest.cs,測試類名改爲ConvTest。把測試方法名改爲TestToInt,並添加XML註釋。
/// <summary>
/// 測試轉換爲整型 /// </summary>
[TestMethod] public void TestToInt() { Assert.AreEqual( 1, Util.Conv.ToInt( "1" ) ); }
關於測試的命名,不少著做都提出了本身不一樣的方法。在《.Net單元測試藝術》中,做者建議使用三部分進行組合命名。還有一些著做建議將測試內容用下劃線分隔單詞,拼成一個長句子,以方便閱讀和理解。這可能對英文水平好的人頗有效,不過個人英文水平很爛,我拿一些單詞拼成一個長句之後,發現更難理解了。因此我所採用的測試方法命名可能不必定好,你能夠按你容易理解的方式來命名。
重構以後,須要從新測試代碼,以觀察是否致使失敗。
上面簡單介紹了TDD的一套開發流程,主要爲了照顧尚未體驗過單元測試的人,後面直接粘貼代碼,以免這樣低效的敘述方式。
單元測試代碼以下。
1 using System; 2 using Microsoft.VisualStudio.TestTools.UnitTesting; 3
4 namespace Util.Tests { 5 /// <summary>
6 /// 類型轉換公共操做類測試 7 /// </summary>
8 [TestClass] 9 public class ConvTest { 10
11 #region ToInt(轉換爲整型)
12
13 /// <summary>
14 ///轉換爲整型,值爲null 15 ///</summary>
16 [TestMethod] 17 public void TestToInt_Null() { 18 Assert.AreEqual( 0, Util.Conv.ToInt( null ) ); 19 } 20
21 /// <summary>
22 ///轉換爲整型,值爲空字符串 23 ///</summary>
24 [TestMethod] 25 public void TestToInt_Empty() { 26 Assert.AreEqual( 0, Util.Conv.ToInt( "" ) ); 27 } 28
29 /// <summary>
30 ///轉換爲整型,無效值 31 ///</summary>
32 [TestMethod] 33 public void TestToInt_Invalid() { 34 Assert.AreEqual( 0, Util.Conv.ToInt( "1A" ) ); 35 } 36
37 /// <summary>
38 ///轉換爲整型,有效值 39 ///</summary>
40 [TestMethod] 41 public void TestToInt() { 42 Assert.AreEqual( 1, Util.Conv.ToInt( "1" ) ); 43 Assert.AreEqual( 1778020, Util.Conv.ToInt( "1778019.7801684" ) ); 44 } 45
46 #endregion
47
48 #region ToIntOrNull(轉換爲可空整型)
49
50 /// <summary>
51 ///轉換爲可空整型,值爲null 52 ///</summary>
53 [TestMethod] 54 public void TestToIntOrNull_Null() { 55 Assert.IsNull( Util.Conv.ToIntOrNull( null ) ); 56 } 57
58 /// <summary>
59 ///轉換爲可空整型,值爲空字符串 60 ///</summary>
61 [TestMethod] 62 public void TestToIntOrNull_Empty() { 63 Assert.IsNull( Util.Conv.ToIntOrNull( "" ) ); 64 } 65
66 /// <summary>
67 ///轉換爲可空整型,無效值 68 ///</summary>
69 [TestMethod] 70 public void TestToIntOrNull_Invalid() { 71 Assert.IsNull( Util.Conv.ToIntOrNull( "1A" ) ); 72 } 73
74 /// <summary>
75 ///轉換爲可空整型,值爲0 76 ///</summary>
77 [TestMethod] 78 public void TestToIntOrNull_0() { 79 Assert.AreEqual( 0, Util.Conv.ToIntOrNull( "0" ) ); 80 } 81
82 /// <summary>
83 ///轉換爲可空整型,有效值 84 ///</summary>
85 [TestMethod] 86 public void TestToIntOrNull() { 87 Assert.AreEqual( 1, Util.Conv.ToIntOrNull( "1" ) ); 88 } 89
90 #endregion
91
92 #region ToDouble(轉換爲雙精度浮點數)
93
94 /// <summary>
95 ///轉換爲雙精度浮點數,值爲null 96 ///</summary>
97 [TestMethod] 98 public void TestToDouble_Null() { 99 Assert.AreEqual( 0, Util.Conv.ToDouble( null ) ); 100 } 101
102 /// <summary>
103 ///轉換爲雙精度浮點數,值爲空字符串 104 ///</summary>
105 [TestMethod] 106 public void TestToDouble_Empty() { 107 Assert.AreEqual( 0, Util.Conv.ToDouble( "" ) ); 108 } 109
110 /// <summary>
111 ///轉換爲雙精度浮點數,無效值 112 ///</summary>
113 [TestMethod] 114 public void TestToDouble_Invalid() { 115 Assert.AreEqual( 0, Util.Conv.ToDouble( "1A" ) ); 116 } 117
118 /// <summary>
119 ///轉換爲雙精度浮點數,有效值 120 ///</summary>
121 [TestMethod] 122 public void TestToDouble() { 123 Assert.AreEqual( 1.2, Util.Conv.ToDouble( "1.2" ) ); 124 } 125
126 /// <summary>
127 /// 轉換爲雙精度浮點數,指定2位小數位數 128 ///</summary>
129 [TestMethod()] 130 public void TestToDouble_DigitsIs2() { 131 Assert.AreEqual( 12.36, Util.Conv.ToDouble( "12.355", 2 ) ); 132 } 133
134 #endregion
135
136 #region ToDoubleOrNull(轉換爲可空雙精度浮點數)
137
138 /// <summary>
139 ///轉換爲可空雙精度浮點數,值爲null 140 ///</summary>
141 [TestMethod] 142 public void TestToDoubleOrNull_Null() { 143 Assert.IsNull( Util.Conv.ToDoubleOrNull( null ) ); 144 } 145
146 /// <summary>
147 ///轉換爲可空雙精度浮點數,值爲空字符串 148 ///</summary>
149 [TestMethod] 150 public void TestToDoubleOrNull_Empty() { 151 Assert.IsNull( Util.Conv.ToDoubleOrNull( "" ) ); 152 } 153
154 /// <summary>
155 ///轉換爲可空雙精度浮點數,無效值 156 ///</summary>
157 [TestMethod] 158 public void TestToDoubleOrNull_Invalid() { 159 Assert.IsNull( Util.Conv.ToDoubleOrNull( "1A" ) ); 160 } 161
162 /// <summary>
163 ///轉換爲可空雙精度浮點數,值爲0 164 ///</summary>
165 [TestMethod] 166 public void TestToDoubleOrNull_0() { 167 Assert.AreEqual( 0, Util.Conv.ToDoubleOrNull( "0" ) ); 168 } 169
170 /// <summary>
171 ///轉換爲可空雙精度浮點數,有效值 172 ///</summary>
173 [TestMethod] 174 public void TestToDoubleOrNull() { 175 Assert.AreEqual( 1.2, Util.Conv.ToDoubleOrNull( "1.2" ) ); 176 } 177
178 #endregion
179
180 #region ToDecimal(轉換爲高精度浮點數)
181
182 /// <summary>
183 ///轉換爲高精度浮點數,值爲null 184 ///</summary>
185 [TestMethod] 186 public void TestToDecimal_Null() { 187 Assert.AreEqual( 0, Util.Conv.ToDecimal( null ) ); 188 } 189
190 /// <summary>
191 ///轉換爲高精度浮點數,值爲空字符串 192 ///</summary>
193 [TestMethod] 194 public void TestToDecimal_Empty() { 195 Assert.AreEqual( 0, Util.Conv.ToDecimal( "" ) ); 196 } 197
198 /// <summary>
199 ///轉換爲高精度浮點數,無效值 200 ///</summary>
201 [TestMethod] 202 public void TestToDecimal_Invalid() { 203 Assert.AreEqual( 0, Util.Conv.ToDecimal( "1A" ) ); 204 } 205
206 /// <summary>
207 ///轉換爲高精度浮點數,有效值 208 ///</summary>
209 [TestMethod] 210 public void TestToDecimal() { 211 Assert.AreEqual( 1.2M, Util.Conv.ToDecimal( "1.2" ) ); 212 } 213
214 /// <summary>
215 /// 轉換爲高精度浮點數,指定2位小數位數 216 ///</summary>
217 [TestMethod()] 218 public void TestToDecimal_DigitsIs2() { 219 Assert.AreEqual( 12.24M, Util.Conv.ToDecimal( "12.235", 2 ) ); 220 } 221
222 #endregion
223
224 #region ToDecimalOrNull(轉換爲可空高精度浮點數)
225
226 /// <summary>
227 ///轉換爲可空高精度浮點數,值爲null 228 ///</summary>
229 [TestMethod] 230 public void TestToDecimalOrNull_Null() { 231 Assert.IsNull( Util.Conv.ToDecimalOrNull( null ) ); 232 } 233
234 /// <summary>
235 ///轉換爲可空高精度浮點數,值爲空字符串 236 ///</summary>
237 [TestMethod] 238 public void TestToDecimalOrNull_Empty() { 239 Assert.IsNull( Util.Conv.ToDecimalOrNull( "" ) ); 240 } 241
242 /// <summary>
243 ///轉換爲可空高精度浮點數,無效值 244 ///</summary>
245 [TestMethod] 246 public void TestToDecimalOrNull_Invalid() { 247 Assert.IsNull( Util.Conv.ToDecimalOrNull( "1A" ) ); 248 } 249
250 /// <summary>
251 ///轉換爲可空高精度浮點數,無效值,指定2位小數位數 252 ///</summary>
253 [TestMethod] 254 public void TestToDecimalOrNull_Invalid_DigitsIs2() { 255 Assert.IsNull( Util.Conv.ToDecimalOrNull( "1A", 2 ) ); 256 } 257
258 /// <summary>
259 ///轉換爲可空高精度浮點數,值爲0 260 ///</summary>
261 [TestMethod] 262 public void TestToDecimalOrNull_0() { 263 Assert.AreEqual( 0, Util.Conv.ToDecimalOrNull( "0" ) ); 264 } 265
266 /// <summary>
267 ///轉換爲可空高精度浮點數,有效值 268 ///</summary>
269 [TestMethod] 270 public void TestToDecimalOrNull() { 271 Assert.AreEqual( 1.2M, Util.Conv.ToDecimalOrNull( "1.2" ) ); 272 } 273
274 /// <summary>
275 /// 轉換爲可空高精度浮點數,指定2位小數位數 276 ///</summary>
277 [TestMethod()] 278 public void ToDecimalOrNull_DigitsIs2() { 279 Assert.AreEqual( 12.24M, Util.Conv.ToDecimalOrNull( "12.235", 2 ) ); 280 } 281
282 #endregion
283
284 #region ToGuid(轉換爲Guid)
285
286 /// <summary>
287 ///轉換爲Guid,值爲null 288 ///</summary>
289 [TestMethod] 290 public void TestToGuid_Null() { 291 Assert.AreEqual( Guid.Empty, Util.Conv.ToGuid( null ) ); 292 } 293
294 /// <summary>
295 ///轉換爲Guid,值爲空字符串 296 ///</summary>
297 [TestMethod] 298 public void TestToGuid_Empty() { 299 Assert.AreEqual( Guid.Empty, Util.Conv.ToGuid( "" ) ); 300 } 301
302 /// <summary>
303 ///轉換爲Guid,無效值 304 ///</summary>
305 [TestMethod] 306 public void TestToGuid_Invalid() { 307 Assert.AreEqual( Guid.Empty, Util.Conv.ToGuid( "1A" ) ); 308 } 309
310 /// <summary>
311 ///轉換爲Guid,有效值 312 ///</summary>
313 [TestMethod] 314 public void TestToGuid() { 315 Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Conv.ToGuid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); 316 } 317
318 #endregion
319
320 #region ToGuidOrNull(轉換爲可空Guid)
321
322 /// <summary>
323 ///轉換爲可空Guid,值爲null 324 ///</summary>
325 [TestMethod] 326 public void TestToGuidOrNull_Null() { 327 Assert.IsNull( Util.Conv.ToGuidOrNull( null ) ); 328 } 329
330 /// <summary>
331 ///轉換爲可空Guid,值爲空字符串 332 ///</summary>
333 [TestMethod] 334 public void TestToGuidOrNull_Empty() { 335 Assert.IsNull( Util.Conv.ToGuidOrNull( "" ) ); 336 } 337
338 /// <summary>
339 ///轉換爲可空Guid,無效值 340 ///</summary>
341 [TestMethod] 342 public void TestToGuidOrNull_Invalid() { 343 Assert.IsNull( Util.Conv.ToGuidOrNull( "1A" ) ); 344 } 345
346 /// <summary>
347 ///轉換爲可空Guid,有效值 348 ///</summary>
349 [TestMethod] 350 public void TestToGuidOrNull() { 351 Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Conv.ToGuidOrNull( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); 352 } 353
354 #endregion
355
356 #region ToGuidList(轉換爲Guid集合)
357
358 /// <summary>
359 /// 轉換爲Guid集合,驗證空字符串 360 /// </summary>
361 [TestMethod] 362 public void TestToGuidList_Empty() { 363 Assert.AreEqual( 0, Util.Conv.ToGuidList( "" ).Count ); 364 } 365
366 /// <summary>
367 /// 轉換爲Guid集合,驗證最後爲逗號 368 /// </summary>
369 [TestMethod] 370 public void TestToGuidList_LastIsComma() { 371 Assert.AreEqual( 1, Util.Conv.ToGuidList( "83B0233C-A24F-49FD-8083-1337209EBC9A," ).Count ); 372 } 373
374 /// <summary>
375 /// 轉換爲Guid集合,驗證中間包含逗號 376 /// </summary>
377 [TestMethod] 378 public void TestToGuidList_MiddleIsComma() { 379 const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,"; 380 Assert.AreEqual( 2, Util.Conv.ToGuidList( guid ).Count ); 381 Assert.AreEqual( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Util.Conv.ToGuidList( guid )[0] ); 382 Assert.AreEqual( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Util.Conv.ToGuidList( guid )[1] ); 383 } 384
385 /// <summary>
386 /// 轉換爲Guid集合,僅1個Guid 387 /// </summary>
388 [TestMethod] 389 public void TestToGuidList_1Guid() { 390 const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A"; 391 Assert.AreEqual( 1, Util.Conv.ToGuidList( guid ).Count ); 392 Assert.AreEqual( new Guid( guid ), Util.Conv.ToGuidList( guid )[0] ); 393 } 394
395 /// <summary>
396 /// 轉換爲Guid集合,2個Guid 397 /// </summary>
398 [TestMethod] 399 public void TestToGuidList_2Guid() { 400 const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A"; 401 Assert.AreEqual( 2, Util.Conv.ToGuidList( guid ).Count ); 402 Assert.AreEqual( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Util.Conv.ToGuidList( guid )[0] ); 403 Assert.AreEqual( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Util.Conv.ToGuidList( guid )[1] ); 404 } 405
406 #endregion
407
408 #region ToDate(轉換爲日期)
409
410 /// <summary>
411 ///轉換爲日期,值爲null 412 ///</summary>
413 [TestMethod] 414 public void TestToDate_Null() { 415 Assert.AreEqual( DateTime.MinValue, Util.Conv.ToDate( null ) ); 416 } 417
418 /// <summary>
419 ///轉換爲日期,值爲空字符串 420 ///</summary>
421 [TestMethod] 422 public void TestToDate_Empty() { 423 Assert.AreEqual( DateTime.MinValue, Util.Conv.ToDate( "" ) ); 424 } 425
426 /// <summary>
427 ///轉換爲日期,無效值 428 ///</summary>
429 [TestMethod] 430 public void TestToDate_Invalid() { 431 Assert.AreEqual( DateTime.MinValue, Util.Conv.ToDate( "1A" ) ); 432 } 433
434 /// <summary>
435 ///轉換爲日期,有效值 436 ///</summary>
437 [TestMethod] 438 public void TestToDate() { 439 Assert.AreEqual( new DateTime( 2000, 1, 1 ), Util.Conv.ToDate( "2000-1-1" ) ); 440 } 441
442 #endregion
443
444 #region ToDateOrNull(轉換爲可空日期)
445
446 /// <summary>
447 ///轉換爲可空日期,值爲null 448 ///</summary>
449 [TestMethod] 450 public void TestToDateOrNull_Null() { 451 Assert.IsNull( Util.Conv.ToDateOrNull( null ) ); 452 } 453
454 /// <summary>
455 ///轉換爲可空日期,值爲空字符串 456 ///</summary>
457 [TestMethod] 458 public void TestToDateOrNull_Empty() { 459 Assert.IsNull( Util.Conv.ToDateOrNull( "" ) ); 460 } 461
462 /// <summary>
463 ///轉換爲可空日期,無效值 464 ///</summary>
465 [TestMethod] 466 public void TestToDateOrNull_Invalid() { 467 Assert.IsNull( Util.Conv.ToDateOrNull( "1A" ) ); 468 } 469
470 /// <summary>
471 ///轉換爲可空日期,有效值 472 ///</summary>
473 [TestMethod] 474 public void TestToDateOrNull() { 475 Assert.AreEqual( new DateTime( 2000, 1, 1 ), Util.Conv.ToDateOrNull( "2000-1-1" ) ); 476 } 477
478 #endregion
479
480 #region ToBool(轉換爲布爾值)
481
482 /// <summary>
483 ///轉換爲布爾值,值爲null 484 ///</summary>
485 [TestMethod] 486 public void TestToBool_Null() { 487 Assert.AreEqual( false, Util.Conv.ToBool( null ) ); 488 } 489
490 /// <summary>
491 ///轉換爲布爾值,值爲空字符串 492 ///</summary>
493 [TestMethod] 494 public void TestToBool_Empty() { 495 Assert.AreEqual( false, Util.Conv.ToBool( "" ) ); 496 } 497
498 /// <summary>
499 ///轉換爲布爾值,無效值 500 ///</summary>
501 [TestMethod] 502 public void TestToBool_Invalid() { 503 Assert.AreEqual( false, Util.Conv.ToBool( "1A" ) ); 504 } 505
506 /// <summary>
507 ///轉換爲布爾值,值爲False 508 ///</summary>
509 [TestMethod] 510 public void TestToBool_False() { 511 Assert.AreEqual( false, Util.Conv.ToBool( "0" ) ); 512 Assert.AreEqual( false, Util.Conv.ToBool( "否" ) ); 513 Assert.AreEqual( false, Util.Conv.ToBool( "no" ) ); 514 Assert.AreEqual( false, Util.Conv.ToBool( "No" ) ); 515 Assert.AreEqual( false, Util.Conv.ToBool( "false" ) ); 516 Assert.AreEqual( false, Util.Conv.ToBool( "False" ) ); 517 } 518
519 /// <summary>
520 ///轉換爲布爾值,值爲True 521 ///</summary>
522 [TestMethod] 523 public void TestToBool_True() { 524 Assert.AreEqual( true, Util.Conv.ToBool( "1" ) ); 525 Assert.AreEqual( true, Util.Conv.ToBool( "是" ) ); 526 Assert.AreEqual( true, Util.Conv.ToBool( "yes" ) ); 527 Assert.AreEqual( true, Util.Conv.ToBool( "Yes" ) ); 528 Assert.AreEqual( true, Util.Conv.ToBool( "true" ) ); 529 Assert.AreEqual( true, Util.Conv.ToBool( "True" ) ); 530 } 531
532 #endregion
533
534 #region ToBoolOrNull(轉換爲可空布爾值)
535
536 /// <summary>
537 ///轉換爲可空布爾值,值爲null 538 ///</summary>
539 [TestMethod] 540 public void TestToBoolOrNull_Null() { 541 Assert.IsNull( Util.Conv.ToBoolOrNull( null ) ); 542 } 543
544 /// <summary>
545 ///轉換爲可空布爾值,值爲空字符串 546 ///</summary>
547 [TestMethod] 548 public void TestToBoolOrNull_Empty() { 549 Assert.IsNull( Util.Conv.ToBoolOrNull( "" ) ); 550 } 551
552 /// <summary>
553 ///轉換爲可空布爾值,無效值 554 ///</summary>
555 [TestMethod] 556 public void TestToBoolOrNull_Invalid() { 557 Assert.IsNull( Util.Conv.ToBoolOrNull( "1A" ) ); 558 } 559
560 /// <summary>
561 ///轉換爲布爾值,值爲False 562 ///</summary>
563 [TestMethod] 564 public void TestToBoolOrNull_False() { 565 Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "0" ) ); 566 Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "否" ) ); 567 Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "no" ) ); 568 Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "No" ) ); 569 Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "false" ) ); 570 Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "False" ) ); 571 } 572
573 /// <summary>
574 ///轉換爲布爾值,值爲True 575 ///</summary>
576 [TestMethod] 577 public void TestToBoolOrNull_True() { 578 Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "1" ) ); 579 Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "是" ) ); 580 Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "yes" ) ); 581 Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "Yes" ) ); 582 Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "true" ) ); 583 Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "True" ) ); 584 } 585
586 #endregion
587
588 #region ToString(轉換爲字符串)
589
590 /// <summary>
591 ///轉換爲字符串,值爲null 592 ///</summary>
593 [TestMethod] 594 public void TestToString_Null() { 595 Assert.AreEqual( string.Empty, Util.Conv.ToString( null ) ); 596 } 597
598 /// <summary>
599 ///轉換爲字符串,值爲空字符串 600 ///</summary>
601 [TestMethod] 602 public void TestToString_Empty() { 603 Assert.AreEqual( string.Empty, Util.Conv.ToString( " " ) ); 604 } 605
606 /// <summary>
607 ///轉換爲字符串,有效值 608 ///</summary>
609 [TestMethod] 610 public void TestToString() { 611 Assert.AreEqual( "1", Util.Conv.ToString( 1 ) ); 612 } 613
614 #endregion
615
616 #region To(通用泛型轉換)
617
618 #region 目標爲int
619
620 /// <summary>
621 ///通用泛型轉換,目標爲整數,值爲null 622 ///</summary>
623 [TestMethod] 624 public void TestTo_Int_Null() { 625 Assert.AreEqual( 0, Conv.To<int>( null ) ); 626 } 627
628 /// <summary>
629 ///通用泛型轉換,目標爲整數,值爲空字符串 630 ///</summary>
631 [TestMethod] 632 public void TestTo_Int_Empty() { 633 Assert.AreEqual( 0, Conv.To<int>( "" ) ); 634 } 635
636 /// <summary>
637 ///通用泛型轉換,目標爲整數,無效值 638 ///</summary>
639 [TestMethod] 640 public void TestTo_Int_Invalid() { 641 Assert.AreEqual( 0, Conv.To<int>( "1A" ) ); 642 } 643
644 /// <summary>
645 ///通用泛型轉換,目標爲整數,有效值 646 ///</summary>
647 [TestMethod] 648 public void TestTo_Int() { 649 Assert.AreEqual( 1, Conv.To<int>( "1" ) ); 650 } 651
652 /// <summary>
653 ///通用泛型轉換,目標爲可空整數,無效值 654 ///</summary>
655 [TestMethod] 656 public void TestTo_IntOrNull_Invalid() { 657 Assert.IsNull( Conv.To<int?>( "1A" ) ); 658 } 659
660 /// <summary>
661 ///通用泛型轉換,目標爲可空整數,有效值 662 ///</summary>
663 [TestMethod] 664 public void TestTo_IntOrNull() { 665 Assert.AreEqual( 1, Conv.To<int?>( "1" ) ); 666 } 667
668 #endregion
669
670 #region 目標爲Guid
671
672 /// <summary>
673 ///通用泛型轉換,目標爲Guid,無效值 674 ///</summary>
675 [TestMethod] 676 public void TestTo_Guid_Invalid() { 677 Assert.AreEqual( Guid.Empty, Conv.To<Guid>( "1A" ) ); 678 } 679
680 /// <summary>
681 ///通用泛型轉換,目標爲Guid,有效值 682 ///</summary>
683 [TestMethod] 684 public void TestTo_Guid() { 685 Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), 686 Conv.To<Guid>( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); 687 } 688
689 /// <summary>
690 ///通用泛型轉換,目標爲可空Guid,無效值 691 ///</summary>
692 [TestMethod] 693 public void TestTo_GuidOrNull_Invalid() { 694 Assert.IsNull( Conv.To<Guid?>( "1A" ) ); 695 } 696
697 /// <summary>
698 ///通用泛型轉換,目標爲可空Guid,有效值 699 ///</summary>
700 [TestMethod] 701 public void TestTo_GuidOrNull() { 702 Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), 703 Conv.To<Guid?>( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); 704 } 705
706 #endregion
707
708 #region 目標爲string
709
710 /// <summary>
711 ///通用泛型轉換,目標爲string,有效值 712 ///</summary>
713 [TestMethod] 714 public void TestTo_String() { 715 Assert.AreEqual( "123", Conv.To<string>( 123 ) ); 716 } 717
718 #endregion
719
720 #region 目標爲double
721
722 /// <summary>
723 ///通用泛型轉換,目標爲double,無效值 724 ///</summary>
725 [TestMethod] 726 public void TestTo_Double_Invalid() { 727 Assert.AreEqual( 0, Conv.To<double>( "1A" ) ); 728 } 729
730 /// <summary>
731 ///通用泛型轉換,目標爲double,有效值 732 ///</summary>
733 [TestMethod] 734 public void TestTo_Double() { 735 Assert.AreEqual( 12.5, Conv.To<double>( "12.5" ) ); 736 } 737
738 /// <summary>
739 ///通用泛型轉換,目標爲可空double,無效值 740 ///</summary>
741 [TestMethod] 742 public void TestTo_DoubleOrNull_Invalid() { 743 Assert.IsNull( Conv.To<double?>( "1A" ) ); 744 } 745
746 /// <summary>
747 ///通用泛型轉換,目標爲可空double,有效值 748 ///</summary>
749 [TestMethod] 750 public void TestTo_DoubleOrNull() { 751 Assert.AreEqual( 12.5, Conv.To<double?>( "12.5" ) ); 752 } 753
754 #endregion
755
756 #region 目標爲decimal
757
758 /// <summary>
759 ///通用泛型轉換,目標爲decimal,無效值 760 ///</summary>
761 [TestMethod] 762 public void TestTo_Decimal_Invalid() { 763 Assert.AreEqual( 0, Conv.To<decimal>( "1A" ) ); 764 } 765
766 /// <summary>
767 ///通用泛型轉換,目標爲decimal,有效值 768 ///</summary>
769 [TestMethod] 770 public void TestTo_Decimal() { 771 Assert.AreEqual( 12.5M, Conv.To<decimal>( "12.5" ) ); 772 } 773
774 /// <summary>
775 ///通用泛型轉換,目標爲可空decimal,無效值 776 ///</summary>
777 [TestMethod] 778 public void TestTo_DecimalOrNull_Invalid() { 779 Assert.IsNull( Conv.To<decimal?>( "1A" ) ); 780 } 781
782 /// <summary>
783 ///通用泛型轉換,目標爲可空decimal,有效值 784 ///</summary>
785 [TestMethod] 786 public void TestTo_DecimalOrNull() { 787 Assert.AreEqual( 12.5M, Conv.To<decimal?>( "12.5" ) ); 788 } 789
790 #endregion
791
792 #region 目標爲bool
793
794 /// <summary>
795 ///通用泛型轉換,目標爲bool,無效值 796 ///</summary>
797 [TestMethod] 798 public void TestTo_Bool_Invalid() { 799 Assert.AreEqual( false, Conv.To<bool>( "1A" ) ); 800 } 801
802 /// <summary>
803 ///通用泛型轉換,目標爲bool,有效值 804 ///</summary>
805 [TestMethod] 806 public void TestTo_Bool() { 807 Assert.AreEqual( true, Conv.To<bool>( 1 ) ); 808 } 809
810 /// <summary>
811 ///通用泛型轉換,目標爲可空bool,無效值 812 ///</summary>
813 [TestMethod] 814 public void TestTo_BoolOrNull_Invalid() { 815 Assert.IsNull( Conv.To<bool?>( "1A" ) ); 816 } 817
818 /// <summary>
819 ///通用泛型轉換,目標爲可空bool,有效值 820 ///</summary>
821 [TestMethod] 822 public void TestTo_BoolOrNull() { 823 Assert.AreEqual( true, Conv.To<bool?>( "true" ) ); 824 } 825
826 #endregion
827
828 #region 目標爲DateTime
829
830 /// <summary>
831 ///通用泛型轉換,目標爲DateTime,無效值 832 ///</summary>
833 [TestMethod] 834 public void TestTo_DateTime_Invalid() { 835 Assert.AreEqual( DateTime.MinValue, Conv.To<DateTime>( "1A" ) ); 836 } 837
838 /// <summary>
839 ///通用泛型轉換,目標爲DateTime,有效值 840 ///</summary>
841 [TestMethod] 842 public void TestTo_DateTime() { 843 Assert.AreEqual( new DateTime( 2000, 1, 1 ), Conv.To<DateTime>( "2000-1-1" ) ); 844 } 845
846 /// <summary>
847 ///通用泛型轉換,目標爲可空DateTime,無效值 848 ///</summary>
849 [TestMethod] 850 public void TestTo_DateTimeOrNull_Invalid() { 851 Assert.IsNull( Conv.To<DateTime?>( "1A" ) ); 852 } 853
854 /// <summary>
855 ///通用泛型轉換,目標爲可空DateTime,有效值 856 ///</summary>
857 [TestMethod] 858 public void TestTo_DateTimeOrNull() { 859 Assert.AreEqual( new DateTime( 2000, 1, 1 ), Conv.To<DateTime?>( "2000-1-1" ) ); 860 } 861
862 #endregion
863
864 #endregion
865 } 866 }
Conv類代碼以下。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4
5 namespace Util { 6 /// <summary>
7 /// 類型轉換 8 /// </summary>
9 public static class Conv { 10
11 #region 數值轉換
12
13 /// <summary>
14 /// 轉換爲整型 15 /// </summary>
16 /// <param name="data">數據</param>
17 public static int ToInt( object data ) { 18 if ( data == null ) 19 return 0; 20 int result; 21 var success = int.TryParse( data.ToString(), out result ); 22 if ( success == true ) 23 return result; 24 try { 25 return Convert.ToInt32( ToDouble( data, 0 ) ); 26 } 27 catch ( Exception ) { 28 return 0; 29 } 30 } 31
32 /// <summary>
33 /// 轉換爲可空整型 34 /// </summary>
35 /// <param name="data">數據</param>
36 public static int? ToIntOrNull( object data ) { 37 if ( data == null ) 38 return null; 39 int result; 40 bool isValid = int.TryParse( data.ToString(), out result ); 41 if ( isValid ) 42 return result; 43 return null; 44 } 45
46 /// <summary>
47 /// 轉換爲雙精度浮點數 48 /// </summary>
49 /// <param name="data">數據</param>
50 public static double ToDouble( object data ) { 51 if ( data == null ) 52 return 0; 53 double result; 54 return double.TryParse( data.ToString(), out result ) ? result : 0; 55 } 56
57 /// <summary>
58 /// 轉換爲雙精度浮點數,並按指定的小數位4舍5入 59 /// </summary>
60 /// <param name="data">數據</param>
61 /// <param name="digits">小數位數</param>
62 public static double ToDouble( object data, int digits ) { 63 return Math.Round( ToDouble( data ), digits ); 64 } 65
66 /// <summary>
67 /// 轉換爲可空雙精度浮點數 68 /// </summary>
69 /// <param name="data">數據</param>
70 public static double? ToDoubleOrNull( object data ) { 71 if ( data == null ) 72 return null; 73 double result; 74 bool isValid = double.TryParse( data.ToString(), out result ); 75 if ( isValid ) 76 return result; 77 return null; 78 } 79
80 /// <summary>
81 /// 轉換爲高精度浮點數 82 /// </summary>
83 /// <param name="data">數據</param>
84 public static decimal ToDecimal( object data ) { 85 if ( data == null ) 86 return 0; 87 decimal result; 88 return decimal.TryParse( data.ToString(), out result ) ? result : 0; 89 } 90
91 /// <summary>
92 /// 轉換爲高精度浮點數,並按指定的小數位4舍5入 93 /// </summary>
94 /// <param name="data">數據</param>
95 /// <param name="digits">小數位數</param>
96 public static decimal ToDecimal( object data, int digits ) { 97 return Math.Round( ToDecimal( data ), digits ); 98 } 99
100 /// <summary>
101 /// 轉換爲可空高精度浮點數 102 /// </summary>
103 /// <param name="data">數據</param>
104 public static decimal? ToDecimalOrNull( object data ) { 105 if ( data == null ) 106 return null; 107 decimal result; 108 bool isValid = decimal.TryParse( data.ToString(), out result ); 109 if ( isValid ) 110 return result; 111 return null; 112 } 113
114 /// <summary>
115 /// 轉換爲可空高精度浮點數,並按指定的小數位4舍5入 116 /// </summary>
117 /// <param name="data">數據</param>
118 /// <param name="digits">小數位數</param>
119 public static decimal? ToDecimalOrNull( object data, int digits ) { 120 var result = ToDecimalOrNull( data ); 121 if ( result == null ) 122 return null; 123 return Math.Round( result.Value, digits ); 124 } 125
126 #endregion
127
128 #region Guid轉換
129
130 /// <summary>
131 /// 轉換爲Guid 132 /// </summary>
133 /// <param name="data">數據</param>
134 public static Guid ToGuid( object data ) { 135 if ( data == null ) 136 return Guid.Empty; 137 Guid result; 138 return Guid.TryParse( data.ToString(), out result ) ? result : Guid.Empty; 139 } 140
141 /// <summary>
142 /// 轉換爲可空Guid 143 /// </summary>
144 /// <param name="data">數據</param>
145 public static Guid? ToGuidOrNull( object data ) { 146 if ( data == null ) 147 return null; 148 Guid result; 149 bool isValid = Guid.TryParse( data.ToString(), out result ); 150 if ( isValid ) 151 return result; 152 return null; 153 } 154
155 /// <summary>
156 /// 轉換爲Guid集合 157 /// </summary>
158 /// <param name="guid">guid集合字符串,範例:83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A</param>
159 public static List<Guid> ToGuidList( string guid ) { 160 var listGuid = new List<Guid>(); 161 if ( string.IsNullOrWhiteSpace( guid ) ) 162 return listGuid; 163 var arrayGuid = guid.Split( ',' ); 164 listGuid.AddRange( from each in arrayGuid where !string.IsNullOrWhiteSpace( each ) select new Guid( each ) ); 165 return listGuid; 166 } 167
168 #endregion
169
170 #region 日期轉換
171
172 /// <summary>
173 /// 轉換爲日期 174 /// </summary>
175 /// <param name="data">數據</param>
176 public static DateTime ToDate( object data ) { 177 if ( data == null ) 178 return DateTime.MinValue; 179 DateTime result; 180 return DateTime.TryParse( data.ToString(), out result ) ? result : DateTime.MinValue; 181 } 182
183 /// <summary>
184 /// 轉換爲可空日期 185 /// </summary>
186 /// <param name="data">數據</param>
187 public static DateTime? ToDateOrNull( object data ) { 188 if ( data == null ) 189 return null; 190 DateTime result; 191 bool isValid = DateTime.TryParse( data.ToString(), out result ); 192 if ( isValid ) 193 return result; 194 return null; 195 } 196
197 #endregion
198
199 #region 布爾轉換
200
201 /// <summary>
202 /// 轉換爲布爾值 203 /// </summary>
204 /// <param name="data">數據</param>
205 public static bool ToBool( object data ) { 206 if ( data == null ) 207 return false; 208 bool? value = GetBool( data ); 209 if ( value != null ) 210 return value.Value; 211 bool result; 212 return bool.TryParse( data.ToString(), out result ) && result; 213 } 214
215 /// <summary>
216 /// 獲取布爾值 217 /// </summary>
218 private static bool? GetBool( object data ) { 219 switch ( data.ToString().Trim().ToLower() ) { 220 case "0": 221 return false; 222 case "1": 223 return true; 224 case "是": 225 return true; 226 case "否": 227 return false; 228 case "yes": 229 return true; 230 case "no": 231 return false; 232 default: 233 return null; 234 } 235 } 236
237 /// <summary>
238 /// 轉換爲可空布爾值 239 /// </summary>
240 /// <param name="data">數據</param>
241 public static bool? ToBoolOrNull( object data ) { 242 if ( data == null ) 243 return null; 244 bool? value = GetBool( data ); 245 if ( value != null ) 246 return value.Value; 247 bool result; 248 bool isValid = bool.TryParse( data.ToString(), out result ); 249 if ( isValid ) 250 return result; 251 return null; 252 } 253
254 #endregion
255
256 #region 字符串轉換
257
258 /// <summary>
259 /// 轉換爲字符串 260 /// </summary>
261 /// <param name="data">數據</param>
262 public static string ToString( object data ) { 263 return data == null ? string.Empty : data.ToString().Trim(); 264 } 265
266 #endregion
267
268 #region 通用轉換
269
270 /// <summary>
271 /// 泛型轉換 272 /// </summary>
273 /// <typeparam name="T">目標類型</typeparam>
274 /// <param name="data">數據</param>
275 public static T To<T>( object data ) { 276 if ( data == null || string.IsNullOrWhiteSpace( data.ToString() ) ) 277 return default( T ); 278 Type type = Nullable.GetUnderlyingType( typeof( T ) ) ?? typeof( T ); 279 try { 280 if ( type.Name.ToLower() == "guid" ) 281 return (T)(object)new Guid( data.ToString() ); 282 if ( data is IConvertible ) 283 return (T)Convert.ChangeType( data, type ); 284 return (T)data; 285 } 286 catch { 287 return default( T ); 288 } 289 } 290
291 #endregion
292 } 293 }
Conv公共操做類的用法,在單元測試中已經說得很清楚了,這也是單元測試的一個用途,即做爲API說明文檔。
單元測試最強大的地方,多是可以幫助你迴歸測試,若是你發現個人代碼有BUG,請通知我一聲,我只須要在單元測試中增長一個測試來捕獲這個BUG,就能夠永久修復它,而且因爲採用TDD方式能夠得到很高的測試覆蓋率,因此我花上幾秒鐘運行一下所有測試,就能夠知道此次修改有沒有影響其它代碼。這也是你建立本身的應用程序框架所必需要作的,它能夠給你提供信心。
能夠看到,我在單元測試中進行了不少邊界測試,好比參數爲null或空字符串等。但不可能窮舉全部可能出錯的狀況,由於可能想不到,另外時間有限,也不可能作到。當在項目上發現BUG後,再經過添加單元測試的方式修復BUG就能夠了。因爲你的項目代碼調用的是應用程序框架API,因此你只須要在框架內修復一次,項目代碼徹底不動。
像數據類型轉換這樣簡單的操做,你發現寫單元測試很是容易,由於它有明確的返回值,但若是沒有返回值呢,甚至有外部依賴呢,那就沒有這麼簡單了,須要不少技巧,因此你多看幾本TDD和單元測試方面的著做有不少好處。
另外,再補充一下,Conv這個類裏面有幾個法寶。一個是ToGuidList這個方法,當你須要把字符串轉換爲List<Guid>的時候就用它。還有一個泛型轉換的方法To<T>,不少時候能夠用它進行泛型轉換。
最後,我把全部方法參數類型都改爲了object,主要是想使用起來方便一點,而不是隻支持字符串參數,這可能致使裝箱和拆箱,從而形成一些性能損失,不過個人大多數項目在性能方面尚未這麼高的要求,因此這個損失對我來說無關痛癢。
還有些數據類型轉換,我沒有放進來,主要是我平時不多用到,當我用到時再增長。
.Net應用程序框架交流QQ羣: 386092459,歡迎有興趣的朋友加入討論。
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/xiadao521/
下載地址: http://files.cnblogs.com/xiadao521/Util.2014.11.12.1.rar