Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)


  上一篇介绍了数据类型转换的一些情况,可以看出,如果不进行封装,有可能导致比较混乱的代码。本文通过TDD方式把数据类型转换公共操作类开发出来,并提供源码下载。

  我们在 应用程序框架实战十一:创建VS解决方案与程序集 一文已经创建了解决方案,包含一个类库项目和一个单元测试项目。单元测试将使用.Net自带的 MsTest,另外通过Resharper工具来观察测试结果。

  首先考虑我们期望的API长成什么样子。基于TDD开发,其中一个作用是帮助程序员设计期望的API,这称为意图导向编程。

  因为数据类型转换是Convert,所以我们先在单元测试项目中创建一个ConvertTest的类文件。 

  类创建好以后,我先随便创建一个方法Test,以迅速展开工作。测试的方法名Test,我是随便起的,因为现在还不清楚API是什么样,我一会再回过头来改。

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,这样比较符合我的习惯。

  现在代码被简化成了下面的代码。

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 }
ConvTest

  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

  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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM