c#中的特性Attribute
一:特性是什么?特性怎么创建怎么使用?
这一章节,我想谈谈c#的特性方面的知识,特性大家在工作开发中都很熟悉,比如我们经常见到的
/// <summary> /// 是给枚举用 提供一个额外信息 /// AllowMultiple特性影响编译器,AttributeTargets修饰的对象 AllowMultiple:能否重复修饰 Inherited:是否可继承 /// 可以指定属性和字段 /// </summary> [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field,AllowMultiple =true,Inherited =true)] public class RemarkAttribute : Attribute { public RemarkAttribute() { } public RemarkAttribute(string remark) { this.Remark = remark; } public string Description; //字段 public string Remark { get; private set; } }
然后特性类上面的AttributeUsage也是一个特性,具体的用处见上面的注释解释,特性创建好了,我们可以创建一个实体,然后再实体的字段上面使用这个特性,具体如下:
[Remark("用户状态")] public enum UserState { /// <summary> /// 正常 /// </summary> [Remark("正常")] Normal = 0, /// <summary> /// 冻结 /// </summary> [Remark("冻结")] Frozen = 1, /// <summary> /// 删除 /// </summary> [Remark("删除")] Deleted = 2 }
因为AttributeTargets.Enum标识这个特性可以放在枚举上面,那我们上面那种代码也是完全被允许的,此外,AttributeTargets如果不写,则默认是全部都可以使用的!特性创建和特性标识已经准备好了,然而怎么使特性生效呢?这个还是需要我们写代码来实现的
public static class RemarkExtend { /// <summary> /// 扩展方法 /// </summary> /// <param name="enumValue"></param> /// <returns></returns> public static string GetRemark(this Enum enumValue) { Type type = enumValue.GetType(); FieldInfo field = type.GetField(enumValue.ToString()); if (field.IsDefined(typeof(RemarkAttribute), true)) { RemarkAttribute remarkAttribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute)); return remarkAttribute.Remark; } else { return enumValue.ToString(); } } }
使特性的生效的代码已经写好了,然后我们如果想要获得一个枚举的中文名字可以通过下面调用
UserState userState = UserState.Normal;
Console.WriteLine(userState.GetRemark());
这样我们就可以随心所欲的获取到这个枚举的中文备注了,没有使用特性之前,我们想要获取到枚举的中文备注,一般会只能写死判断,如下:
UserState userState = UserState.Normal; if (userState == UserState.Normal) { Console.WriteLine("正常状态"); } else if (userState == UserState.Frozen) { Console.WriteLine("冻结状态"); } else { Console.WriteLine("删除状态"); }
然后一旦要修改状态名字,则代码也随着修改,这就会导致维护比较困难,如果使用了特性,则会用到的地方不需要太关注这些文字,只需要调用,然后修改的时候,只需要修改枚举上面的文字即可,极其容易维护!
二:特性的运用范围
特性运用范围极其广,框架中我们看到上面的总结就晓得了,下面的我总结了以下几个地方也可以使用特性
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:支持规则的随意扩展
运用了这个特性校验后,就不用再每个地方再分别写校验的逻辑,简单易用!