Util应用程序框架公共操作类(三):数据类型转换公共操作类(扩展篇)

  上一篇以TDD方式介绍了数据类型转换公共操作类的开发,并提供了单元测试和实现代码,本文将演示通过扩展方法来增强公共操作类,以便调用时更加简化。

  下面以字符串转换为List<Guid>为例进行讨论。

string input = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A";
var result = Util.Conv.ToGuidList( input );

  观察上面的代码,它确实已经被封装起来了,通过一个明确的API进行调用。不过它是最简化形式吗?

  在.Net 4.0提供了一个扩展方法的语法,这是一个非常强大的功能,它通过静态方法的方式向目标类型添加一个模拟的实例方法。

  上面例子就被简化成如下形式。

string input = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A";
var result = input.ToGuidList();

  这样在调用的时候就会更爽,从而帮助我们在项目开发过程中进一步减轻负担。

  下面在Util.Tests单元测试项目中添加一个Extensions文件夹,并在Extensions文件夹中添加ConvertExtensionTest.cs的文件,类名为ConvertExtensionTest,用来测试类型转换扩展。

  ConvertExtensionTest代码如下。

  1 using System;
  2 using System.Collections.Generic;
  3 using Microsoft.VisualStudio.TestTools.UnitTesting;
  4
  5 namespace Util.Tests.Extensions {
  6     /// <summary>
  7     /// 类型转换扩展测试
  8     /// </summary>
  9     [TestClass]
 10     public class ConvertExtensionTest {
 11         /// <summary>
 12         /// 转换为整数
 13         /// </summary>
 14         [TestMethod]
 15         public void TestToInt() {
 16             string obj1 = "";
 17             string obj2 = "1";
 18             Assert.AreEqual( 0, obj1.ToInt() );
 19             Assert.AreEqual( 1, obj2.ToInt() );
 20         }
 21
 22         /// <summary>
 23         /// 转换为可空整数
 24         /// </summary>
 25         [TestMethod]
 26         public void TestToIntOrNull() {
 27             string obj1 = "";
 28             string obj2 = "1";
 29             Assert.IsNull( obj1.ToIntOrNull() );
 30             Assert.AreEqual( 1, obj2.ToIntOrNull() );
 31         }
 32
 33         /// <summary>
 34         /// 转换为双精度浮点数
 35         /// </summary>
 36         [TestMethod]
 37         public void TestToDouble() {
 38             string obj1 = "";
 39             string obj2 = "1.2";
 40             Assert.AreEqual( 0, obj1.ToDouble() );
 41             Assert.AreEqual( 1.2, obj2.ToDouble() );
 42         }
 43
 44         /// <summary>
 45         /// 转换为可空双精度浮点数
 46         /// </summary>
 47         [TestMethod]
 48         public void TestToDoubleOrNull() {
 49             string obj1 = "";
 50             string obj2 = "1.2";
 51             Assert.IsNull( obj1.ToDoubleOrNull() );
 52             Assert.AreEqual( 1.2, obj2.ToDoubleOrNull() );
 53         }
 54
 55         /// <summary>
 56         /// 转换为高精度浮点数
 57         /// </summary>
 58         [TestMethod]
 59         public void TestToDecimal() {
 60             string obj1 = "";
 61             string obj2 = "1.2";
 62             Assert.AreEqual( 0, obj1.ToDecimal() );
 63             Assert.AreEqual( 1.2M, obj2.ToDecimal() );
 64         }
 65
 66         /// <summary>
 67         /// 转换为可空高精度浮点数
 68         /// </summary>
 69         [TestMethod]
 70         public void TestToDecimalOrNull() {
 71             string obj1 = "";
 72             string obj2 = "1.2";
 73             Assert.IsNull( obj1.ToDecimalOrNull() );
 74             Assert.AreEqual( 1.2M, obj2.ToDecimalOrNull() );
 75         }
 76
 77         /// <summary>
 78         /// 转换为日期
 79         /// </summary>
 80         [TestMethod]
 81         public void TestToDate() {
 82             string obj1 = "";
 83             string obj2 = "2000-1-1";
 84             Assert.AreEqual( DateTime.MinValue, obj1.ToDate() );
 85             Assert.AreEqual( new DateTime( 2000, 1, 1 ), obj2.ToDate() );
 86         }
 87
 88         /// <summary>
 89         /// 转换为可空日期
 90         /// </summary>
 91         [TestMethod]
 92         public void TestToDateOrNull() {
 93             string obj1 = "";
 94             string obj2 = "2000-1-1";
 95             Assert.IsNull( obj1.ToDateOrNull() );
 96             Assert.AreEqual( new DateTime( 2000, 1, 1 ), obj2.ToDateOrNull() );
 97         }
 98
 99         /// <summary>
100         /// 转换为Guid
101         /// </summary>
102         [TestMethod]
103         public void TestToGuid() {
104             string obj1 = "";
105             string obj2 = "B9EB56E9-B720-40B4-9425-00483D311DDC";
106             Assert.AreEqual( Guid.Empty, obj1.ToGuid() );
107             Assert.AreEqual( new Guid( obj2 ), obj2.ToGuid() );
108         }
109
110         /// <summary>
111         /// 转换为可空Guid
112         /// </summary>
113         [TestMethod]
114         public void TestToGuidOrNull() {
115             string obj1 = "";
116             string obj2 = "B9EB56E9-B720-40B4-9425-00483D311DDC";
117             Assert.IsNull( obj1.ToGuidOrNull() );
118             Assert.AreEqual( new Guid( obj2 ), obj2.ToGuidOrNull() );
119         }
120
121         /// <summary>
122         /// 转换为Guid集合,值为字符串
123         /// </summary>
124         [TestMethod]
125         public void TestToGuidList_String() {
126             const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,";
127             Assert.AreEqual( 2, guid.ToGuidList().Count );
128             Assert.AreEqual( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), guid.ToGuidList()[0] );
129             Assert.AreEqual( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), guid.ToGuidList()[1] );
130         }
131
132         /// <summary>
133         /// 转换为Guid集合,值为字符串集合
134         /// </summary>
135         [TestMethod]
136         public void TestToGuidList_StringList() {
137             var list = new List<string> {"83B0233C-A24F-49FD-8083-1337209EBC9A", "EAB523C6-2FE7-47BE-89D5-C6D440C3033A"};
138             Assert.AreEqual( 2, list.ToGuidList().Count );
139             Assert.AreEqual( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), list.ToGuidList()[0] );
140             Assert.AreEqual( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), list.ToGuidList()[1] );
141         }
142
143         /// <summary>
144         /// 转换为字符串
145         /// </summary>
146         [TestMethod]
147         public void TestToStr() {
148             object value = null;
149             Assert.AreEqual( string.Empty, value.ToStr() );
150             value = 1;
151             Assert.AreEqual( "1", value.ToStr() );
152         }
153     }
154 }

  在Util类库项目中,添加Extensions.Convert.cs文件,类名为Extensions,它是一个静态类,这是扩展方法的强制要求,并且它还是一个部分类,这是因为以后需要进行扩展时,就可以在Extensions类中继续扩展其它功能。

  Extensions.Convert.cs中的代码如下。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Util {
    /// <summary>
    /// 类型转换扩展
    /// </summary>
    public static partial class Extensions {
        /// <summary>
        /// 转换为int
        /// </summary>
        /// <param name="data">数据</param>
        public static int ToInt( this string data ) {
            return Conv.ToInt( data );
        }

        /// <summary>
        /// 转换为可空int
        /// </summary>
        /// <param name="data">数据</param>
        public static int? ToIntOrNull( this string data ) {
            return Conv.ToIntOrNull( data );
        }

        /// <summary>
        /// 转换为double
        /// </summary>
        /// <param name="data">数据</param>
        public static double ToDouble( this string data ) {
            return Conv.ToDouble( data );
        }

        /// <summary>
        /// 转换为可空double
        /// </summary>
        /// <param name="data">数据</param>
        public static double? ToDoubleOrNull( this string data ) {
            return Conv.ToDoubleOrNull( data );
        }

        /// <summary>
        /// 转换为decimal
        /// </summary>
        /// <param name="data">数据</param>
        public static decimal ToDecimal( this string data ) {
            return Conv.ToDecimal( data );
        }

        /// <summary>
        /// 转换为可空decimal
        /// </summary>
        /// <param name="data">数据</param>
        public static decimal? ToDecimalOrNull( this string data ) {
            return Conv.ToDecimalOrNull( data );
        }

        /// <summary>
        /// 转换为日期
        /// </summary>
        /// <param name="data">数据</param>
        public static DateTime ToDate( this string data ) {
            return Conv.ToDate( data );
        }

        /// <summary>
        /// 转换为可空日期
        /// </summary>
        /// <param name="data">数据</param>
        public static DateTime? ToDateOrNull( this string data ) {
            return Conv.ToDateOrNull( data );
        }

        /// <summary>
        /// 转换为Guid
        /// </summary>
        /// <param name="data">数据</param>
        public static Guid ToGuid( this string data ) {
            return Conv.ToGuid( data );
        }

        /// <summary>
        /// 转换为可空Guid
        /// </summary>
        /// <param name="data">数据</param>
        public static Guid? ToGuidOrNull( this string data ) {
            return Conv.ToGuidOrNull( data );
        }

        /// <summary>
        /// 转换为Guid集合
        /// </summary>
        /// <param name="data">数据,范例: "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A"</param>
        public static List<Guid> ToGuidList( this string data ) {
            return Conv.ToGuidList( data );
        }

        /// <summary>
        /// 转换为Guid集合
        /// </summary>
        /// <param name="data">字符串集合</param>
        public static List<Guid> ToGuidList( this IList<string> data ) {
            if ( data == null )
                return new List<Guid>();
            return data.Select( t => t.ToGuid() ).ToList();
        }

        /// <summary>
        /// 获取字符串
        /// </summary>
        /// <param name="data">对象</param>
        public static string ToStr( this object data ) {
            return Conv.ToString( data );
        }
    }
}

  为了避免代码冗余,在数据类型扩展类中,会将调用委托给conv进行处理,而不是在扩展类中重新实现一次。

  扩展方法的一个弊端是可能污染原生的.Net环境,从而导致混乱,特别是在扩展object对象时要十分小心,因为会添加到每个对象中。从我提供的示例代码可以看到,我主要是在string对象上扩展,因为这样范围会小得多。

  对于是否会滥用扩展方法,我的建议是如果你的应用程序框架使用范围很小,那么不必理会其它人的看法,使劲扩展直至你自己都觉得负担重重,然后再进行减肥。但是,当框架使用范围比较大,应该尽量不污染系统原生类,因为很多程序员在代码提示中找到这些API后,可能在不了解的情况下胡乱调用导致BUG,且由于使用范围大,框架创建人不能及时纠正其它人的问题。

  .Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

  谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

  下载地址:http://files.cnblogs.com/xiadao521/Util.2014.11.13.1.rar

时间: 2024-08-10 13:41:55

Util应用程序框架公共操作类(三):数据类型转换公共操作类(扩展篇)的相关文章

Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)

今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> /// 测试值为枚举 /// </summary> [TestMethod] public void TestGetValue_Enum() { var test1 = new Test1(); test1.NullableEnumValue = LogType.Error; //属性为枚举,值

Util应用程序框架公共操作类(四):验证公共操作类

为了能够验证领域实体,需要一个验证公共操作类来提供支持.由于我将使用企业库(Enterprise Library)的验证组件来完成这项任务,所以本文也将演示对第三方框架的封装要点. .Net提供了一个称为DataAnnotations的验证技术,即在对象的属性上添加一些Attribute,比如[Required]用来验证必填项.这是非常强大的特性,通过附加元数据的方式来提供验证,甚至在Mvc框架中还能自动生成Js客户端验证,从而可以非常方便的实现客户端和服务端的双重验证. 但是遗憾的是,.Net

Util应用程序框架公共操作类(一):数据类型转换公共操作类(介绍篇)

本系列文章将介绍一些对初学者有帮助的辅助类,这些辅助类本身并没有什么稀奇之处,如何能发现需要封装它们可能更加重要,所谓授之以鱼不如授之以渔,掌握封装公共操作类的技巧才是关键,我会详细说明创建这些类的动机和思考过程,以帮助初学者发现和封装自己需要的东西.创建公共操作类的技巧,大家可以参考我的这篇文章——应用程序框架实战十二:公共操作类开发技巧(初学者必读). 封装公共操作类,不仅要把技术上困难的封装进来,还需要不断观察自己的代码,以找出哪些部分可以更加简化.本文将介绍一个容易被大家所忽视的东西——

Util应用程序框架公共操作类(六):验证扩展

前面介绍了仓储的基本操作,下面准备开始扩展查询,在扩展查询之前,首先要增加两个公共操作类,一个是经常要用到的验证方法,另一个是Lambda表达式的操作类. 很多时候,我们会判断一个对象是否为null,由于null是一个不能接受的值,它会导致“未将对象引用设置到对象的实例”的严重错误,所以当检测到null值时一般直接抛出ArgumentNullException异常. public void Test( string name ) { if( name == null ) throw new Ar

Util应用程序框架公共操作类(五):异常公共操作类

任何系统都需要处理错误,本文介绍的异常公共操作类,用于对业务上的错误进行简单支持. 对于刚刚接触.Net的新手,碰到错误的时候,一般喜欢通过返回bool值的方式指示是否执行成功. public bool 方法名() { //执行代码,成功返回true,否则返回false } 不过上面的方法有一个问题是,无法知道确切的错误原因,所以需要添加一个out参数来返回错误消息. public bool 方法名( out string errorMessage ) { //执行代码,成功返回true,否则返

Util应用程序框架公共操作类(十一):表达式生成器

本篇介绍的表达式生成器,用于动态创建表达式. 在Util项目Lambdas目录中,添加ExpressionBuilder,代码如下. using System; using System.Linq.Expressions; namespace Util.Lambdas { /// <summary> /// 表达式生成器 /// </summary> public class ExpressionBuilder<TEntity> { /// <summary>

Util应用程序框架公共操作类(十):可空值类型扩展

当你使用可空的值类型时,你会发现取值很不方便,比如Guid? obj,你要从obj中获取值,可以使用Value属性obj. Value,但obj可能为null,这时候就会抛出一个异常. 可空值类型提供了一个HasValue属性,它可以识别出obj是不是一个null值,每当你获取可空值都需要加上这个判断if(value.HasValue){ var value = obj.Value;}. 下面我们通过几个扩展方法,把判断封装起来. 在Util项目中添加Extensions.Nullable.cs

应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)

前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持. 什么是仓储 仓储表示聚合的集合. 仓储所表现出来的集合外观,仅仅是一种模拟,除了测试以外,没有理由使用内存中真正的集合来创建仓储. 不应该为所有实体建立仓储,只有聚合才拥有仓储. 仓储用来重建已持久化的聚合,而工厂用于新建聚合. 使用仓储的优点 直接使用Entity Framework的DbContext不是很好吗,为什么还要在DbContext的上方封装一层仓储呢,这

201506300917_《Javascript权威指南(第六版)——类和模块、定义类三步法、定义简单类的函数 》(P200-210)

一. 类和模块 1. 类的实现基于原型继承机制的. 二. 类和原型 三. 类和构造函数 1. 构造函数是用来初始化新创建的对象的. 2. 使用new,所以,构造函数只要初始化对象状态即可. 3. 构建构造函数即是定义类,所以首字母要大写. 4. 四. 构造函数和类的标识 五. constrctor属性 1. constructor属性的值是一个函数对象 例如: var F = function() {}; //这是一个函数对象: var p = F.prototype;  //原型对象 var