C# 自定义特性Attribute
一、特性Attribute和注释有什么区别
特性Attribute
A:就是一个类,直接继承/间接继承Attribute
B:特性可以在后期反射中处理,特性本身是没有什么*用的
C:特性会影响编译和运行时功能
注释
A:就是对代码的解释和说明,其目的是让人们能够更加轻松地了解代码。注释是编写程序时,写程序的人给一个语句、程序段、函数等的解释或提示,能提高程序代码的可读性
B:注释不能后期处理
二、自定义Attribute特性的使用
自定义Attribute特性的语法
其实特性就是一个类,直接继承或者间接的继承Atrribute的就是一个特性
首先声明特性,以下面的代码举个例子
//直接继承Attribute public class CustomAttribute : Attribute { public string Name { get; set; } public CustomAttribute() { Console.WriteLine($"{this.GetType().Name} 无参构造函数"); } public CustomAttribute(string name) { Console.WriteLine($"{this.GetType().Name} string 参数构造函数"); Name = name; } } //间接继承Attribute public class CustomAttributeChild : CustomAttribute { public CustomAttributeChild() { Console.WriteLine($"{this.GetType().Name} 无参构造函数"); } public CustomAttributeChild(string name) : base(name) { Console.WriteLine($"{this.GetType().Name} string 参数构造函数"); } }
特性的使用
首先我们准备一个自定义特性DisplayName
自定义特性
/// <summary> /// 是给属性和字段 提供一个额外信息 /// AllowMultiple特性影响编译器,AttributeTargets修饰的对象 AllowMultiple:能否重复修饰 Inherited:是否可继承 /// 可以指定属性和字段 /// </summary> namespace Ramon.Common.CustomAttribute { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public sealed class DisplayNameAttribute : Attribute { public string Name { get; } public DisplayNameAttribute(string sValue) { Name = sValue; } } }
在CompanyModel的属性上使用DisplayName的特性
BaseModel
namespace Ramon.Common.Framework { public class BaseModel { public int Id { get; set; } } }
CompanyModel
namespace Ramon.Model.Model { public class CompanyModel : BaseModel { [DisplayName("公司名称")] public string Name { get; set; } [DisplayName("创建日期")] public DateTime CreateTime { get; set; } [DisplayName("创建人Id")] public int CreatorId { get; set; } [DisplayName("最后修改人Id")] public int? LastModifierId { get; set; } [DisplayName("最后修改时间")] public DateTime? LastModifyTime { get; set; } } }
我们标识了这些特性到底要怎么使用呢?
文章的开始就已经说了特性可以在后期反射中处理,特性本身是没有什么*用的
那么我们通过反射如何去使用特性
接下来以上面给出的代码去实现,定义了一个扩展方法
用到了反射和泛型等知识,如果有小伙伴不懂可参考以下文章
反射:https://www.cnblogs.com/Ramon-Zeng/p/10189097.html
泛型:https://www.cnblogs.com/Ramon-Zeng/p/10172818.html
public static class BaseModelExtenstion { public static void ConsoleEntity<TEntity>(this TEntity tEntity) where TEntity : BaseModel { Type type = tEntity.GetType(); PropertyInfo[] propInfos = type.GetProperties(); foreach (var prop in propInfos) { string parameterName = prop.Name; if (prop.IsDefined(typeof(DisplayNameAttribute), false)) { DisplayNameAttribute attribute = (DisplayNameAttribute)prop.GetCustomAttribute(typeof(DisplayNameAttribute), false); parameterName = attribute.Name.IsNullOrWhiteSpace() ? prop.Name : attribute.Name; } Console.WriteLine($"{parameterName}:{prop.GetValue(tEntity)}"); } } }
代码调用
class Program { static void Main(string[] args) { CompanyModel companyModel = new CompanyModel() { Id = 1, Name = "Ramon----集团", CreateTime = DateTime.Now, CreatorId = 1, }; companyModel.ConsoleEntity(); } }
结果
二:特性的运用范围
特性运用范围极其广,框架中我们看到上面的总结就晓得了,下面的我总结了以下几个地方也可以使用特性
1:数据展示 不想展示属性名字,而是用中文描述
2:想指定哪个是主键,哪个是自增
3:别名 数据库里面叫A 程序是B,怎么映射等
然后特性还可以校验一些数据字段等,下面我写个例子,以便更加容易理解:
1:先创建数据校验的特性
public abstract class AbstractValidateAttribute : Attribute { public abstract bool Validate(object oValue); } public class LongValidateAttribute : AbstractValidateAttribute { private long _lMin = 0; private long _lMax = 0; public LongValidateAttribute(long lMin, long lMax) { this._lMin = lMin; this._lMax = lMax; } public override bool Validate(object oValue) { return this._lMin < (long)oValue && (long)oValue < this._lMax; } } public class RequirdValidateAttribute : AbstractValidateAttribute { public override bool Validate(object oValue) { return oValue != null; } }
2:再一个实体类上面的字段增加特性
public class Student { [RequirdValidate] public int Id { get; set; } [LongValidate(5,10)]//还有各种检查 public string Name { get; set; } [LongValidate(20, 50)] public string Accont { get; set; } /// <summary> /// 10001~999999999999 /// </summary> [LongValidate(10001, 999999999999)] public long QQ { get; set; } }
3:写下面的方法,使特性生效
public class DataValidate { public static bool Validate<T>(T t) { Type type = t.GetType(); //IsDefined 是判断,不会调用构造函数 //if (type.IsDefined(typeof(AbstractValidateAttribute), true)) //{ // //调用构造函数 // var oAttributeArray = type.GetCustomAttributes(typeof(AbstractValidateAttribute), true); // foreach (var item in oAttributeArray) // { // } //} //foreach (var method in type.GetMethods()) //{ // if (method.IsDefined(typeof(AbstractValidateAttribute), true)) // { // object item = method.GetCustomAttributes(typeof(AbstractValidateAttribute), true)[0]; // AbstractValidateAttribute attribute = item as AbstractValidateAttribute; // //if (!attribute.Validate(method.GetValue(t))) // //{ // // result = false; // // break; // //} // } //} bool result = true; foreach (var prop in type.GetProperties()) { if (prop.IsDefined(typeof(AbstractValidateAttribute), true)) { object item = prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true)[0]; AbstractValidateAttribute attribute = item as AbstractValidateAttribute; if (!attribute.Validate(prop.GetValue(t))) { result = false; break; } } } return result; } }
4:应用判断时如下:
Student student = new Student(); student.Id = 123; student.Name = "MrSorry"; student.QQ = 123456; var result = DataValidate.Validate(student);
这样就完成了数据校验,特性校验的特点,总结得到了如下三点:
1:可以校验多个属性
2:可以支持多重校验
3:支持规则的随意扩展
运用了这个特性校验后,就不用再每个地方再分别写校验的逻辑,简单易用!