本文以一個項目中通用的驗證類來舉例說明如何使用自定義Attribute來擴展元數據。
在項目中,我們爲了保證各個層次之間的鬆藕合,通常把在各個層次之間傳遞數據的封裝在一個稱爲實體類的類中,比如ActionFrom
using System;
namespace AttributeTest
{
public class ActionForm
{
private string email = "";
private string password = "";
public string Email
{
get { return this.email; }
set { this.email = value; }
}
public string Password
{
get { return this.password; }
set { this.password = value; }
}
}
}
現在,在使用這些實體類中的數據之前,我們需要對其中的數據進行驗證。通常我們會寫個靜態類,用來提供各種不同的驗證方法。比如需要驗證Email,驗證Password,比如:
using System;
using System.Reflection;
using System.Text.RegularExpressions;
namespace AttributeTest
{
public class Validator
{
public static bool ValidateEmail(string email)
{
//方法體
}
public static bool ValidatePassword(string passwd)
{
//方法體
}
}
}
這樣的硬編碼混跡於各個層次之間,一旦實體類裏某個屬性發生變化,就不得不修改各個層次中的相關驗證代碼。於是,我們想到可以使用一個統一的驗證方法用來驗證所有的實體類中的屬性。
public static bool Validate(string propertyName, string propertyValue, Validator.ValidateType t) {...}
//這裏,Validator.ValidateType 是Validator中提供的一個枚舉。
public enum ValidateType
{
Email,
Password,
Number,
Id
}
這裏這個驗證方法,的第三個參數使得驗證與實體類的耦合密度增加了。我們還是不得不在修改實體類的時候,修改驗證方法的調用代碼。
現在,我們需要自定義Attribute來擴展實體類的元數據。通過對實體類元數據的描述,我們可以去掉驗證方法裏的第三個參數。
using System;
namespace AttributeTest
{
[System.AttributeUsage(AttributeTargets.Property)]
public class ValidateAttribute : System.Attribute
{
public ValidateAttribute(ValidateType validateType)
{
this.validateType = validateType;
}
private ValidateType validateType;
public ValidateType ValidateType
{
get { return this.validateType; }
set { this.validateType = value; }
}
}
public enum ValidateType
{
Email,
Password,
Number,
Id
}
}
自定義Attribute(特性)必須繼承於System.Attribute。還可以通過System.AttributeUsageAttribute特性,控制自定義特性的使用範圍(構件),例如,字段、方法。[System.AttributeUsage(AttributeTargets.Property)]限制這個自定義特性只能使用在類的屬性上。
現在,我們實現這個驗證方法:
using System;
using System.Reflection;
using System.Text.RegularExpressions;
namespace AttributeTest
{
public class Validator
{
public static bool Validate(object validateObject, string validateProperty)
{
System.Type t = validateObject.GetType();
PropertyInfo pi = t.GetProperty(validateProperty);
string validateValue = pi.GetValue(validateObject, null) as string;
if (pi.IsDefined(typeof(ValidateAttribute), true))
{
object[] atts = pi.GetCustomAttributes(true);
ValidateAttribute vatt = atts[0] as ValidateAttribute;
string strExpr = "";
switch (vatt.ValidateType)
{
case ValidateType.Email:
strExpr = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+{1}quot;;
break;
case ValidateType.Password:
strExpr = @"\d{6}";
break;
case ValidateType.Number:
strExpr = @"^\d*{1}quot;;
break;
case ValidateType.Id:
strExpr = @"^\w*{1}quot;;
break;
default:
return true;
}
Regex validateRegex = new Regex(strExpr);
return validateRegex.IsMatch(validateValue);
}
return true;
}
}
}
該方法需要兩個參數,一個是需要驗證的實體類的實例,還有一個是需要驗證的屬性名。當然,我們還需要在實體類上加上我們自定義的特性:
using System;
namespace AttributeTest
{
public class ActionForm
{
private string email = "";
private string password = "";
[Validate(ValidateType.Email)]
public string Email
{
get { return this.email; }
set { this.email = value; }
}
[Validate(ValidateType.Password)]
public string Password
{
get { return this.password; }
set { this.password = value; }
}
}
}
我們通過自定義特性對實體類的元數據進行擴展,指定每個屬性需要驗證的類型。
現在我們可以這樣使用這個驗證類:
ActionForm form = new ActionForm();
form.Email = [email protected];
form.Password = "123456";
bool isValidEmail = Validator.Validate(form, "Email");
bool isValidPassword = Validator.Validate(form, "Password");
Console.WriteLine("Email is {0}.", isValidEmail?"valid":"invalid");
Console.WriteLine("Password is {0}.", isValidPassword?"valid":"invalid");
Console.ReadLine();
我們通過拋出自定義異常的方法,將驗證擴大到實體類級別的驗證:
public static void ValidateProperty(object validateObject, string validateProperty)
{
System.Type t = validateObject.GetType();
PropertyInfo pi = t.GetProperty(validateProperty);
string validateValue = pi.GetValue(validateObject, null) as string;
if( pi.IsDefined(typeof(ValidateAttribute), true) )
{
object[] atts = pi.GetCustomAttributes(true);
ValidateAttribute vatt = atts[0] as ValidateAttribute;
string strExpr = "";
switch(vatt.ValidateType)
{
case ValidateType.Email:
strExpr = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+{1}quot;;
break;
case ValidateType.Password:
strExpr = @"\d{6}";
break;
case ValidateType.Number:
strExpr = @"^\d*{1}quot;;
break;
case ValidateType.Id:
strExpr = @"^\w*{1}quot;;
break;
default:
return;
}
Regex validateRegex = new Regex(strExpr);
if( !validateRegex.IsMatch(validateValue) )
{
throw new ApplicationException(validateProperty + " is invalid.");
}
}
}
public static void Validate(object validateObject)
{
System.Type t = validateObject.GetType();
PropertyInfo[] ps = t.GetProperties();
foreach(PropertyInfo pi in ps)
{
ValidateProperty(validateObject, pi.Name);
}
}
現在驗證,只需要這樣:
try
{
Validator.Validate(form);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
原文地址:http://blog.csdn.net/fangxing80/article/details/519547