借助扩展方法简化参数合法性判断
有许多的方法需要对传入的参数进行合法性判断,比如下面这种
1 void AddItem(int rpIndex, Item rpItem)
2 {
3 if(rpIndex < 0)
4 throw new ArgumentOutOfRangeException("rpIndex");
5 if(rpItem == null)
6 throw new ArgumentNullException("rpItem");
7
8 //其它代码
9 }
不过我觉得这代码有点不美观(大量的if和throw new),也有点不方便(太繁杂了)。在我看来,在编程的过程中要让我们的眼睛舒服,毕竟我们每次打开项目就要花上几个小时看着几百几千行枯燥的代码。单色背景对我来说都忍受不了,何况是这几行代码呢
幸好我用的语言是C#,可以借助一些扩展方法来简化一下代码
思路
大致构思了下代码,就像这样
1 Validate.Begin()
2 .IsGreaterThan(rpIndex, 0, "rpIndex")
3 .IsNotNull(rpItem, "rpItem")
4 .Check();
这里就说说思路,先通过Validate静态类的Begin()方法开始验证,然后通过一些逻辑判断方法形成一条验证链,最后通过End()方法关闭验证链并判断所有验证结果,如果有不合法的参数,则抛出验证链上积累的所有要抛出的异常
在这验证过程中有两个关键类——Validate和Validation。前者是用来新建一条验证链;后者这是验证链的“链条”,在这链条上存放了在这验证过程中要抛出的异常
关键辅助类
Validate类非常简单,就一个Begin()方法,很简单地返回一个null值
1 public static class Validate
2 {
3 public static Validation Begin() { return null; }
4 }
而Validation类要复杂些,有一个存放异常的列表以及一个是否检查过的属性
1 public class Validation
2 {
3 static readonly object r_ThreadSyncLock = new object();
4
5 bool r_IsChecked;
6 List<Exception> r_Exceptions;
7 internal List<Exception> ExceptionsList
8 {
9 get
10 {
11 if (!r_IsChecked)
12 Check();
13 return r_Exceptions;
14 }
15 }
16 public IEnumerable<Exception> Exceptions { get { return ExceptionsList; } }
17
18 public Validation AddException(Exception ex)
19 {
20 lock (r_ThreadSyncLock)
21 {
22 if (r_Exceptions == null)
23 r_Exceptions = new List<Exception>();
24 r_Exceptions.Add(ex);
25 if (r_IsChecked)
26 {
27 GC.ReRegisterForFinalize(this); //允许GC对此实例调用析构函数
28 r_IsChecked = false;
29 }
30 }
31 return this;
32 }
33
34 ~Validation()
35 {
36 //如果出现异常却没有得到处理,这程序能正常吗?
37 if (!r_IsChecked && r_Exceptions != null && r_Exceptions.Count != 0)
38 this.Check("验证链没有关闭,而且出现了异常,没有得到处理");
39 }
40 public void Check()
41 {
42 r_IsChecked = true;
43 GC.SuppressFinalize(this); //阻止GC对此实例调用析构函数
44 }
45 }
有人觉得,既然是新建一条验证链,那Begin()方法里就应该new一个Validation实例,可为什么会返回null值呢?这个呢,我个人在编写代码的时候,都是能少用new就少用new。不可能每次进入方法就要无聊地新建一个实例吧,这样的话一段时间内就堆积了大量垃圾,GC不累才怪呢。如果出现了异常,再new一个Validation对象,这样不是很好吗?简单的说,整条链的结果是null就没问题(所有参数合法);有Validation对象就有问题(有参数不合法)
当然,有些人会忘记关闭验证链。如果所有参数合法的话,这没什么问题,但如果有参数不合法,程序却还继续运行,这能正常吗?所以要在Validation里增加一个析构函数。这样的话,有异常却没有关闭验证链,程序会不知不觉地突然中断了,那个粗心的程序员就不得不寻找哪个地方忘记关闭验证链了
扩展方法
有了这两个辅助类,下面就让扩展方法登场
1 public static class ValidationExtensions
2 {
3 public static Validation Check(this Validation rpValidation)
4 {
5 return rpValidation.Check(null);
6 }
7 public static Validation Check(this Validation rpValidation, string rpMessage)
8 {
9 if (rpValidation == null || rpValidation.Exceptions == null)
10 return rpValidation;
11 if (rpValidation.ExceptionsList.Count == 1)
12 throw new ValidationException(rpMessage, rpValidation.ExceptionsList[0]);
13 throw new ValidationException(rpMessage, new MutilException(rpValidation.ExceptionsList.ToArray()));
14 }
15 public static void End(this Validation rpValidation) { rpValidation.Check(null); }
16 public static void End(this Validation rpValidation, string rpMessage) { rpValidation.Check(rpMessage); }
17 //其它逻辑判断方法
18 }
在这里,关键方法是Check()。它的主要作用很简单,就是看看是否有Validation对象,没有就结束,有就看看里面是否有异常,有就抛出,没有就结束。这里的ValidationException和MutilException我就不用详细介绍,前者就直接从Exception继承,后者也直接从Exception继承,然后重写一下ToString()方法,把所有的异常信息列出来
逻辑判断方法
常用的逻辑判断通常都是数值大于、小于、大于等于、小于等于和在某个区间,以及对象不为null和没有Dispose
下面就节选一个判断大于的方法,其他的自己动动手,改改那个布尔判断和异常对象就行了(比较数字的有三个重载——int、long和double的)
public static Validation IsGreaterThan(this Validation rpValidation, int rpValue, int rpValueGreaterThan, string rpParamName)
{
if (rpValue <= rpValueGreaterThan)
{
string rMessage = string.Format("{0}\n参数类型:{1}\n参数值:{2}\n参数要求:> {3}",
rpParamName, typeof(int), rpValue.ToString(), rpValueGreaterThan.ToString());
return (rpValidation ?? new Validation()).AddException(new ArgumentOutOfRangeException(rMessage));
}
return rpValidation;
}
效果
最后
有了这个东西,代码打起来就方便多了,而且看起来美观XD
就是这样,谢谢