C#基礎--Attribute(標簽) 和 reflect(反射) 應用
1.Attribute的定義與作用:
公共語言運行時允許你添加類似關鍵字的描述聲明,叫做attributes, 它對程序中的元素進行標注,如類型、字段、方法和屬性等。Attributes和Microsoft .NET Framework文件的元數據保存在一起,可以用來向運行時描述你的代碼,或者在程序運行的時候影響應用程序的行為.比如在WCF中常用的序列化。比如添加的[DataMenber]就是一種標簽。以及xml序列化也是一樣的。
2.Reflect的定義與作用
反射可以用於觀察並修改程序在運行時的執行。一個reflection-oriented程序組件可以監測一個范圍內的代碼執行情況,可以根據期望的目標與此相關的范圍修改本身。這通常是通過在運行時動態分配程序代碼實現。在面向對象的編程語言如Java中,反射允許在編譯期間不知道接口的名稱,字段、方法的情況下在運行時檢查類、接口、字段和方法。它還允許的實例化新對象和調用的方法。(摘抄至維基百科). 其實可以簡單理解為,通過反射可以很輕易的獲取到一個類中的所有方法屬性字段狀態。
3.為了更方便的認識反射以及Attribute的應用,下面有個例子.
業務背景如下: 有個Person對象,目前只有Name和Num屬性,然後要用方法校驗Person對象的合法性。
判斷規則: 1.Name必填,若Name為空,則提示"Name不能為空";
2.Num可以為空,但是若有數據的話必須由數字組成,若為非數字字符串則彈出"格式不正確".
3.Address可以為空,若有數據的話長度不能超過12,若超過12則提示"Address 長度不能超過12"
需要考慮: 對於Person類,以後熟悉的判斷規則可能會變化,也可能增加屬性,減少屬性。
4.解決方案一:
對於常規解決方案,可能大家會想到。目前只有Name,Address,Num三個屬性,每次我就寫三個代碼段分別對Name,Address,Num驗證。如果增加屬性, 比如增加了公共屬性EmailAddress,那麼就必須添加驗證字符串是否是郵箱的代碼,若刪除了某個屬性,就吧判斷該屬性是否合格的代碼刪除掉不進行判斷。這個也是我最開始想到的方法。不過這樣不好的地方是可維護性不好。改動太大了。而且會導致重復代碼。不如如果需求改成Address不能為空了。那麼在判斷address是否合法的時候還要去添加判斷不能為空的情況。
5.解決方案二:
為了解決方案一的問題,其實我們可以通過Attribute和反射來實現。思路如下:
5.1 首先我們創建一個PersonCheckAttribute對象,他繼承與System.Attribute.
復制代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReflectTest.Model
{
public class PersonCheckAttribute:Attribute
{
// 三個bool變量用於確定是有要驗證這些信息
private bool checkEmpty = false; //是否為空
private bool checkMaxLength = false; //最大長度
private bool checkRegex = false; //用正則表達式驗證參數(是郵箱,是否刷數字)
private int maxLength = 0;
private string regexStr = string.Empty;
public bool CheckEmpty
{
get { return this.checkEmpty; }
set { this.checkEmpty = value; }
}
public bool CheckMaxLength
{
get { return this.checkMaxLength; }
set { this.checkMaxLength = value; }
}
public bool CheckRegex
{
get { return this.checkRegex; }
set { this.checkRegex = value; }
}
public int MaxLength
{
get { return this.maxLength; }
set { this.maxLength = value; }
}
public string RegexStr
{
get { return this.regexStr; }
set { this.regexStr = value; }
}
}
}
復制代碼
5.2 然後我們來添加Person類。注意裡面的標簽。
Tips:我在裡面打了標簽,對於5.1的PersonCheckAttribute類中bool變量用來確定是否要進行驗證,公共屬性用來存取判斷參數。
復制代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReflectTest.Model
{
public class Person
{
[PersonCheck(CheckEmpty=true)]
public string Name { get; set; }
[PersonCheck(CheckRegex = true, RegexStr = "^[0-9]*[1-9][0-9]*$")]
public string Num { get; set; }
[PersonCheck(CheckMaxLength = true, MaxLength = 12)]
public string Address { get; set; }
}
}
復制代碼
5.3 屬性已經搞定,下面來看看,如何通過反射來獲取類的公共屬性,以及每個屬性的Attribute。
復制代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReflectTest.Model;
using System.Reflection;
using System.Text.RegularExpressions;
namespace ReflectTest.Business
{
public class PersonCheck
{
// 獲取所有的錯誤提示
public static string GetErrorMessage(Person person)
{
PropertyInfo[] propertyInfos = person.GetType().GetProperties(); // 獲取一個類的所有屬性
string errorMsg = string.Empty;
//對所有公用屬性(Name,Num,Address)遍歷
foreach (PropertyInfo info in propertyInfos)
{
errorMsg += GetSignalPropertity(info, person);
}
return errorMsg;
}
// 獲取單個公共屬性的Attribute.也就是model類裡面的[PersonCheck]信息
private static string GetSignalPropertity(PropertyInfo propertyInfo,Person person)
{
// 因為對於此示例,每個Properties(屬性)只有一個Attribute(標簽),所以用了first()來獲取,
// 不過有一點,就是必須在屬性裡面添加[PersonCheck]標簽,但是可以不設置表情裡面的字段.因為沒有的.GetCustomAttributes()返回為null.指向first會報錯.
PersonCheckAttribute attribute = propertyInfo.GetCustomAttributes().First() as PersonCheckAttribute;
string errorMsg = string.Empty;
//以下的if語句是判斷標簽裡面的設置,設置了什麼就執行什麼數據校驗
if (attribute.CheckEmpty)
{
string obj = propertyInfo.GetValue(person) as string;
if (string.IsNullOrEmpty(obj))
{
errorMsg += Environment.NewLine + string.Format("{0} 不能為空", propertyInfo.Name);
}
}
if (attribute.CheckMaxLength)
{
string obj = propertyInfo.GetValue(person) as string;
if (obj != null && obj.Length > attribute.MaxLength)
{
errorMsg += Environment.NewLine + string.Format("{0} 最大長度為{1}", propertyInfo.Name, attribute.MaxLength);
}
}
// 對於判斷數字郵箱都可以通過正則表達式
if (attribute.CheckRegex)
{
string obj = propertyInfo.GetValue(person) as string;
Regex regex = new Regex(attribute.RegexStr);
if (obj != null && !regex.IsMatch(obj))
{
errorMsg += Environment.NewLine + string.Format("{0} 格式不對", propertyInfo.Name);
}
}
return errorMsg;
}
}
}