文章目录
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: 在这里表示特性目标。
代码示例:
特性声明:
publicclassTestAttribute:Attribute{publicint Parm {get;set;}privateint id;privatestring name;publicTestAttribute(){}publicTestAttribute(int id,string name){this.id = id;this.name = name;}}
特性使用:
[Test]// 使用无参构造函数publicclassTestClass1{}[Test(Parm =123)]// 使用无参构造函数 + 指定参数publicclassTestClass2{}[Test(1,"test")]// 使用有参构造函数publicclassTestClass3{}
2.2 特性目标
特性目标指的是应用特性的实体。例如,特性目标可以是类、特定方法或整个程序集。一般情况,特性应用于紧跟在它后面的元素。不过,C# 特性支持显示标识,例如可以显示标识为将特性应用于方法,或者是应用于其参数或返回值。
显示标识特性目标的语法如下:
[target : attribute-list]
- target:表示指定的特性目标值。
- attribute-list:表示要应用的特性列表。
下表展示常用的 target 值:
目标值适用对象assembly整个程序集module当前程序集模块field类或结构中的字段event事件method方法或 get 和 set 属性访问器param方法参数或 set 属性访问器参数propertyProperty(属性)return方法、属性索引器或 get 属性访问器的返回值type结构、类、接口、枚举或委托
代码示例:
// 默认: 应用于方法[Test]intMethod1(){return0;}// 显示指定应用于方法[method:Test]intMethod2(){return0;}// 应用于参数intMethod3([Test]string contract){return0;}// 应用于返回值[return:Test]intMethod4(){return0;}
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)]publicclassExampleAttribute:Attribute{publicExampleAttribute(string name){}}
3.2 Conditional
特性 Conditional 是有条件的意思。使用特性修饰的方法也就是条件方法,条件方法的执行依赖于指定的预处理标识符。预处理标识符影响着方法调用的条件编译,这种影响取决于指定的值。例如事先用预处理器指令定义了一个 Test 字符,当条件方法指定的值也为 Test 时, 则该方法会被执行。
语法:
[Conditional(conditionalSymbol)]
- 参数 conditionalSymbol 表示指定的预处理标识符。
#define TestusingSystem;publicclassExample{[Conditional("Test")]staticvoidMethod1(){
Console.WriteLine("Method1");}staticvoidMethod2(){
Console.WriteLine("Method2");}staticvoidMain(){Method1();Method2();}}
输出:
Method1
Method2
注释掉 #define Test 输出
Method2
3.3 其它预定义特性
特性说明
[Obsolete]
标记已过时的代码,使得在使用过时成员时发出警告或错误信息。
[Serializable]
用于标记类,表示该类可以序列化,即可以在网络上传输或者在文件中存储。
[DllImport]
用于指示要在程序中调用非托管代码(通常是 DLL)的方法。
[Conditional]
与预处理指令 ‘#if’ 和 ‘#endif’ 结合使用,根据定义的条件编译代码。
[AttributeUsage]
用于指定自定义特性的使用方式,如允许的目标类型和是否允许多次应用等。
[Conditional]
根据条件编译代码,类似于预处理指令,但是使用 Attribute。
[DefaultValue]
为属性或字段设置默认值。
[Description]
为属性或者事件提供一个描述,通常在设计时使用。
4.MyAttributeHelper(特性使用帮助类)
usingSystem.Reflection;namespaceMing.Utils{publicstaticclassMyAttributeHelper{/// <summary>/// 获取该类型下所有的带Attribute的方法/// </summary>/// <typeparam name="T">特性类型</typeparam>/// <param name="type"></param>/// <returns></returns>publicstaticList<MethodInfo>GetAllMethods<T>(Type type)whereT:class,new(){var res =newList<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>publicstaticList<PropertyInfo>GetAllPropertys<T>(Type type)whereT:class,new(){var res =newList<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>publicstaticList<Type>GetAllTypes<T>()whereT:Attribute{var res =newList<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>publicstaticTGetAttribute<T>(Type type)whereT:Attribute,new(){var res =newT();
res = type.GetCustomAttribute<T>();return res;}/// <summary>/// 获取方法上的特性/// </summary>/// <typeparam name="T"></typeparam>/// <param name="model"></param>/// <returns></returns>publicstaticTGetAttribute<T>(MethodInfo type)whereT:Attribute,new(){var res =newT();
res = type.GetCustomAttribute<T>();return res;}/// <summary>/// 获取属性上的特性/// </summary>/// <typeparam name="T"></typeparam>/// <param name="model"></param>/// <returns></returns>publicstaticTGetAttribute<T>(PropertyInfo type)whereT:Attribute,new(){var res =newT();
res = type.GetCustomAttribute<T>();return res;}/// <summary>/// 返回带有Attribute的类型元祖列表/// </summary>/// <typeparam name="Att"></typeparam>/// <returns></returns>publicstaticList<(Type type, Att att)>GetAll_TypeAndAtt<Att>()whereAtt:Attribute,new(){var res =newList<(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>publicstaticList<(PropertyInfo property, Att att)>GetAll_PropertyAndAtt<Att>(Type type)whereAtt:Attribute,new(){var res =newList<(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>publicstaticList<(MethodInfo method, Att att)>GetAll_MethodAndAtt<Att>(Type type)whereAtt:Attribute,new(){var res =newList<(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>publicclassRemarkAttribute:Attribute{privatestring Remark {get;set;}publicRemarkAttribute(string Remark){this.Remark = Remark;}publicstringGetRemark(){returnthis.Remark;}}// 枚举publicenumESex{[Remark("男")]
male =1,[Remark("女")]
female =2,}/// <summary>/// Enum扩展方法/// </summary>publicstaticclassEnumExtension{publicstaticstringGetRemark(thisEnum model){if(model isESex){Type type =typeof(ESex);FieldInfo fi = type.GetField(model.ToString());object[] attributes = fi.GetCustomAttributes(true);foreach(var attr in attributes){if(attr isRemarkAttribute){RemarkAttribute remark =(RemarkAttribute)attr;return remark.GetRemark();}}}returnstring.Empty;}}
使用:
Console.WriteLine(ESex.male.GetRemark());// 男
5.2 数据验证
可参考:
https://www.cnblogs.com/jiangxifanzhouyudu/p/11107734.html
(1)基类抽象特性
usingSystem;namespaceMyAttribute.ValidateExtend{publicabstractclassAbstractValidateAttribute:Attribute{publicabstractboolValidate(object oValue);}}
(2)子类特性实现–数字长度
usingSystem;namespaceMyAttribute.ValidateExtend{[AttributeUsage(AttributeTargets.Property)]publicclassLongAttribute:AbstractValidateAttribute{privatelong _Min =0;privatelong _Max =0;publicLongAttribute(long min,long max){this._Min = min;this._Max = max;}publicoverrideboolValidate(object oValue){return oValue !=null&&long.TryParse(oValue.ToString(),outlong lValue)&& lValue >=this._Min
&& lValue <=this._Max;}}}
(3)子类特性实现–可空
namespaceMyAttribute.ValidateExtend{publicclassRequiredAttribute:AbstractValidateAttribute{publicoverrideboolValidate(object oValue){return oValue !=null&&!string.IsNullOrWhiteSpace(oValue.ToString());}}}
(4)子类特性实现–字符串长度
usingSystem;namespaceMyAttribute.ValidateExtend{[AttributeUsage(AttributeTargets.Property)]publicclassStringLengthAttribute:AbstractValidateAttribute{privateint _Min =0;privateint _Max =0;publicStringLengthAttribute(int min,int max){this._Min = min;this._Max = max;}publicoverrideboolValidate(object oValue){return oValue !=null&& oValue.ToString().Length >=this._Min
&& oValue.ToString().Length <=this._Max;}}}
(5)泛型扩展方法
usingSystem;namespaceMyAttribute.ValidateExtend{publicstaticclassAttributeExtend{publicstaticboolValidate<T>(thisT 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))returnfalse;}}}returntrue;}}}
(6)常规类字段定义
usingSystem;namespaceMyAttribute.ValidateExtend{publicstaticclassAttributeExtend{publicstaticboolValidate<T>(thisT 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))returnfalse;}}}return tue;}}}
(7)类调用扩展方法验证字段
usingMyAttribute.EnumExtend;usingMyAttribute.ValidateExtend;usingSystem;namespaceMyAttribute{/// <summary>/// main方法调用/// </summary>classProgram{staticvoidMain(string[] args){try{#region 特性实现数据验证,并且可扩展{//通过特性去提供额外行为//数据验证--到处都需要验证StudentVip student =newStudentVip(){
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();}}}
版权归原作者 明明明h 所有, 如有侵权,请联系我们删除。