• 自定义特性与应用


      自定义特性允许把自定义元数据与程序元素关联起来。在.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

    I travel alone,not to be alone.
  • 相关阅读:
    centos7安装zabbix4.2
    python3.x 基础三:文件IO
    python3.x 基础三:字符集问题
    python3.x 基础三:set集合
    python3.x 基础二:内置函数
    python3.x 基础一:dict字典
    python3.x 基础一:str字符串方法
    python3.x 基础一
    [leetcode]Path Sum
    [leetcode]Balanced Binary Tree
  • 原文地址:https://www.cnblogs.com/pilgrim/p/9220863.html
Copyright © 2020-2023  润新知