验证能有多优雅

 

背景

我们都知道ENTLIBVAB,也知道如果不在乎大量的XML损视力的话,VAB非常非常优雅,但是在不大的项目中,很多情况下我们依旧自己写着验证的代码

所以在这篇文章中,打算展示一下学习.NET一年半以来,写验证代码的各个阶段,并展示一种个人觉得比较优雅的验证代码的写法,如果大家有别的方案,也请提出来与大家分享哦

 

第一阶段--强写

所谓强写,自然就是强行地写了,从知道需要参数验证(很惭愧,学了.NET整整1个月才知道这事)开始,好长一段时间都在强写着验证的代码,也不记得什么时候开始觉得这么写不舒服,什么时候开始换成了别的方法,总之早期的代码中充斥着这样的片断

public   void  SomeMethod( string  s)
{
    
if  (s  ==   null )
    {
        
throw   new  ArgumentNullException( " s " );
    }
    
if  (s.Length  ==   0 )
    {
        
throw   new  ArgumentException( " s cannot be empty  " );
    }
}

最终的结果是,一个.cs文件中有整整1/2的内容是这些可爱的if, throw和花括号

这个方案不用评价了,我自己会把他贬得很惨,再怎么说曾经也是为了这些东西写到手抽筋啊,痛苦啊

 

 

第二阶段--Guard类

 

嗯,MS很多项目中都有这个Guard类,把一些主要的验证的方法写在了类中,方法直接抛出异常,大致是这样的

public   class  Guard
{
    
public   void  NotNull( object  value,  string  argName)
    {
        
if  (value  ==   null )
        {
            
throw   new  ArgumentNullException(argName);
        }
    }

    
// 其他验证方法
}

 

调用的方法大致是这样的

public   void  SomeMethod( string  s)
{
    Guard.NotNull(s, 
" s " );
    Guard.NotEmpty(s, 
" s " );
    Guard.ShorterThan(s, 
10 " s " );
    Guard.LongerThan(s, 
3 " s " );
    
// 其他逻辑
}

看起来清爽多了,再也没有if, throw和美丽的花括号了,可喜可贺~可喜可贺~

 

但是!并不是一切都这么美好的!每一次调用方法都需要值一个字符串以表示参数的名称,这里写了4”s”,当然你会说4”s”没什么,但是写4”user.Name”再写6”user.CreationDate”呢……

什么感觉?手酸?NO~NO~这不是最重要的,重要的是写字符串的时候VS没有自动提示啊!你敢保证写10个不错一个字母么?

其实对这个阶段还是非常有感情的,怎么说都是对“把类似功能合在一起”的理念的尝试呢

 

第三阶段--扩展方法

然后,然后就有了C# 3.0,就有了扩展方法,然后Guard中的方法的第一个参数都加了一个this,调用就成了这样

public   void  SomeMethod( string  s)
{
    s.NotNull(
" s " );
    s.NotEmpty(
" s " );
    s.ShorterThan(
10 " s " );
    s.LongerThan(
3 " s " );
    
// 其他逻辑
}

基本我不认可这是一个阶段呢,只是用了点语法糖而已,最重要的字符串多次输入参数名称的问题根本没有得到解决,少打几个字会很快乐么……作为一个标准的程序员,我想说:NO!

 

 

第四阶段--不知道怎么说

 

就是现在在用的方法啦,刚刚“发明”出来的哦,个人自我感觉良好,但实在不知道怎么从设计上去解释,就先写一下实现方案吧

很久很久以前,有一个孤独的类,句叫ValidationHelper<T>,他的任务就是惩治世上所有不听话的参数,根据见过他的变量们的描述,他是长得这样的

 

Code
public class ValidationHelper<T>
{
#region 成员

private T m_Value;

private string m_Name;

#endregion

#region 属性

/// <summary>
/// 获取待验证的参数的值.
/// </summary>
public T Value
{
get
{
return m_Value;
}
}

/// <summary>
/// 获取待验证的参数的名称.
/// </summary>
public string Name
{
get
{
return m_Name;
}
}

#endregion

#region 构造函数

/// <summary>
/// 创建一个<see cref="ValidationHelper&lt;T&gt;"/>的对象.
/// </summary>
/// <param name="value">待验证的参数的值.</param>
/// <param name="name">待验证的参数的名称.</param>
public ValidationHelper(T value, string name)
{
m_Value
= value;
m_Name
= name;
}

#endregion

#region 基本方法

/// <summary>
/// 验证参数不为其默认值.
/// </summary>
/// <returns>this指针以方便链式调用.</returns>
/// <exception cref="ArgumentException">参数为值类型且为默认值.</exception>
/// <exception cref="ArgumentNullException">参数为引用类型且为null.</exception>
public ValidationHelper<T> NotDefault()
{
if (Value.Equals(default(T)))
{
if (Value is ValueType)
{
throw new ArgumentException(
String.Format(
"参数{0}不能使用默认值", Name), Name);
}
else
{
throw new ArgumentNullException(
String.Format(
"参数{0}不能为null", Name), Name);
}
}

return this;
}

/// <summary>
/// 使用自定义方法进行验证.
/// </summary>
/// <param name="rule">用以验证的自定义方法.</param>
/// <returns>this指针以方便链式调用.</returns>
/// <exception cref="Exception">验证失败抛出相应异常.</exception>
/// <remarks><paramref name="rule"/>的第一个参数为参数值,第二个参数为参数名称.</remarks>
public ValidationHelper<T> CustomRule(Action<T, string> rule)
{
rule(Value, Name);

return this;
}

#endregion
}

 

 

因为ValidationHelper<T>可以保存住参数的值和参数的名称,因此就一下子地解决了多次输入参数名称的问题,真不亏是大侠啊

但是,很多人都不知道,为什么功能这么少的ValidationHelper<T>可以消灭几乎所有的不规范参数,其实秘诀还在于扩展方法,通过扩展方法的联系,ValidationHelper<T>找到了很多伙伴,其实除暴安良的,是一个团队,而非一个人,下面来看看StringValidationHelper是怎么样对付不规范的string的吧

 

好了,伙伴找到了,现在的问题是,当需要大侠帮助的时候,我们不得不使出全身的力气使用new来召唤大侠……这显然很不爽,因此又有了一扩展方法,我们称之为“工厂”

public   static   class  Validation
{
    
public   static  ValidationHelper < T >  InitValidation < T > ( this  T value,  string  argName)
    {
        
return   new  ValidationHelper < T > (value, argName);
    }
}

最后,我们是这样来打败黑暗势力的

public   void  SomeMethod( string  s)
{
    s.InitValidation(
" s " )
        .NotDefault()
        .NotEmpty()
        .ShorterThan(
10 )
        .LongerThan(
3 );
}

反正本人是找不到更好的方法了,还请大家指教了

Code
public static class StringValidationHelper
{
/// <summary>
/// 验证<see cref="System.String"/>类型的参数不为空.
/// </summary>
/// <param name="current">用于验证的<see cref="ValidationHelper&lt;T&gt;"/></param>
/// <returns><paramref name="current"/>的引用以方便链式调用.</returns>
public static ValidationHelper<string> NotEmpty(this ValidationHelper<string> current)
{
current.NotDefault();

if (current.Value.Length == 0)
{
throw new ArgumentException(
String.Format(
"{0}不可为空字符串", current.Name), current.Name);
}

return current;
}

/// <summary>
/// 验证<see cref="System.String"/>类型的参数的长度小于一定值.
/// </summary>
/// <param name="current">用于验证的<see cref="ValidationHelper&lt;T&gt;"/></param>
/// <param name="length">可行的最大长度(包括此值).</param>
/// <returns><paramref name="current"/>的引用以方便链式调用.</returns>
public static ValidationHelper<string> ShorterThan(this ValidationHelper<string> current, int length)
{
current.NotDefault();

if (current.Value.Length > length)
{
throw new ArgumentException(
String.Format(
"{0}的长度不可超过{1}", current.Name, length), current.Name);
}

return current;
}

/// <summary>
/// 验证<see cref="System.String"/>类型的参数的长度大于一定值.
/// </summary>
/// <param name="current">用于验证的<see cref="ValidationHelper&lt;T&gt;"/></param>
/// <param name="length">可行的最小长度(包括此值).</param>
/// <returns><paramref name="current"/>的引用以方便链式调用.</returns>
public static ValidationHelper<string> LongerThan(this ValidationHelper<string> current, int length)
{
current.NotDefault();

if (current.Value.Length < length)
{
throw new ArgumentException(
String.Format(
"{0}的长度不可小于{1}", current.Name, length), current.Name);
}

return current;
}

/// <summary>
/// 验证<see cref="System.String"/>类型的参数的长度在一定值之间.
/// </summary>
/// <param name="current">用于验证的<see cref="ValidationHelper&lt;T&gt;"/></param>
/// <param name="minLength">可行的最小长度(包括此值).</param>
/// <param name="maxLength">可行的最大长度(包括此值).</param>
/// <returns><paramref name="current"/>的引用以方便链式调用.</returns>
public static ValidationHelper<string> LengthBetween(this ValidationHelper<string> current, int minLength, int maxLength)
{
current.NotDefault();

if (current.Value.Length < minLength || current.Value.Length > maxLength)
{
throw new ArgumentException(
String.Format(
"{0}的长度必须在{1}和{2}之间", current.Name, minLength, maxLength), current.Name);
}

return current;
}
}

 

你可能感兴趣的:(职场,验证,优雅,休闲)