1. 程式人生 > >自定義特性與應用

自定義特性與應用

TP stat target 一個 計算 如果 得到 for https

  自定義特性允許把自定義元數據與程序元素關聯起來。在.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

自定義特性與應用