继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

自定义特性与应用

慕少森
关注TA
已关注
手记 236
粉丝 42
获赞 216

自定义特性允许把自定义元数据与程序元素关联起来。在.NET Framework框架中,微软定义了许多特性提供给开发人员使用,如StructLayout特性中的信息在内存中布置结构。这些已有的特性得到了C#编译器的支持,编译器可以以特殊的方式定制编译过程。但是,在某些特定场合需要开发人员定义自己的特性,如数据验证、字段解释等场景。

  自定义特性在很大程度上是依赖于反射,代码在运行期间读取这些元数据,使用它们在运行期间做出决策,可以直接影响代码的运行方式。

1、编写自定义特性

  自定义特性需要继承自抽象特性类Attribute。假定已定义了一个自定义特性类DescriptionAttribute,其已用于以下属性:

[Description("姓名")]public string Name { get; set; }

  当C#编译器发现这个属性应用了Description特性时,会先把字符串Attribute追加到Description名称后面,形成DescriptionAttribute,然后全局搜索所有命名空间查找对应的类。如果应用特性时后面有Attribute,编译器就不会把该字符串添加到后面。

  编译器找到含有该名称的类,且该类直接或者间接派生自Attribute。编译器会认为该类包含控制特性的信息。特别时属性类需要指定:

  • 特性可以应用到那哪些类型的程序元素上(类、结构、属性、方法等)

  • 是否可以多次应用到同一程序元素上

  • 特性应用到类或接口上时,是否由派生类和接口继承

  • 特性有哪些必选和可选元素

  如完成上面的自定义特性类DescriptionAttribute:

复制代码

 /// <summary>
 /// 解释说明特性 /// </summary>
 [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Class | AttributeTargets.Field |AttributeTargets.Property,AllowMultiple =false,Inherited =false)] public class DescriptionAttribute : Attribute
 {     /// <summary>
     /// 说明解释     /// </summary>
     public string Description { get; private set; }     public DescriptionAttribute(string description)
     {         this.Description = description;
     }
 }

复制代码

  AttributeUsage特性类用以标记特性类,它只能用于特性类上,不能用于非特性类。AtrributeUsage类主要是标识自定义特性可以用于那些类型的程序元素上,使用AttributeTargets枚举可以指定用于一个或多个类型元素上。在指定用于多个类型元素上时,使用“|”运算符。该值时必须的,默认为All。特性的另外两个参数:AllowMultiple和Inherited是可选参数。

2、特性类的应用

  建立一个Student类,并对其应用上述特性:

复制代码

 /// <summary>
 /// 性别 /// </summary>
 public enum Sex
 {     /// <summary>
     /// 男     /// </summary>
     [DescriptionAttribute("男")]
     Man=1,     /// <summary>
     /// 女     /// </summary>
     [DescriptionAttribute("女")]
     Woman =2
 } public class Student
 {     /// <summary>
     /// 学生ID     /// </summary>
     [DescriptionAttribute("学号")]     public long ID { get; set; }     /// <summary>
     /// 姓名     /// </summary>
     [Description("姓名")]     public string Name { get; set; }     /// <summary>
     /// 性别     /// </summary>
     [DescriptionAttribute("性别")]     public Sex Sex { get; set; }     /// <summary>
     /// 年龄     /// </summary>
     [DescriptionAttribute("年龄")]     public byte  Age { get; private set; }     private DateTime birthDate = DateTime.Now.Date;     /// <summary>
     /// 出生日期     /// </summary>
     [DescriptionAttribute("出生日期")]     public DateTime BirthDate
     {         get { return birthDate; }         set
         {
             birthDate = value;
             Age = (byte)ComputeAge(birthDate);
         }
     }     /// <summary>
     /// 计算年龄     /// </summary>
     /// <param name="birthDate">出生日期</param>
     /// <returns>年龄</returns>
     private int ComputeAge(DateTime birthDate)
     {         if (DateTime.Now.Year > birthDate.Year)
         {             return DateTime.Now.Year - birthDate.Year;
         }         else if (DateTime.Now.Year == birthDate.Year)
         {             return 1;
         }         return 0;
     }
 }

复制代码

  现在需要在程序代码中访问,使用和获取对应特性应用的效果。首先,在Program类中编写一个静态函数ShowDescription(object[] attributes),用以显示对应的描述:

复制代码

/// <summary>/// 显示解释/// </summary>/// <param name="attributes">特性集合</param>public static void ShowDescription(object[] attributes)
{    if (attributes != null && attributes.Length > 0)
    {        foreach (object obj in attributes)
        {            if (obj is DescriptionAttribute)
            {                string description = (obj as DescriptionAttribute).Description;
                Console.WriteLine(description);                break;
            }
        }
    }
}

复制代码

  获取类上面的描述特性:

 Type type = typeof(Student); object[] attributeArray = type.GetCustomAttributes(typeof(DescriptionAttribute), true);//获取指定类型的特性 //Attribute[] attributeArray =Attribute.GetCustomAttributes(type);//获取所有的特性
 ShowDescription(attributeArray);

  获取字段上的描述特性:

复制代码

Type type = typeof(Student);
FieldInfo[] fields= type.GetFields();foreach(FieldInfo field in fields)
{    object[] attributeArray = field.GetCustomAttributes(typeof(DescriptionAttribute), true);//获取指定类型的特性    //Attribute[] attributeArray =Attribute.GetCustomAttributes(field);//获取所有的特性    ShowDescription(attributeArray);    //上面两句代码可用下面代码替换
    field.GetDescripition();//扩展方法显示特性}

复制代码

  获取属性上的描述特性:

复制代码

//获取属性的特性Type type = typeof(Student);
PropertyInfo[] propertyInfos = type.GetProperties();foreach (PropertyInfo property in propertyInfos)
{    object[] attributeArray = property.GetCustomAttributes(typeof(DescriptionAttribute), true);//获取指定类型的特性    //Attribute[] attributeArray =Attribute.GetCustomAttributes(property);//获取所有的特性    ShowDescription(attributeArray);    //上面两句代码可用下面代码替换
    property.GetDescripition();//扩展方法显示特性}

复制代码

  获取枚举上的描述特性:

  获取特定枚举上的特性获取稍微复杂。首先需要获取其类型,然后获取该类型中指定的成员信息,再获取相关的描述特性。在此,使用扩展方法获取枚举的特性描述:

  首先,创建内部访问的函数GetDescription(object[] attributes),用以在有描述特性时返回描述信息,没有描述信息时返回空白:

复制代码

 /// <summary>
 /// 从特性列表中查找属性解释 /// </summary>
 /// <param name="attributes">已知特性列表</param>
 /// <returns>解释</returns>
 private static string GetDescripition(object[] attributes)
 {     if (attributes != null)
     {         foreach (object obj in attributes)
         {             if (obj is DescriptionAttribute)
             {                 return (obj as DescriptionAttribute).Description;
             }
         }
     }     return string.Empty;
 }

复制代码

  其次,建议枚举类型的扩展方法,以支持获取和返回对象的描述信息:

复制代码

/// <summary>/// 获取枚举的标记信息/// </summary>/// <param name="enumValue">枚举值</param>/// <returns>枚举值对应的解释</returns>public static string GetDescripition(this Enum enumValue)
{
    Type type = enumValue.GetType();    if (!type.IsEnum)
    {        throw new ArgumentException("EnumerationValue必须是一个枚举值", "enumValue");
    }
    MemberInfo[] memberInfo = type.GetMember(enumValue.ToString());//获取对应的成员
    if (memberInfo != null && memberInfo.Length > 0)
    {        object[] attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);        string descripition = GetDescripition(attributes);        if (string.IsNullOrWhiteSpace(descripition)==false)
        {            return descripition;
        }
    }   return enumValue.ToString(); 
}

复制代码

  在程序调用时,仅需要如下使用方式:Sex sex = Sex.Man;   sex.GetDescripition();//扩展方法显示特性

3、特性与扩展方法

  以上方法都是在使用时建立相关的静态方法获取特性。实际上,可以对属性、字段等像枚举一样,建立对应的静态方法,以此方便调用,减少代码:

 

复制代码

/// <summary>/// 获取字段的解释/// </summary>/// <param name="fieldInfo">字段信息</param>/// <returns>注释</returns>public static string GetDescripition(this FieldInfo fieldInfo)
{    object[] attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);    string descripition = GetDescripition(attributes);    if(string.IsNullOrWhiteSpace(descripition))
    {        return fieldInfo.Name;
    }    else
    {        return descripition;
    }
}/// <summary>/// 获取属性的解释/// </summary>/// <param name="propertyInfo">属性信息</param>/// <returns>属性的解释</returns>public static string GetDescripition(this PropertyInfo propertyInfo)
{    object[] attributes = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);    string descripition = GetDescripition(attributes);    if (string.IsNullOrWhiteSpace(descripition))
    {        return propertyInfo.Name;
    }    else
    {        return descripition;
    }
}

复制代码

相关用法,在前面已使用到。相关源码下载:https://files.cnblogs.com/files/pilgrim/StudentManage.rar

自定义特性与应用

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP