C#特性(Attribute)
一、什么是特性
特性(Attribute)是用于在運行時傳遞程序中各種元素(比如類、方法、結(jié)構(gòu)、枚舉、組件等)的行為信息的聲明性標(biāo)簽。您可以通過使用特性向程序添加聲明性信息。一個聲明性標(biāo)簽是通過放置在它所應(yīng)用的元素前面的方括號([ ])來描述的。
特性(Attribute)用于添加元數(shù)據(jù),如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預(yù)定義特性和自定義特性。
特性的語法如下:
[attribute(positional_parameters, name_parameter = value, ...)] element
特性(Attribute)的名稱和值是在方括號內(nèi)規(guī)定的,放置在它所應(yīng)用的元素之前。positional_parameters 規(guī)定必需的信息,name_parameter 規(guī)定可選的信息。
二、預(yù)定義特性
Obsolete特性
這個預(yù)定義特性標(biāo)記了不應(yīng)被使用的程序?qū)嶓w。它可以讓您通知編譯器丟棄某個特定的目標(biāo)元素。例如,當(dāng)一個新方法被用在一個類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個應(yīng)該使用新方法,而不是舊方法的消息,來把它標(biāo)記為 obsolete(過時的)。
語法如下:
[Obsolete( message )] [Obsolete( message, iserror )]
其中:
- 參數(shù) message,是一個字符串,描述項目為什么過時的原因以及該替代使用什么。
- 參數(shù) iserror,是一個布爾值。如果該值為 true,編譯器應(yīng)把該項目的使用當(dāng)作一個錯誤。默認(rèn)值是 false(編譯器生成一個警告)。
請看下面的一個小例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
[Obsolete("請不要使用該類了,該類已經(jīng)過時了,請使用什么代替")]
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Accont { get; set; }
public long QQ { get; set; }
public string Answer([Custom]string name)
{
return $"This is {name}";
}
}
}上面的例子中,在Student類上面使用了Obsolete特性來標(biāo)注該類已經(jīng)過時了。編譯代碼結(jié)果:

三、自定義特性
.Net 框架允許創(chuàng)建自定義特性,用于存儲聲明性的信息,且可在運行時被檢索。該信息根據(jù)設(shè)計標(biāo)準(zhǔn)和應(yīng)用程序需要,可與任何目標(biāo)元素相關(guān)。
創(chuàng)建并使用自定義特性包含四個步驟:
- 聲明自定義特性
- 構(gòu)建自定義特性
- 在目標(biāo)程序元素上應(yīng)用自定義特性
- 通過反射訪問特性
1、聲明自定義特性
在上面的例子中,使用F12查看Obsolete的定義:

從上面的截圖中可以看出,.NET框架中的預(yù)定義特性是繼承自Attribute類,所以要自定義一個特性,只需要該類繼承自Attribute即可,下面定義一個Custom自定義特性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
/// <summary>
/// 自定義Custom特性
/// </summary>
public class CustomAttribute :Attribute
{
}
}注意:所有的特性默認(rèn)以Attribute結(jié)尾,但聲明的時候可以不以Attribute結(jié)尾。
2、構(gòu)建自定義特性
每個特性必須至少有一個構(gòu)造函數(shù)。必需的定位( positional)參數(shù)應(yīng)通過構(gòu)造函數(shù)傳遞。下面的代碼演示了CustomAttribute類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
/// <summary>
/// 自定義Custom特性
/// </summary>
public class CustomAttribute :Attribute
{
/// <summary>
/// 無參構(gòu)造函數(shù)
/// </summary>
public CustomAttribute()
{
}
/// <summary>
/// 有參構(gòu)造函數(shù)
/// </summary>
/// <param name="id"></param>
public CustomAttribute(string description)
{
this.Description = description;
}
/// <summary>
/// 屬性
/// </summary>
public string Description { get; set; }
/// <summary>
/// 字段
/// </summary>
public string Remark = null;
public void Show()
{
Console.WriteLine("This Is CustomAttribute");
}
}
}3、在目標(biāo)程序元素上應(yīng)用自定義特性
通過把特性放置在緊接著它的目標(biāo)(類、方法、屬性、字段等)上面,來應(yīng)用該特性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
[Obsolete("請不要使用該類了,該類已經(jīng)過時了")]
[Custom("這是Custom自定義特性")]
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Accont { get; set; }
public long QQ { get; set; }
public string Answer([Custom]string name)
{
return $"This is {name}";
}
}
}注意:
1、如果在聲明自定義特性的時候使用了Attribute結(jié)尾,那么應(yīng)用自定義特性的時候可以把Attribute省略掉;如果聲明的時候沒有以Attribute結(jié)尾,那么應(yīng)用自定義特性的時候就不能把Attribute省略掉。
2、默認(rèn)情況下相同的特性只能應(yīng)用一次,如果想應(yīng)用多次特性,那么需要給特性添加AttributeUsage特性,CustomAttribute特性修改如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
/// <summary>
/// 自定義Custom特性
/// </summary>
[AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =true)]
public class CustomAttribute :Attribute
{
/// <summary>
/// 無參構(gòu)造函數(shù)
/// </summary>
public CustomAttribute()
{
}
/// <summary>
/// 有參構(gòu)造函數(shù)
/// </summary>
/// <param name="id"></param>
public CustomAttribute(string description)
{
this.Description = description;
}
/// <summary>
/// 屬性
/// </summary>
public string Description { get; set; }
/// <summary>
/// 字段
/// </summary>
public string Remark = null;
public void Show()
{
Console.WriteLine("This Is CustomAttribute");
}
}
}其中,AttributeTargets是枚舉值,F(xiàn)12轉(zhuǎn)到定義可以查看AttributeTargets的所有枚舉值:

AttributeTargets的枚舉值表示Custom特性可以應(yīng)用在哪些目標(biāo)上面。例如:AttributeTargets的枚舉值是Class,則表示CustomAttribute只能應(yīng)用在類上面。這里枚舉值是All,表示可以在任何類型上面使用該特性。默認(rèn)情況下枚舉值是All。
AllowMultiple表示該特性是否可以在類型上面多次使用:

這里AllowMultiple的值為true,表示可以在類型上面多次使用該特性。如果為false,則表示只能使用一次。默認(rèn)情況下是false。
Inherited表示該特性是否可以由子類繼承:

默認(rèn)情況下Inherited為true。
這是在看Student類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
[Obsolete("請不要使用該類了,該類已經(jīng)過時了")]
[Custom("這是Custom自定義特性")]//使用有參構(gòu)造
[Custom()]//使用無參構(gòu)造
public class Student
{
public int Id { get; set; }
/// <summary>
/// 在屬性上面使用Custom特性
/// </summary>
[Custom("這是Name屬性")]
public string Name { get; set; }
public string Accont { get; set; }
public long QQ { get; set; }
/// <summary>
/// 在方法和參數(shù)上面使用Custom特性
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[Custom("這是Answer方法")]
public string Answer([Custom("這是方法參數(shù)")]string name)
{
return $"This is {name}";
}
}
}注意:如果一個類型上面多次使用了同一種特性,那么特性可以寫在一起,中間用逗號隔開,例如上面的定義和下面的是同樣的效果:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
[Obsolete("請不要使用該類了,該類已經(jīng)過時了")]
[Custom("這是Custom自定義特性"),Custom,Custom(),Custom(Remark ="備注")]
public class Student
{
public int Id { get; set; }
/// <summary>
/// 在屬性上面使用Custom特性
/// </summary>
[Custom("這是Name屬性")]
public string Name { get; set; }
public string Accont { get; set; }
public long QQ { get; set; }
/// <summary>
/// 在方法、方法參數(shù)、方法的返回值上面使用Custom特性
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[Custom("這是Answer方法")]//方法上面應(yīng)用特性
[return:Custom()] //方法的返回值應(yīng)用特性
public string Answer([Custom("這是方法參數(shù)")]string name)
{
return $"This is {name}";
}
}
}注意:在Web API中FromBaby和FromUri就是給方法的參數(shù)應(yīng)用特性。
4、通過反射訪問特性
定義一個Manager類來管理特性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
/// <summary>
/// 管理特性
/// </summary>
public class Manager
{
public static void Show(Student student)
{
// 獲取類型
Type type = typeof(Student); //或者使用student.GetType();
// 找到類型上面的特性 type.IsDefined表示找類型上面的特性
if (type.IsDefined(typeof(CustomAttribute), true))//檢查有沒有 性能高
{
//GetCustomAttribute 獲取特性 type.GetCustomAttribute表示找到類型上面定義的特性,表示調(diào)用構(gòu)造函數(shù)創(chuàng)建一個CustomAttribute類型的對象
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
// attribute.Description表示特性類里面的屬性 attribute.Remark表示特性類里面的字段
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
#region 獲取ID屬性上面定義的特性
// 獲取Id屬性
PropertyInfo property = type.GetProperty("Id");
//檢查Id屬性上面是否定義了CustomAttribute特性
if (property.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
#endregion
#region 獲取Answer()方法上面定義的特性
// 獲取Answer方法
MethodInfo method = type.GetMethod("Answer");
if (method.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
#endregion
#region 獲取參數(shù)定義的特性
ParameterInfo parameter = method.GetParameters()[0];
if (parameter.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)parameter.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
#endregion
#region 獲取返回值定義的特性
ParameterInfo returnParameter = method.ReturnParameter;
if (returnParameter.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
#endregion
string result = student.Answer("Tom");
Console.WriteLine(result);
}
}
}Main()方法里面調(diào)用:
using MyAttribute.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
class Program
{
static void Main(string[] args)
{
Student student = new Student();
student.Id = 123;
student.Name = "time";
// 使用Manager類管理Student
Manager.Show(student);
Console.ReadKey();
}
}
}結(jié)果:

四、應(yīng)用特性
場景一:用戶狀態(tài)的枚舉值,定義的是英文的字段,需要輸出中文含義。枚舉定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 枚舉類型 用戶狀態(tài)
/// </summary>
public enum UserState
{
/// <summary>
/// 正常
/// </summary>
Normal = 0,
/// <summary>
/// 凍結(jié)
/// </summary>
Frozen = 1,
/// <summary>
/// 刪除
/// </summary>
Deleted = 2
}
}普通做法:根據(jù)枚舉值進行判斷,然后輸出中文含義:
UserState userState = UserState.Normal;
switch(userState)
{
case UserState.Normal:
Console.WriteLine("正常");
break;
case UserState.Frozen:
Console.WriteLine("凍結(jié)");
break;
case UserState.Deleted:
Console.WriteLine("刪除");
break;
}這種寫法違反開不原則,不利于以后的擴展,下面使用特性實現(xiàn)。
先定義Remark特性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace MyAttribute.Extension
{
/// <summary>
/// RemarkAttribute 特性
/// </summary>
public class RemarkAttribute :Attribute
{
private string _Remark = null;
/// <summary>
/// 有參構(gòu)造
/// </summary>
/// <param name="remark"></param>
public RemarkAttribute(string remark)
{
this._Remark = remark;
}
public string GetRemark()
{
return _Remark;
}
}
}UserState枚舉修改如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 枚舉類型 用戶狀態(tài)
/// </summary>
public enum UserState
{
/// <summary>
/// 正常
/// </summary>
[Remark("正常")]
Normal = 0,
/// <summary>
/// 凍結(jié)
/// </summary>
[Remark("凍結(jié)")]
Frozen = 1,
/// <summary>
/// 刪除
/// </summary>
[Remark("刪除")]
Deleted = 2
}
}對Enum類型進行擴展:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
public static class EnumExtension
{
/// <summary>
/// Enum的擴展方法,靜態(tài)類、靜態(tài)方法 第一個參數(shù)前面添加this關(guān)鍵字
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetRemark(this Enum value)
{
// 獲取類型
Type type = value.GetType();
// 獲取字段
FieldInfo field = type.GetField(value.ToString());
// 判斷字段上面是否定義了RemarkAttribute特性
if (field.IsDefined(typeof(RemarkAttribute)))
{
// 創(chuàng)建實例
RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute));
// 返回RemarkAttribute特性里面的GetRemark()方法
return attribute.GetRemark();
}
else
{
return value.ToString();
}
}
}
}Main()方法里面調(diào)用:
using MyAttribute.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute
{
class Program
{
static void Main(string[] args)
{
Student student = new Student();
student.Id = 123;
student.Name = "time";
// 使用Manager類管理Student
//Manager.Show(student);
UserState userState = UserState.Normal;
//switch(userState)
//{
// case UserState.Normal:
// Console.WriteLine("正常");
// break;
// case UserState.Frozen:
// Console.WriteLine("凍結(jié)");
// break;
// case UserState.Deleted:
// Console.WriteLine("刪除");
// break;
//}
Console.WriteLine(userState.GetRemark());
Console.ReadKey();
}
}
}結(jié)果:

場景二、做數(shù)據(jù)校驗
Student中有QQ這個屬性,范圍是10000-999999999999,校驗QQ屬性的值在這個范圍區(qū)間內(nèi)。
1、定義一個RangeAttribute特性,用來驗證屬性范圍
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 定義LongAttribute特性,并且特性只能應(yīng)用在字段和屬性上面
/// </summary>
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
public class RangeAttribute :Attribute
{
/// <summary>
/// 最小范圍
/// </summary>
private long _MinRange = 0;
/// <summary>
/// 最大范圍
/// </summary>
private long _MaxRange = 0;
public RangeAttribute(long min,long max)
{
this._MinRange = min;
this._MaxRange = max;
}
/// <summary>
/// 檢查屬性范圍
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Check(object value)
{
if(value!=null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if(long.TryParse(value.ToString(),out long IResult))
{
if(IResult>this._MinRange && IResult<this._MaxRange)
{
return true;
}
}
}
return false;
}
}
}2、在Student類的QQ屬性上面應(yīng)用特性
[Range(10001,999999999999)]
public long QQ { get; set; }3、對Object類型進行擴展
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// object類型的驗證擴展
/// </summary>
public static class ObjectExtension
{
/// <summary>
/// 對object類型擴展一個Validate的方法
/// </summary>
/// <param name="obj"></param>
/// <param name="msg">輸出參數(shù),輸出驗證信息。如果驗證通過,輸出空字符串;如果驗證不通過,輸出具體信息</param>
/// <returns></returns>
public static bool Validate(this object obj,out string msg)
{
// 獲取類型
Type type = obj.GetType();
// 獲取屬性
PropertyInfo[] propertyInfos= type.GetProperties();
foreach(PropertyInfo prop in propertyInfos)
{
if(prop.IsDefined(typeof(LongAttribute)))
{
LongAttribute attribute = (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute));
if(!attribute.Check(prop.GetValue(obj)))
{
msg = prop.Name + "檢查失敗";
return false;
}
}
}
msg = "";
return true;
}
}
}4、在Manager類里面使用Validate擴展方法
// 驗證
string msg = string.Empty;
bool tfResult= student.Validate(out msg);
if(!tfResult)
{
Console.WriteLine(msg);
}5、在Main()方法里面調(diào)用
Student student = new Student(); student.Id = 123; student.Name = "time"; student.QQ = 9999; // 使用Manager類管理Student Manager.Show(student);
結(jié)果:

如果這時候Student里面增加了Name屬性,并且要驗證Name屬性的長度,這時需要增加一個驗證屬性長度的特性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 驗證長度的特性,只能應(yīng)用于字段和屬性上面
/// </summary>
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
public class LengthAttribute:Attribute
{
/// <summary>
/// 最小長度
/// </summary>
private int _MinLength = 0;
/// <summary>
/// 最大長度
/// </summary>
private int _MaxLength = 0;
public LengthAttribute(int min, int max)
{
this._MinLength = min;
this._MaxLength = max;
}
/// <summary>
/// 檢查屬性長度
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Check(object value)
{
if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if (long.TryParse(value.ToString(), out long IResult))
{
if (IResult > this._MinLength && IResult < this._MaxLength)
{
return true;
}
}
}
return false;
}
}
}在Student類的Name屬性上面應(yīng)用LengthAttribute特性:
[Length(5,10)]
public string Name { get; set; }在ObjectExtension里面增加長度的驗證:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// object類型的驗證擴展
/// </summary>
public static class ObjectExtension
{
/// <summary>
/// 對object類型擴展一個Validate的方法
/// </summary>
/// <param name="obj"></param>
/// <param name="msg">輸出參數(shù),輸出驗證信息。如果驗證通過,輸出空字符串;如果驗證不通過,輸出具體信息</param>
/// <returns></returns>
public static bool Validate(this object obj,out string msg)
{
// 獲取類型
Type type = obj.GetType();
// 獲取屬性
PropertyInfo[] propertyInfos= type.GetProperties();
foreach(PropertyInfo prop in propertyInfos)
{
// 檢查屬性上面是否定義了RangeAttribute特性
if (prop.IsDefined(typeof(RangeAttribute)))
{
RangeAttribute attribute = (RangeAttribute)prop.GetCustomAttribute(typeof(RangeAttribute));
if(!attribute.Check(prop.GetValue(obj)))
{
msg = string.Format($"屬性{ prop.Name}范圍檢查失敗");
return false;
}
}
// 檢查屬性上面是否定義了LengthAttribute特性
if (prop.IsDefined(typeof(LengthAttribute)))
{
LengthAttribute attribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute));
if (!attribute.Check(prop.GetValue(obj)))
{
msg = string.Format($"屬性{ prop.Name}長度檢查失敗");
return false;
}
}
}
msg = "";
return true;
}
}
}最后在Main()方法里面調(diào)用:
Student student = new Student(); student.Id = 123; student.Name = "time"; // 使用Manager類管理Student Manager.Show(student);
結(jié)果:

仔細(xì)查看ObjectExtension擴展類:每增加一個特性,擴展方法里面就要增加一段相同的代碼(只是特性的類型不同),那么能不能做到增加特性,而這里不需要修改呢?請看下面的修改:
1、定義一個抽象類繼承自Attribute,里面有一個抽象的Check()方法,定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 抽象基類,繼承自Attribute
/// </summary>
public abstract class AbstractValidateAttribute:Attribute
{
public abstract bool Check(object value);
}
}2、修改RangeAttribute和LengthAttribute兩個特性類,都繼承自AbstractValidateAttribute基類
RangeAttribute類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 定義LongAttribute特性,并且特性只能應(yīng)用在字段和屬性上面
/// </summary>
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
public class RangeAttribute : AbstractValidateAttribute
{
/// <summary>
/// 最小范圍
/// </summary>
private long _MinRange = 0;
/// <summary>
/// 最大范圍
/// </summary>
private long _MaxRange = 0;
public RangeAttribute(long min,long max)
{
this._MinRange = min;
this._MaxRange = max;
}
/// <summary>
/// 重寫基類方法 檢查屬性范圍
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override bool Check(object value)
{
if(value!=null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if(long.TryParse(value.ToString(),out long IResult))
{
if(IResult>this._MinRange && IResult<this._MaxRange)
{
return true;
}
}
}
return false;
}
}
}LengthAttribute類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// 驗證長度的特性,只能應(yīng)用于字段和屬性上面
/// </summary>
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
public class LengthAttribute: AbstractValidateAttribute
{
/// <summary>
/// 最小長度
/// </summary>
private int _MinLength = 0;
/// <summary>
/// 最大長度
/// </summary>
private int _MaxLength = 0;
public LengthAttribute(int min, int max)
{
this._MinLength = min;
this._MaxLength = max;
}
/// <summary>
/// 重寫基類方法 檢查屬性長度
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override bool Check(object value)
{
if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if (long.TryParse(value.ToString(), out long IResult))
{
if (IResult > this._MinLength && IResult < this._MaxLength)
{
return true;
}
}
}
return false;
}
}
}3、修改ObjectExtension擴展類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyAttribute.Extension
{
/// <summary>
/// object類型的驗證擴展
/// </summary>
public static class ObjectExtension
{
/// <summary>
/// 對object類型擴展一個Validate的方法
/// </summary>
/// <param name="obj"></param>
/// <param name="msg">輸出參數(shù),輸出驗證信息。如果驗證通過,輸出空字符串;如果驗證不通過,輸出具體信息</param>
/// <returns></returns>
public static bool Validate(this object obj,out string msg)
{
// 獲取類型
Type type = obj.GetType();
// 獲取屬性
PropertyInfo[] propertyInfos= type.GetProperties();
foreach(PropertyInfo prop in propertyInfos)
{
// 判斷屬性上面是否定義了AbstractValidateAttribute特性
if (prop.IsDefined(typeof(AbstractValidateAttribute),true))
{
// 屬性上面可能會定義多個特性,所以這里使用數(shù)組
object[] attributeArray = prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
foreach(AbstractValidateAttribute attribute in attributeArray)
{
if (!attribute.Check(prop.GetValue(obj)))
{
msg = string.Format($"屬性{ prop.Name}檢查失敗");
return false;
}
}
}
//// 檢查屬性上面是否定義了RangeAttribute特性
//if (prop.IsDefined(typeof(RangeAttribute)))
//{
// RangeAttribute attribute = (RangeAttribute)prop.GetCustomAttribute(typeof(RangeAttribute));
// if(!attribute.Check(prop.GetValue(obj)))
// {
// msg = string.Format($"屬性{ prop.Name}范圍檢查失敗");
// return false;
// }
//}
//// 檢查屬性上面是否定義了LengthAttribute特性
//if (prop.IsDefined(typeof(LengthAttribute)))
//{
// LengthAttribute attribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute));
// if (!attribute.Check(prop.GetValue(obj)))
// {
// msg = string.Format($"屬性{ prop.Name}長度檢查失敗");
// return false;
// }
//}
}
msg = "";
return true;
}
}
}4、運行結(jié)果:

經(jīng)過上面的修改以后,如果以后要新增一個特性,那么該特性只需要在本類中重寫基類的Check()方法即可,而不需要在修改ObjectExtension擴展類。
到此這篇關(guān)于C#特性的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于動態(tài)修改App.Config與web.Config的使用詳解
本篇文章是對動態(tài)修改App.Config與web.Config的使用進行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

