文章目錄
- 0.前篇
- 1.特性概念
- 2.特性的聲明和使用
- 2.1 特性定義語法
- 2.2 特性目標
- 3.預定義特性
- 3.1 AttributeUsage
- 3.2 Conditional
- 3.3 其它預定義特性
- 4.MyAttributeHelper(特性使用幫助類)
- 5.特性應用
- 5.1 添加說明信息并獲取
- 5.2 數據驗證
0.前篇
學習本文前建議先閱讀我的關于反射的博文:
https://editor.csdn.net/md/?articleId=139095147
1.特性概念
特性是用于在運行時傳遞程序中各種元素(比如類、方法、結構、枚舉、組件等)的行為信息的聲明性標簽。
2.特性的聲明和使用
2.1 特性定義語法
特性在聲明時以Attribute
結尾,在使用時可省去Attribute
[attribute(positional_parameters, name_parameter = value, …)]
element
參數說明:
- positional_parameters: 表示特性必須具備的信息。
- name_paramete:表示特性可選的信息。
- element: 在這里表示特性目標。
代碼示例:
特性聲明:
public class TestAttribute : Attribute
{public int Parm { get; set; }private int id;private string name;public TestAttribute(){}public TestAttribute(int id, string name){this.id = id;this.name = name;}
}
特性使用:
[Test] // 使用無參構造函數
public class TestClass1
{}[Test(Parm = 123)] // 使用無參構造函數 + 指定參數
public class TestClass2
{}[Test(1, "test")] // 使用有參構造函數
public class TestClass3
{}
2.2 特性目標
特性目標指的是應用特性的實體。例如,特性目標可以是類、特定方法或整個程序集。一般情況,特性應用于緊跟在它后面的元素。不過,C# 特性支持顯示標識,例如可以顯示標識為將特性應用于方法,或者是應用于其參數或返回值。
顯示標識特性目標的語法如下:
[target : attribute-list]
- target:表示指定的特性目標值。
- attribute-list:表示要應用的特性列表。
下表展示常用的 target 值:
目標值 | 適用對象 |
---|---|
assembly | 整個程序集 |
module | 當前程序集模塊 |
field | 類或結構中的字段 |
event | 事件 |
method | 方法或 get 和 set 屬性訪問器 |
param | 方法參數或 set 屬性訪問器參數 |
property | Property(屬性) |
return | 方法、屬性索引器或 get 屬性訪問器的返回值 |
type | 結構、類、接口、枚舉或委托 |
代碼示例:
// 默認: 應用于方法
[Test]
int Method1()
{ return 0;
}// 顯示指定應用于方法
[method: Test]
int Method2()
{ return 0;
}// 應用于參數
int Method3([Test] string contract)
{ return 0;
}// 應用于返回值
[return: Test]
int Method4()
{ return 0;
}
3.預定義特性
3.1 AttributeUsage
特性 AttributeUsage 描述了如何使用一個自定義特性類。注意,使用特性修飾的類 AttributeUsage 必須是 System.Attribute 的直接或間接派生類,否則將發生編譯時錯誤。
AttributeUsage 特性的語法如下:
[AttributeUsage(validon, AllowMultiple = allowmultiple, Inherited = inherited)]
- validon: 表示可被應用的特性目標值。它是枚舉器 AttributeTargets 的值的組合。默認值是 AttributeTargets.All。
- AllowMultiple: 可選,allowmultiple 選項為其提供一個布爾值。表示特性是否能被重復放置多次
- Inherited:可選,inherited 選項為其提供一個布爾值。表示 能否被派生類所繼承。
示例代碼:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ExampleAttribute : Attribute
{public ExampleAttribute(string name){}
}
3.2 Conditional
特性 Conditional 是有條件的意思。使用特性修飾的方法也就是條件方法,條件方法的執行依賴于指定的預處理標識符。預處理標識符影響著方法調用的條件編譯,這種影響取決于指定的值。例如事先用預處理器指令定義了一個 Test 字符,當條件方法指定的值也為 Test 時, 則該方法會被執行。
語法:
[Conditional(conditionalSymbol)]
- 參數 conditionalSymbol 表示指定的預處理標識符。
#define Test
using System;public class Example
{[Conditional("Test")]static void Method1(){Console.WriteLine("Method1");}static void Method2(){Console.WriteLine("Method2");}static void Main(){Method1();Method2();}
}
輸出:
Method1
Method2
注釋掉 #define Test 輸出
Method2
3.3 其它預定義特性
特性 | 說明 |
---|---|
[Obsolete] | 標記已過時的代碼,使得在使用過時成員時發出警告或錯誤信息。 |
[Serializable] | 用于標記類,表示該類可以序列化,即可以在網絡上傳輸或者在文件中存儲。 |
[DllImport] | 用于指示要在程序中調用非托管代碼(通常是 DLL)的方法。 |
[Conditional] | 與預處理指令 ‘#if’ 和 ‘#endif’ 結合使用,根據定義的條件編譯代碼。 |
[AttributeUsage] | 用于指定自定義特性的使用方式,如允許的目標類型和是否允許多次應用等。 |
[Conditional] | 根據條件編譯代碼,類似于預處理指令,但是使用 Attribute。 |
[DefaultValue] | 為屬性或字段設置默認值。 |
[Description] | 為屬性或者事件提供一個描述,通常在設計時使用。 |
4.MyAttributeHelper(特性使用幫助類)
using System.Reflection;namespace Ming.Utils
{public static class MyAttributeHelper{/// <summary>/// 獲取該類型下所有的帶Attribute的方法/// </summary>/// <typeparam name="T">特性類型</typeparam>/// <param name="type"></param>/// <returns></returns>public static List<MethodInfo> GetAllMethods<T>(Type type) where T : class, new(){var res = new List<MethodInfo>();res = type.GetMethods().Where(t => t.GetCustomAttributes(typeof(T), false).Any()).ToList();return res;}/// <summary>/// 獲取該類型下所有的帶Attribute的屬性/// </summary>/// <typeparam name="T"></typeparam>/// <param name="type"></param>/// <returns></returns>public static List<PropertyInfo> GetAllPropertys<T>(Type type) where T : class, new(){var res = new List<PropertyInfo>();res = type.GetProperties().Where(t => t.GetCustomAttributes(typeof(T), false).Any()).ToList();return res;}/// <summary>/// 獲取程序集所有帶 T 特性的類class/// </summary>/// <typeparam name="T">特性類型</typeparam>/// <returns>程序集下所有帶 T 特性的類class</returns>public static List<Type> GetAllTypes<T>() where T : Attribute{var res = new List<Type>();//Assembly存放所有的程序集res = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.GetCustomAttributes(typeof(T), false).Any())//我們找到所有程序集中帶有T特性的Type類型.ToList();return res;}/// <summary>/// 獲取類上的特性/// </summary>/// <typeparam name="T"></typeparam>/// <param name="model"></param>/// <returns></returns>public static T GetAttribute<T>(Type type) where T : Attribute, new(){var res = new T();res = type.GetCustomAttribute<T>();return res;}/// <summary>/// 獲取方法上的特性/// </summary>/// <typeparam name="T"></typeparam>/// <param name="model"></param>/// <returns></returns>public static T GetAttribute<T>(MethodInfo type) where T : Attribute, new(){var res = new T();res = type.GetCustomAttribute<T>();return res;}/// <summary>/// 獲取屬性上的特性/// </summary>/// <typeparam name="T"></typeparam>/// <param name="model"></param>/// <returns></returns>public static T GetAttribute<T>(PropertyInfo type) where T : Attribute, new(){var res = new T();res = type.GetCustomAttribute<T>();return res;}/// <summary>/// 返回帶有Attribute的類型元祖列表/// </summary>/// <typeparam name="Att"></typeparam>/// <returns></returns>public static List<(Type type, Att att)> GetAll_TypeAndAtt<Att>() where Att : Attribute, new(){var res = new List<(Type type, Att att)> ();var typeLists = GetAllTypes<Att>();foreach (var item in typeLists){var att = GetAttribute<Att>(item);res.Add((item, att)); }return res;}/// <summary>/// 返回帶有Attribute的變量元祖列表/// </summary>/// <typeparam name="Att"></typeparam>/// <param name="type"></param>/// <returns></returns>public static List<(PropertyInfo property, Att att)> GetAll_PropertyAndAtt<Att>(Type type) where Att : Attribute, new(){var res = new List<(PropertyInfo type, Att att)>();var typeLists = GetAllPropertys<Att>(type);foreach (var item in typeLists){var att = GetAttribute<Att>(item);res.Add((item, att));}return res;}/// <summary>/// 返回帶有Attribute的方法元祖列表/// </summary>/// <typeparam name="Att"></typeparam>/// <param name="type"></param>/// <returns></returns>public static List<(MethodInfo method, Att att)> GetAll_MethodAndAtt<Att>(Type type) where Att : Attribute, new(){var res = new List<(MethodInfo type, Att att)>();var typeLists = GetAllMethods<Att>(type);foreach (var item in typeLists){var att = GetAttribute<Att>(item);res.Add((item, att));}return res;}}
}
5.特性應用
5.1 添加說明信息并獲取
/// <summary>
/// 備注特性
/// </summary>
public class RemarkAttribute : Attribute
{private string Remark { get; set; }public RemarkAttribute(string Remark){this.Remark = Remark;}public string GetRemark(){return this.Remark;}
}// 枚舉
public enum ESex
{[Remark("男")]male = 1,[Remark("女")]female = 2,
}/// <summary>
/// Enum擴展方法
/// </summary>
public static class EnumExtension
{public static string GetRemark(this Enum model){if (model is ESex){Type type = typeof(ESex);FieldInfo fi = type.GetField(model.ToString());object[] attributes = fi.GetCustomAttributes(true);foreach (var attr in attributes){if (attr is RemarkAttribute){RemarkAttribute remark = (RemarkAttribute)attr;return remark.GetRemark();}}}return string.Empty;}
}
使用:
Console.WriteLine(ESex.male.GetRemark());
// 男
5.2 數據驗證
可參考:
https://www.cnblogs.com/jiangxifanzhouyudu/p/11107734.html
(1)基類抽象特性
using System;namespace MyAttribute.ValidateExtend
{public abstract class AbstractValidateAttribute : Attribute{public abstract bool Validate(object oValue);}
}
(2)子類特性實現–數字長度
using System;namespace MyAttribute.ValidateExtend
{[AttributeUsage(AttributeTargets.Property)]public class LongAttribute : AbstractValidateAttribute{private long _Min = 0;private long _Max = 0;public LongAttribute(long min, long max){this._Min = min;this._Max = max;}public override bool Validate(object oValue){return oValue != null&& long.TryParse(oValue.ToString(), out long lValue)&& lValue >= this._Min&& lValue <= this._Max;}}
}
(3)子類特性實現–可空
namespace MyAttribute.ValidateExtend
{public class RequiredAttribute : AbstractValidateAttribute{public override bool Validate(object oValue){return oValue != null&& !string.IsNullOrWhiteSpace(oValue.ToString());}}
}
(4)子類特性實現–字符串長度
using System;namespace MyAttribute.ValidateExtend
{[AttributeUsage(AttributeTargets.Property)]public class StringLengthAttribute : AbstractValidateAttribute{private int _Min = 0;private int _Max = 0;public StringLengthAttribute(int min, int max){this._Min = min;this._Max = max;}public override bool Validate(object oValue){return oValue != null&& oValue.ToString().Length >= this._Min&& oValue.ToString().Length <= this._Max;}}
}
(5)泛型擴展方法
using System;namespace MyAttribute.ValidateExtend
{public static class AttributeExtend{public static bool Validate<T>(this T t){Type type = t.GetType();foreach (var prop in type.GetProperties()){if (prop.IsDefined(typeof(AbstractValidateAttribute), true)){object oValue = prop.GetValue(t);foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true)){if (!attribute.Validate(oValue))return false;}}}return true;}}
}
(6)常規類字段定義
using System;namespace MyAttribute.ValidateExtend
{public static class AttributeExtend{public static bool Validate<T>(this T t){Type type = t.GetType();foreach (var prop in type.GetProperties()){if (prop.IsDefined(typeof(AbstractValidateAttribute), true)){object oValue = prop.GetValue(t);foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true)){if (!attribute.Validate(oValue))return false;}}}return tue;}}
}
(7)類調用擴展方法驗證字段
using MyAttribute.EnumExtend;
using MyAttribute.ValidateExtend;
using System;namespace MyAttribute
{/// <summary>/// main方法調用/// </summary>class Program{static void Main(string[] args){try{ #region 特性實現數據驗證,并且可擴展{//通過特性去提供額外行為//數據驗證--到處都需要驗證StudentVip student = new StudentVip(){Id = 123,Name = "無為",QQ = 729220650,Salary = 1010000}; if (student.Validate()){Console.WriteLine("特性校驗成功");}//1 可以校驗多個屬性//2 支持多重校驗//3 支持規則的隨意擴展}#endregion}catch (Exception ex){Console.WriteLine(ex.Message);}Console.Read();}}
}