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( , Util.ConvertHelper.ToInt( "" ) );
}
我把常用公共操作类尽量放到顶级命名空间Util,这样我就可以通过编写Util.来弹出代码提示,这样我连常用类也不用记了。
使用ConvertHelper是一个常规命名,大多数开发人员可以理解它是一个类型转换的公共操作类。我也这样用了多年,不过后面我发现Util.ConvertHelper有点啰嗦,所以我简化成Util.Convert,但Convert又和系统重名了,所以我现在使用Util.Conv,你不一定要按我的这个命名,你可以使用ConvertHelper这样的命名以提高代码清晰度。
System.Convert使用ToInt32来精确表示int是一个32位的数字,不过我们的公共操作类不用这样精确,ToInt就可以了,如果要封装ToInt64呢,我就用ToLong,这样比较符合我的习惯。
现在代码被简化成了下面的代码。
Assert.AreEqual( , Util.Conv.ToInt( "" ) );
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( , Util.Conv.ToInt( "" ) );
}
关于测试的命名,很多著作都提出了自己不同的方法。在《.Net单元测试艺术》中,作者建议使用三部分进行组合命名。还有一些著作建议将测试内容用下划线分隔单词,拼成一个长句子,以方便阅读和理解。这可能对英文水平好的人很有效,不过我的英文水平很烂,我拿一些单词拼成一个长句以后,发现更难理解了。所以我所采用的测试方法命名可能不一定好,你可以按你容易理解的方式来命名。
重构之后,需要重新测试代码,以观察是否导致失败。
上面简单介绍了TDD的一套开发流程,主要为了照顾还没有体验过单元测试的人,后面直接粘贴代码,以避免这样低效的叙述方式。
单元测试代码如下。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Util.Tests {
/// <summary>
/// 类型转换公共操作类测试
/// </summary>
[TestClass]
public class ConvTest { #region ToInt(转换为整型) /// <summary>
///转换为整型,值为null
///</summary>
[TestMethod]
public void TestToInt_Null() {
Assert.AreEqual( , Util.Conv.ToInt( null ) );
} /// <summary>
///转换为整型,值为空字符串
///</summary>
[TestMethod]
public void TestToInt_Empty() {
Assert.AreEqual( , Util.Conv.ToInt( "" ) );
} /// <summary>
///转换为整型,无效值
///</summary>
[TestMethod]
public void TestToInt_Invalid() {
Assert.AreEqual( , Util.Conv.ToInt( "1A" ) );
} /// <summary>
///转换为整型,有效值
///</summary>
[TestMethod]
public void TestToInt() {
Assert.AreEqual( , Util.Conv.ToInt( "" ) );
Assert.AreEqual( , Util.Conv.ToInt( "1778019.7801684" ) );
} #endregion #region ToIntOrNull(转换为可空整型) /// <summary>
///转换为可空整型,值为null
///</summary>
[TestMethod]
public void TestToIntOrNull_Null() {
Assert.IsNull( Util.Conv.ToIntOrNull( null ) );
} /// <summary>
///转换为可空整型,值为空字符串
///</summary>
[TestMethod]
public void TestToIntOrNull_Empty() {
Assert.IsNull( Util.Conv.ToIntOrNull( "" ) );
} /// <summary>
///转换为可空整型,无效值
///</summary>
[TestMethod]
public void TestToIntOrNull_Invalid() {
Assert.IsNull( Util.Conv.ToIntOrNull( "1A" ) );
} /// <summary>
///转换为可空整型,值为0
///</summary>
[TestMethod]
public void TestToIntOrNull_0() {
Assert.AreEqual( , Util.Conv.ToIntOrNull( "" ) );
} /// <summary>
///转换为可空整型,有效值
///</summary>
[TestMethod]
public void TestToIntOrNull() {
Assert.AreEqual( , Util.Conv.ToIntOrNull( "" ) );
} #endregion #region ToDouble(转换为双精度浮点数) /// <summary>
///转换为双精度浮点数,值为null
///</summary>
[TestMethod]
public void TestToDouble_Null() {
Assert.AreEqual( , Util.Conv.ToDouble( null ) );
} /// <summary>
///转换为双精度浮点数,值为空字符串
///</summary>
[TestMethod]
public void TestToDouble_Empty() {
Assert.AreEqual( , Util.Conv.ToDouble( "" ) );
} /// <summary>
///转换为双精度浮点数,无效值
///</summary>
[TestMethod]
public void TestToDouble_Invalid() {
Assert.AreEqual( , Util.Conv.ToDouble( "1A" ) );
} /// <summary>
///转换为双精度浮点数,有效值
///</summary>
[TestMethod]
public void TestToDouble() {
Assert.AreEqual( 1.2, Util.Conv.ToDouble( "1.2" ) );
} /// <summary>
/// 转换为双精度浮点数,指定2位小数位数
///</summary>
[TestMethod()]
public void TestToDouble_DigitsIs2() {
Assert.AreEqual( 12.36, Util.Conv.ToDouble( "12.355", ) );
} #endregion #region ToDoubleOrNull(转换为可空双精度浮点数) /// <summary>
///转换为可空双精度浮点数,值为null
///</summary>
[TestMethod]
public void TestToDoubleOrNull_Null() {
Assert.IsNull( Util.Conv.ToDoubleOrNull( null ) );
} /// <summary>
///转换为可空双精度浮点数,值为空字符串
///</summary>
[TestMethod]
public void TestToDoubleOrNull_Empty() {
Assert.IsNull( Util.Conv.ToDoubleOrNull( "" ) );
} /// <summary>
///转换为可空双精度浮点数,无效值
///</summary>
[TestMethod]
public void TestToDoubleOrNull_Invalid() {
Assert.IsNull( Util.Conv.ToDoubleOrNull( "1A" ) );
} /// <summary>
///转换为可空双精度浮点数,值为0
///</summary>
[TestMethod]
public void TestToDoubleOrNull_0() {
Assert.AreEqual( , Util.Conv.ToDoubleOrNull( "" ) );
} /// <summary>
///转换为可空双精度浮点数,有效值
///</summary>
[TestMethod]
public void TestToDoubleOrNull() {
Assert.AreEqual( 1.2, Util.Conv.ToDoubleOrNull( "1.2" ) );
} #endregion #region ToDecimal(转换为高精度浮点数) /// <summary>
///转换为高精度浮点数,值为null
///</summary>
[TestMethod]
public void TestToDecimal_Null() {
Assert.AreEqual( , Util.Conv.ToDecimal( null ) );
} /// <summary>
///转换为高精度浮点数,值为空字符串
///</summary>
[TestMethod]
public void TestToDecimal_Empty() {
Assert.AreEqual( , Util.Conv.ToDecimal( "" ) );
} /// <summary>
///转换为高精度浮点数,无效值
///</summary>
[TestMethod]
public void TestToDecimal_Invalid() {
Assert.AreEqual( , Util.Conv.ToDecimal( "1A" ) );
} /// <summary>
///转换为高精度浮点数,有效值
///</summary>
[TestMethod]
public void TestToDecimal() {
Assert.AreEqual( 1.2M, Util.Conv.ToDecimal( "1.2" ) );
} /// <summary>
/// 转换为高精度浮点数,指定2位小数位数
///</summary>
[TestMethod()]
public void TestToDecimal_DigitsIs2() {
Assert.AreEqual( 12.24M, Util.Conv.ToDecimal( "12.235", ) );
} #endregion #region ToDecimalOrNull(转换为可空高精度浮点数) /// <summary>
///转换为可空高精度浮点数,值为null
///</summary>
[TestMethod]
public void TestToDecimalOrNull_Null() {
Assert.IsNull( Util.Conv.ToDecimalOrNull( null ) );
} /// <summary>
///转换为可空高精度浮点数,值为空字符串
///</summary>
[TestMethod]
public void TestToDecimalOrNull_Empty() {
Assert.IsNull( Util.Conv.ToDecimalOrNull( "" ) );
} /// <summary>
///转换为可空高精度浮点数,无效值
///</summary>
[TestMethod]
public void TestToDecimalOrNull_Invalid() {
Assert.IsNull( Util.Conv.ToDecimalOrNull( "1A" ) );
} /// <summary>
///转换为可空高精度浮点数,无效值,指定2位小数位数
///</summary>
[TestMethod]
public void TestToDecimalOrNull_Invalid_DigitsIs2() {
Assert.IsNull( Util.Conv.ToDecimalOrNull( "1A", ) );
} /// <summary>
///转换为可空高精度浮点数,值为0
///</summary>
[TestMethod]
public void TestToDecimalOrNull_0() {
Assert.AreEqual( , Util.Conv.ToDecimalOrNull( "" ) );
} /// <summary>
///转换为可空高精度浮点数,有效值
///</summary>
[TestMethod]
public void TestToDecimalOrNull() {
Assert.AreEqual( 1.2M, Util.Conv.ToDecimalOrNull( "1.2" ) );
} /// <summary>
/// 转换为可空高精度浮点数,指定2位小数位数
///</summary>
[TestMethod()]
public void ToDecimalOrNull_DigitsIs2() {
Assert.AreEqual( 12.24M, Util.Conv.ToDecimalOrNull( "12.235", ) );
} #endregion #region ToGuid(转换为Guid) /// <summary>
///转换为Guid,值为null
///</summary>
[TestMethod]
public void TestToGuid_Null() {
Assert.AreEqual( Guid.Empty, Util.Conv.ToGuid( null ) );
} /// <summary>
///转换为Guid,值为空字符串
///</summary>
[TestMethod]
public void TestToGuid_Empty() {
Assert.AreEqual( Guid.Empty, Util.Conv.ToGuid( "" ) );
} /// <summary>
///转换为Guid,无效值
///</summary>
[TestMethod]
public void TestToGuid_Invalid() {
Assert.AreEqual( Guid.Empty, Util.Conv.ToGuid( "1A" ) );
} /// <summary>
///转换为Guid,有效值
///</summary>
[TestMethod]
public void TestToGuid() {
Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Conv.ToGuid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) );
} #endregion #region ToGuidOrNull(转换为可空Guid) /// <summary>
///转换为可空Guid,值为null
///</summary>
[TestMethod]
public void TestToGuidOrNull_Null() {
Assert.IsNull( Util.Conv.ToGuidOrNull( null ) );
} /// <summary>
///转换为可空Guid,值为空字符串
///</summary>
[TestMethod]
public void TestToGuidOrNull_Empty() {
Assert.IsNull( Util.Conv.ToGuidOrNull( "" ) );
} /// <summary>
///转换为可空Guid,无效值
///</summary>
[TestMethod]
public void TestToGuidOrNull_Invalid() {
Assert.IsNull( Util.Conv.ToGuidOrNull( "1A" ) );
} /// <summary>
///转换为可空Guid,有效值
///</summary>
[TestMethod]
public void TestToGuidOrNull() {
Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Conv.ToGuidOrNull( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) );
} #endregion #region ToGuidList(转换为Guid集合) /// <summary>
/// 转换为Guid集合,验证空字符串
/// </summary>
[TestMethod]
public void TestToGuidList_Empty() {
Assert.AreEqual( , Util.Conv.ToGuidList( "" ).Count );
} /// <summary>
/// 转换为Guid集合,验证最后为逗号
/// </summary>
[TestMethod]
public void TestToGuidList_LastIsComma() {
Assert.AreEqual( , Util.Conv.ToGuidList( "83B0233C-A24F-49FD-8083-1337209EBC9A," ).Count );
} /// <summary>
/// 转换为Guid集合,验证中间包含逗号
/// </summary>
[TestMethod]
public void TestToGuidList_MiddleIsComma() {
const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,";
Assert.AreEqual( , Util.Conv.ToGuidList( guid ).Count );
Assert.AreEqual( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Util.Conv.ToGuidList( guid )[] );
Assert.AreEqual( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Util.Conv.ToGuidList( guid )[] );
} /// <summary>
/// 转换为Guid集合,仅1个Guid
/// </summary>
[TestMethod]
public void TestToGuidList_1Guid() {
const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A";
Assert.AreEqual( , Util.Conv.ToGuidList( guid ).Count );
Assert.AreEqual( new Guid( guid ), Util.Conv.ToGuidList( guid )[] );
} /// <summary>
/// 转换为Guid集合,2个Guid
/// </summary>
[TestMethod]
public void TestToGuidList_2Guid() {
const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A";
Assert.AreEqual( , Util.Conv.ToGuidList( guid ).Count );
Assert.AreEqual( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Util.Conv.ToGuidList( guid )[] );
Assert.AreEqual( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Util.Conv.ToGuidList( guid )[] );
} #endregion #region ToDate(转换为日期) /// <summary>
///转换为日期,值为null
///</summary>
[TestMethod]
public void TestToDate_Null() {
Assert.AreEqual( DateTime.MinValue, Util.Conv.ToDate( null ) );
} /// <summary>
///转换为日期,值为空字符串
///</summary>
[TestMethod]
public void TestToDate_Empty() {
Assert.AreEqual( DateTime.MinValue, Util.Conv.ToDate( "" ) );
} /// <summary>
///转换为日期,无效值
///</summary>
[TestMethod]
public void TestToDate_Invalid() {
Assert.AreEqual( DateTime.MinValue, Util.Conv.ToDate( "1A" ) );
} /// <summary>
///转换为日期,有效值
///</summary>
[TestMethod]
public void TestToDate() {
Assert.AreEqual( new DateTime( , , ), Util.Conv.ToDate( "2000-1-1" ) );
} #endregion #region ToDateOrNull(转换为可空日期) /// <summary>
///转换为可空日期,值为null
///</summary>
[TestMethod]
public void TestToDateOrNull_Null() {
Assert.IsNull( Util.Conv.ToDateOrNull( null ) );
} /// <summary>
///转换为可空日期,值为空字符串
///</summary>
[TestMethod]
public void TestToDateOrNull_Empty() {
Assert.IsNull( Util.Conv.ToDateOrNull( "" ) );
} /// <summary>
///转换为可空日期,无效值
///</summary>
[TestMethod]
public void TestToDateOrNull_Invalid() {
Assert.IsNull( Util.Conv.ToDateOrNull( "1A" ) );
} /// <summary>
///转换为可空日期,有效值
///</summary>
[TestMethod]
public void TestToDateOrNull() {
Assert.AreEqual( new DateTime( , , ), Util.Conv.ToDateOrNull( "2000-1-1" ) );
} #endregion #region ToBool(转换为布尔值) /// <summary>
///转换为布尔值,值为null
///</summary>
[TestMethod]
public void TestToBool_Null() {
Assert.AreEqual( false, Util.Conv.ToBool( null ) );
} /// <summary>
///转换为布尔值,值为空字符串
///</summary>
[TestMethod]
public void TestToBool_Empty() {
Assert.AreEqual( false, Util.Conv.ToBool( "" ) );
} /// <summary>
///转换为布尔值,无效值
///</summary>
[TestMethod]
public void TestToBool_Invalid() {
Assert.AreEqual( false, Util.Conv.ToBool( "1A" ) );
} /// <summary>
///转换为布尔值,值为False
///</summary>
[TestMethod]
public void TestToBool_False() {
Assert.AreEqual( false, Util.Conv.ToBool( "" ) );
Assert.AreEqual( false, Util.Conv.ToBool( "否" ) );
Assert.AreEqual( false, Util.Conv.ToBool( "no" ) );
Assert.AreEqual( false, Util.Conv.ToBool( "No" ) );
Assert.AreEqual( false, Util.Conv.ToBool( "false" ) );
Assert.AreEqual( false, Util.Conv.ToBool( "False" ) );
} /// <summary>
///转换为布尔值,值为True
///</summary>
[TestMethod]
public void TestToBool_True() {
Assert.AreEqual( true, Util.Conv.ToBool( "" ) );
Assert.AreEqual( true, Util.Conv.ToBool( "是" ) );
Assert.AreEqual( true, Util.Conv.ToBool( "yes" ) );
Assert.AreEqual( true, Util.Conv.ToBool( "Yes" ) );
Assert.AreEqual( true, Util.Conv.ToBool( "true" ) );
Assert.AreEqual( true, Util.Conv.ToBool( "True" ) );
} #endregion #region ToBoolOrNull(转换为可空布尔值) /// <summary>
///转换为可空布尔值,值为null
///</summary>
[TestMethod]
public void TestToBoolOrNull_Null() {
Assert.IsNull( Util.Conv.ToBoolOrNull( null ) );
} /// <summary>
///转换为可空布尔值,值为空字符串
///</summary>
[TestMethod]
public void TestToBoolOrNull_Empty() {
Assert.IsNull( Util.Conv.ToBoolOrNull( "" ) );
} /// <summary>
///转换为可空布尔值,无效值
///</summary>
[TestMethod]
public void TestToBoolOrNull_Invalid() {
Assert.IsNull( Util.Conv.ToBoolOrNull( "1A" ) );
} /// <summary>
///转换为布尔值,值为False
///</summary>
[TestMethod]
public void TestToBoolOrNull_False() {
Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "" ) );
Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "否" ) );
Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "no" ) );
Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "No" ) );
Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "false" ) );
Assert.AreEqual( false, Util.Conv.ToBoolOrNull( "False" ) );
} /// <summary>
///转换为布尔值,值为True
///</summary>
[TestMethod]
public void TestToBoolOrNull_True() {
Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "" ) );
Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "是" ) );
Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "yes" ) );
Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "Yes" ) );
Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "true" ) );
Assert.AreEqual( true, Util.Conv.ToBoolOrNull( "True" ) );
} #endregion #region ToString(转换为字符串) /// <summary>
///转换为字符串,值为null
///</summary>
[TestMethod]
public void TestToString_Null() {
Assert.AreEqual( string.Empty, Util.Conv.ToString( null ) );
} /// <summary>
///转换为字符串,值为空字符串
///</summary>
[TestMethod]
public void TestToString_Empty() {
Assert.AreEqual( string.Empty, Util.Conv.ToString( " " ) );
} /// <summary>
///转换为字符串,有效值
///</summary>
[TestMethod]
public void TestToString() {
Assert.AreEqual( "", Util.Conv.ToString( ) );
} #endregion #region To(通用泛型转换) #region 目标为int /// <summary>
///通用泛型转换,目标为整数,值为null
///</summary>
[TestMethod]
public void TestTo_Int_Null() {
Assert.AreEqual( , Conv.To<int>( null ) );
} /// <summary>
///通用泛型转换,目标为整数,值为空字符串
///</summary>
[TestMethod]
public void TestTo_Int_Empty() {
Assert.AreEqual( , Conv.To<int>( "" ) );
} /// <summary>
///通用泛型转换,目标为整数,无效值
///</summary>
[TestMethod]
public void TestTo_Int_Invalid() {
Assert.AreEqual( , Conv.To<int>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为整数,有效值
///</summary>
[TestMethod]
public void TestTo_Int() {
Assert.AreEqual( , Conv.To<int>( "" ) );
} /// <summary>
///通用泛型转换,目标为可空整数,无效值
///</summary>
[TestMethod]
public void TestTo_IntOrNull_Invalid() {
Assert.IsNull( Conv.To<int?>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为可空整数,有效值
///</summary>
[TestMethod]
public void TestTo_IntOrNull() {
Assert.AreEqual( , Conv.To<int?>( "" ) );
} #endregion #region 目标为Guid /// <summary>
///通用泛型转换,目标为Guid,无效值
///</summary>
[TestMethod]
public void TestTo_Guid_Invalid() {
Assert.AreEqual( Guid.Empty, Conv.To<Guid>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为Guid,有效值
///</summary>
[TestMethod]
public void TestTo_Guid() {
Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ),
Conv.To<Guid>( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) );
} /// <summary>
///通用泛型转换,目标为可空Guid,无效值
///</summary>
[TestMethod]
public void TestTo_GuidOrNull_Invalid() {
Assert.IsNull( Conv.To<Guid?>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为可空Guid,有效值
///</summary>
[TestMethod]
public void TestTo_GuidOrNull() {
Assert.AreEqual( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ),
Conv.To<Guid?>( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) );
} #endregion #region 目标为string /// <summary>
///通用泛型转换,目标为string,有效值
///</summary>
[TestMethod]
public void TestTo_String() {
Assert.AreEqual( "", Conv.To<string>( ) );
} #endregion #region 目标为double /// <summary>
///通用泛型转换,目标为double,无效值
///</summary>
[TestMethod]
public void TestTo_Double_Invalid() {
Assert.AreEqual( , Conv.To<double>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为double,有效值
///</summary>
[TestMethod]
public void TestTo_Double() {
Assert.AreEqual( 12.5, Conv.To<double>( "12.5" ) );
} /// <summary>
///通用泛型转换,目标为可空double,无效值
///</summary>
[TestMethod]
public void TestTo_DoubleOrNull_Invalid() {
Assert.IsNull( Conv.To<double?>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为可空double,有效值
///</summary>
[TestMethod]
public void TestTo_DoubleOrNull() {
Assert.AreEqual( 12.5, Conv.To<double?>( "12.5" ) );
} #endregion #region 目标为decimal /// <summary>
///通用泛型转换,目标为decimal,无效值
///</summary>
[TestMethod]
public void TestTo_Decimal_Invalid() {
Assert.AreEqual( , Conv.To<decimal>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为decimal,有效值
///</summary>
[TestMethod]
public void TestTo_Decimal() {
Assert.AreEqual( 12.5M, Conv.To<decimal>( "12.5" ) );
} /// <summary>
///通用泛型转换,目标为可空decimal,无效值
///</summary>
[TestMethod]
public void TestTo_DecimalOrNull_Invalid() {
Assert.IsNull( Conv.To<decimal?>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为可空decimal,有效值
///</summary>
[TestMethod]
public void TestTo_DecimalOrNull() {
Assert.AreEqual( 12.5M, Conv.To<decimal?>( "12.5" ) );
} #endregion #region 目标为bool /// <summary>
///通用泛型转换,目标为bool,无效值
///</summary>
[TestMethod]
public void TestTo_Bool_Invalid() {
Assert.AreEqual( false, Conv.To<bool>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为bool,有效值
///</summary>
[TestMethod]
public void TestTo_Bool() {
Assert.AreEqual( true, Conv.To<bool>( ) );
} /// <summary>
///通用泛型转换,目标为可空bool,无效值
///</summary>
[TestMethod]
public void TestTo_BoolOrNull_Invalid() {
Assert.IsNull( Conv.To<bool?>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为可空bool,有效值
///</summary>
[TestMethod]
public void TestTo_BoolOrNull() {
Assert.AreEqual( true, Conv.To<bool?>( "true" ) );
} #endregion #region 目标为DateTime /// <summary>
///通用泛型转换,目标为DateTime,无效值
///</summary>
[TestMethod]
public void TestTo_DateTime_Invalid() {
Assert.AreEqual( DateTime.MinValue, Conv.To<DateTime>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为DateTime,有效值
///</summary>
[TestMethod]
public void TestTo_DateTime() {
Assert.AreEqual( new DateTime( , , ), Conv.To<DateTime>( "2000-1-1" ) );
} /// <summary>
///通用泛型转换,目标为可空DateTime,无效值
///</summary>
[TestMethod]
public void TestTo_DateTimeOrNull_Invalid() {
Assert.IsNull( Conv.To<DateTime?>( "1A" ) );
} /// <summary>
///通用泛型转换,目标为可空DateTime,有效值
///</summary>
[TestMethod]
public void TestTo_DateTimeOrNull() {
Assert.AreEqual( new DateTime( , , ), Conv.To<DateTime?>( "2000-1-1" ) );
} #endregion #endregion
}
}
ConvTest
Conv类代码如下。
using System;
using System.Collections.Generic;
using System.Linq; namespace Util {
/// <summary>
/// 类型转换
/// </summary>
public static class Conv { #region 数值转换 /// <summary>
/// 转换为整型
/// </summary>
/// <param name="data">数据</param>
public static int ToInt( object data ) {
if ( data == null )
return ;
int result;
var success = int.TryParse( data.ToString(), out result );
if ( success == true )
return result;
try {
return Convert.ToInt32( ToDouble( data, ) );
}
catch ( Exception ) {
return ;
}
} /// <summary>
/// 转换为可空整型
/// </summary>
/// <param name="data">数据</param>
public static int? ToIntOrNull( object data ) {
if ( data == null )
return null;
int result;
bool isValid = int.TryParse( data.ToString(), out result );
if ( isValid )
return result;
return null;
} /// <summary>
/// 转换为双精度浮点数
/// </summary>
/// <param name="data">数据</param>
public static double ToDouble( object data ) {
if ( data == null )
return ;
double result;
return double.TryParse( data.ToString(), out result ) ? result : ;
} /// <summary>
/// 转换为双精度浮点数,并按指定的小数位4舍5入
/// </summary>
/// <param name="data">数据</param>
/// <param name="digits">小数位数</param>
public static double ToDouble( object data, int digits ) {
return Math.Round( ToDouble( data ), digits );
} /// <summary>
/// 转换为可空双精度浮点数
/// </summary>
/// <param name="data">数据</param>
public static double? ToDoubleOrNull( object data ) {
if ( data == null )
return null;
double result;
bool isValid = double.TryParse( data.ToString(), out result );
if ( isValid )
return result;
return null;
} /// <summary>
/// 转换为高精度浮点数
/// </summary>
/// <param name="data">数据</param>
public static decimal ToDecimal( object data ) {
if ( data == null )
return ;
decimal result;
return decimal.TryParse( data.ToString(), out result ) ? result : ;
} /// <summary>
/// 转换为高精度浮点数,并按指定的小数位4舍5入
/// </summary>
/// <param name="data">数据</param>
/// <param name="digits">小数位数</param>
public static decimal ToDecimal( object data, int digits ) {
return Math.Round( ToDecimal( data ), digits );
} /// <summary>
/// 转换为可空高精度浮点数
/// </summary>
/// <param name="data">数据</param>
public static decimal? ToDecimalOrNull( object data ) {
if ( data == null )
return null;
decimal result;
bool isValid = decimal.TryParse( data.ToString(), out result );
if ( isValid )
return result;
return null;
} /// <summary>
/// 转换为可空高精度浮点数,并按指定的小数位4舍5入
/// </summary>
/// <param name="data">数据</param>
/// <param name="digits">小数位数</param>
public static decimal? ToDecimalOrNull( object data, int digits ) {
var result = ToDecimalOrNull( data );
if ( result == null )
return null;
return Math.Round( result.Value, digits );
} #endregion #region Guid转换 /// <summary>
/// 转换为Guid
/// </summary>
/// <param name="data">数据</param>
public static Guid ToGuid( object data ) {
if ( data == null )
return Guid.Empty;
Guid result;
return Guid.TryParse( data.ToString(), out result ) ? result : Guid.Empty;
} /// <summary>
/// 转换为可空Guid
/// </summary>
/// <param name="data">数据</param>
public static Guid? ToGuidOrNull( object data ) {
if ( data == null )
return null;
Guid result;
bool isValid = Guid.TryParse( data.ToString(), out result );
if ( isValid )
return result;
return null;
} /// <summary>
/// 转换为Guid集合
/// </summary>
/// <param name="guid">guid集合字符串,范例:83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A</param>
public static List<Guid> ToGuidList( string guid ) {
var listGuid = new List<Guid>();
if ( string.IsNullOrWhiteSpace( guid ) )
return listGuid;
var arrayGuid = guid.Split( ',' );
listGuid.AddRange( from each in arrayGuid where !string.IsNullOrWhiteSpace( each ) select new Guid( each ) );
return listGuid;
} #endregion #region 日期转换 /// <summary>
/// 转换为日期
/// </summary>
/// <param name="data">数据</param>
public static DateTime ToDate( object data ) {
if ( data == null )
return DateTime.MinValue;
DateTime result;
return DateTime.TryParse( data.ToString(), out result ) ? result : DateTime.MinValue;
} /// <summary>
/// 转换为可空日期
/// </summary>
/// <param name="data">数据</param>
public static DateTime? ToDateOrNull( object data ) {
if ( data == null )
return null;
DateTime result;
bool isValid = DateTime.TryParse( data.ToString(), out result );
if ( isValid )
return result;
return null;
} #endregion #region 布尔转换 /// <summary>
/// 转换为布尔值
/// </summary>
/// <param name="data">数据</param>
public static bool ToBool( object data ) {
if ( data == null )
return false;
bool? value = GetBool( data );
if ( value != null )
return value.Value;
bool result;
return bool.TryParse( data.ToString(), out result ) && result;
} /// <summary>
/// 获取布尔值
/// </summary>
private static bool? GetBool( object data ) {
switch ( data.ToString().Trim().ToLower() ) {
case "":
return false;
case "":
return true;
case "是":
return true;
case "否":
return false;
case "yes":
return true;
case "no":
return false;
default:
return null;
}
} /// <summary>
/// 转换为可空布尔值
/// </summary>
/// <param name="data">数据</param>
public static bool? ToBoolOrNull( object data ) {
if ( data == null )
return null;
bool? value = GetBool( data );
if ( value != null )
return value.Value;
bool result;
bool isValid = bool.TryParse( data.ToString(), out result );
if ( isValid )
return result;
return null;
} #endregion #region 字符串转换 /// <summary>
/// 转换为字符串
/// </summary>
/// <param name="data">数据</param>
public static string ToString( object data ) {
return data == null ? string.Empty : data.ToString().Trim();
} #endregion #region 通用转换 /// <summary>
/// 泛型转换
/// </summary>
/// <typeparam name="T">目标类型</typeparam>
/// <param name="data">数据</param>
public static T To<T>( object data ) {
if ( data == null || string.IsNullOrWhiteSpace( data.ToString() ) )
return default( T );
Type type = Nullable.GetUnderlyingType( typeof( T ) ) ?? typeof( T );
try {
if ( type.Name.ToLower() == "guid" )
return (T)(object)new Guid( data.ToString() );
if ( data is IConvertible )
return (T)Convert.ChangeType( data, type );
return (T)data;
}
catch {
return default( T );
}
} #endregion
}
}
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
Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)的更多相关文章
- Util应用程序框架公共操作类(三):数据类型转换公共操作类(扩展篇)
上一篇以TDD方式介绍了数据类型转换公共操作类的开发,并提供了单元测试和实现代码,本文将演示通过扩展方法来增强公共操作类,以便调用时更加简化. 下面以字符串转换为List<Guid>为例进 ...
- 深入浅出Mybatis系列(二)---配置简介(mybatis源码篇)
上篇文章<深入浅出Mybatis系列(一)---Mybatis入门>, 写了一个Demo简单体现了一下Mybatis的流程.本次,将简单介绍一下Mybatis的配置文件: 上次例子中,我们 ...
- 深入浅出Mybatis系列(二)---配置简介(mybatis源码篇)[转]
上篇文章<深入浅出Mybatis系列(一)---Mybatis入门>, 写了一个Demo简单体现了一下Mybatis的流程.本次,将简单介绍一下Mybatis的配置文件: 上次例子中,我们 ...
- Util应用程序框架公共操作类
随笔分类 - Util应用程序框架公共操作类 Util应用程序框架公共操作类 Util应用程序框架公共操作类(五):异常公共操作类 摘要: 任何系统都需要处理错误,本文介绍的异常公共操作类,用于对业务 ...
- Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)
今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> ...
- Util应用程序框架公共操作类(七):Lambda表达式公共操作类
前一篇扩展了两个常用验证方法,本文将封装两个Lambda表达式操作,用来为下一篇的查询扩展服务. Lambda表达式是一种简洁的匿名函数语法,可以用它将方法作为委托参数传递.在Linq中,大量使用La ...
- Util应用程序框架公共操作类(四):验证公共操作类
为了能够验证领域实体,需要一个验证公共操作类来提供支持.由于我将使用企业库(Enterprise Library)的验证组件来完成这项任务,所以本文也将演示对第三方框架的封装要点. .Net提供了一个 ...
- Util应用程序框架公共操作类(一):数据类型转换公共操作类(介绍篇)
本系列文章将介绍一些对初学者有帮助的辅助类,这些辅助类本身并没有什么稀奇之处,如何能发现需要封装它们可能更加重要,所谓授之以鱼不如授之以渔,掌握封装公共操作类的技巧才是关键,我会详细说明创建这些类的动 ...
- 应用程序框架实战十五:DDD分层架构之领域实体(验证篇)
在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...
随机推荐
- Android自定义折线图
老师布置了个作业:http://www.cnblogs.com/qingxu/p/5316897.html 作业中提到的 “玩了几天以后,大家发现了一些很有意思的现象,比如黄金点在逐渐地往下移动.” ...
- js闭包-在你身边却不知
今天组里小伙很纳闷的问了我js绑事件带出的一个小问题,随便聊聊闭包那点事,背景如下: 当点击Button的时候给li绑定事件,事件的大概内容是获取li位置的index再做点事,据他描述代码看上去也没错 ...
- 浅谈MySql的存储引擎(表类型)
来源:http://www.cnblogs.com/lina1006/archive/2011/04/29/2032894.html 什么是MySql数据库 通常意义上,数据库也就是数据的集合,具体到 ...
- jsp 中 有没有类似java if else语句
<c:when test=""></c:when> <c:otherwise></c:otherwise> 有if else的功能 ...
- Codeforces Round #361 (Div. 2) B
B - Mike and Shortcuts Description Recently, Mike was very busy with studying for exams and contests ...
- 我的window10
前言 这个一时半会写不完,也比较耗费时间,留着以后,每周更新一些新的技巧. 折腾了3天多时间的成果——>window10 的全新桌面,不比苹果差!不要说 windows 不能用 mac . 既然 ...
- JVM垃圾收集器
JVM中垃圾的回收由垃圾收集器进行,随着JDK的不断升级,垃圾收集器也开发出了各种版本,垃圾收集器不断优化的动力,就是为了实现更短的停顿. 下面是7种不同的分代收集器,如果两个收集器之间有连线,则表示 ...
- git 常用操作命令
A. 新建Git仓库,创建新文件夹git init B. 添加文件到git索引git add <filename> --- 单个文件添加git add * --- 全部文件添加 C. 提 ...
- sass/scss 和 less的区别
一. Sass/Scss.Less是什么? Sass (Syntactically Awesome Stylesheets)是一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量 ...
- 为什么commonjs不适合于浏览器端
有了服务器端模块以后,很自然地,大家就想要客户端模块.而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行. 但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境.还是上 ...