C#中Attribute 的基础和自定义使用

2/29/2024 10:38:08 AM
348
0

1.什么是Attribute?

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性

规定特性(Attribute)

规定特性(Attribute)的语法如下:

[attribute(positional_parameters, name_parameter = value, ...)]
element
例如:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
 public class EnumAttribute : Attribute{
}

指定了特性可以使用到字段,一个字段上可以使用1个特性,不能使用多个,可以被继承

特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

其中:

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例如:

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]

Conditional

这个预定义特性标记了一个条件方法,其执行依赖于它顶的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。

规定该特性的语法如下:

[Conditional(conditionalSymbol)]

例如:

 [Conditional("DEBUG")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }

Obsolete

这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)

规定该特性的语法如下:

[Obsolete(message)]
[Obsolete(message, iserror)]

其中:

  • 参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么。
  • 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

下面的实例演示了该特性:

using System;
public class MyClass
{
   [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
   static void OldMethod()
   { 
      Console.WriteLine("It is the old method");
   }
   static void NewMethod()
   { 
      Console.WriteLine("It is the new method"); 
   }
   public static void Main()
   {
      OldMethod();
   }
}

当您尝试编译该程序时,编译器会给出一个错误消息说明:

Don't use OldMethod, use NewMethod instead

创建自定义特性(Attribute)

特性也是一个类,必须继承于System.Attribute类,命名规范为“类名”+Attribute。不管是直接还是间接继承,都会成为一个特性类,特性类的声明定义了一种可以放置在声明之上新的特性。

每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递

下面是一些开发自定义Attribute时,可能需要用到的资料:

【1】Attribute可以关联的元素包括:

      程序集(assembly)、模块(module)、类型(type)、属性(property)、事件(event)、字段(field)、方法(method)、参数(param)、返回值(return)。

【2】AttributeTargets目标包括

标记 说明
All 可以对任何应用程序元素应用属性。
Assembly 可以对程序集应用属性。 
Class 可以对类应用属性。
Constructor 可以对构造函数应用属性。
Delegate 可以对委托应用属性。
Enum 可以对枚举应用属性。
Event 可以对事件应用属性。
Field 可以对字段应用属性。
GenericParameter 可以对泛型参数应用属性。
Interface 可以对接口应用属性。
Method 可以对方法应用属性。
Module Module 指的是可移植的可执行文件(.dll 或 .exe),而非 Visual Basic 标准模块。
Parameter 可以对参数应用属性。
Property 可以对属性 (Property) 应用属性 (Attribute)。
ReturnValue 可以对返回值应用属性。
Struct 可以对结构应用属性,即值类型。

【3】AttributeUsageAttribute中的3个属性(Property)说明:

属性名 说明
ValidOn 该定位参数指定可在其上放置所指示的属性 (Attribute) 的程序元素。AttributeTargets 枚举数中列出了可在其上放置属性 (Attribute) 的所有可能元素的集合。可通过按位“或”运算组合多个 AttributeTargets 值,以获取所需的有效程序元素组合。
AllowMultiple 该命名参数指定能否为给定的程序元素多次指定所指示的属性。
Inherited 该命名参数指定所指示的属性能否由派生类和重写成员继承。

Attribute应用实例

案例1 

这是一个用于Enum类型字段上的Attribute

   /// <summary>
    ///描述枚举的属性
    /// </summary>
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
    public class EnumAttribute : Attribute
    {
        private string _name;
        private string _description;

        /// <summary>
        /// 枚举名称
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// 枚举描述
        /// </summary>
        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">枚举名称</param>
        public EnumAttribute(string name)
        {
            this.Name = name;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">枚举名称</param>
        /// <param name="description">枚举描述</param>
        public EnumAttribute(string name, string description)
        {
            this.Name = name;
            this.Description = description;
        }
    }

使用特性,应用特性到枚举对象

 public enum MsgType
    {
        [Enum("文本")]
        text,
        news,
        video,
        images,
        muiyc
    }

枚举的操作帮助类

    /// <summary>
    /// 提供对枚举操作的一些方法。
    /// </summary>
    public class EnumUtils
    {
        /// <summary>
        /// 用于缓存枚举值的属性值
        /// </summary>
        private static readonly Dictionary<object, EnumAttribute> enumAttr = new Dictionary<object, EnumAttribute>();

        /// <summary>
        /// 获取枚举值的名称,该名称由EnumAttribute定义
        /// </summary>
        /// <param name="value">枚举值</param>
        /// <returns>枚举值对应的名称</returns>
        public static string GetName(Enum value)
        {
            EnumAttribute ea = GetAttribute(value);
            return ea != null ? ea.Name : "";
        }

        /// <summary>
        /// 获取枚举值的名称,该名称由EnumAttribute定义
        /// </summary>
        /// <param name="value">枚举值</param>
        /// <returns>枚举值对应的名称</returns>
        public static string GetDescription(Enum value)
        {
            EnumAttribute ea = GetAttribute(value);
            return ea != null ? ea.Description : "";
        }

        /// <summary>
        /// 利用检举填充控件列表<br/>
        /// 该方法取枚举的名称和值生成ListItem添后添加到列表中
        /// </summary>
        /// <param name="list">控件列表</param>
        /// <param name="enumType">枚举类型</param>
        public static void FillList(ListItemCollection list, Type enumType)
        {
            FillList(list, enumType, null, null);
        }

        /// <summary>
        /// 利用检举填充控件列表<br/>
        /// 该方法取枚举的名称和值生成ListItem添后添加到列表中
        /// </summary>
        /// <param name="list">控件列表</param>
        /// <param name="enumType">枚举类型</param>
        /// <param name="headerValue">列表头的值</param>
        /// <param name="headerText">列表头的显示文字</param>
        public static void FillList(ListItemCollection list, Type enumType, string headerValue, string headerText)
        {
            if (headerValue != null || headerText != null) list.Add(new ListItem(headerText, headerValue));
            Dictionary<string, string> dic = GetValueName(enumType);
            foreach (KeyValuePair<string, string> kv in dic)
            {
                list.Add(new ListItem(kv.Value, kv.Key));
            }
        }

        /// <summary>
        /// 获取枚举类型的 值-名称 列表
        /// </summary>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetValueName(Type enumType)
        {
            Type underlyingType = Enum.GetUnderlyingType(enumType);
            Dictionary<string, string> dic = new Dictionary<string, string>();
            foreach (object o in Enum.GetValues(enumType))
            {
                Enum e = (Enum)o;
                string value = Convert.ChangeType(o, underlyingType).ToString();
                dic.Add(value, GetName(e));
            }
            return dic;
        }

        /// <summary>
        /// 从字符串转换为枚举类型
        /// </summary>
        /// <typeparam name="T">枚举类型(包括可空枚举)</typeparam>
        /// <param name="str">要转为枚举的字符串</param>
        /// <exception cref="Exception">转换失败</exception>
        /// <returns>转换结果</returns>
        public static T GetEnum<T>(string str)
        {
            Type type = typeof(T);

            Type nullableType = Nullable.GetUnderlyingType(type);
            if (nullableType != null) type = nullableType;

            Type underlyingType = Enum.GetUnderlyingType(type);
            object o = Convert.ChangeType(str, underlyingType);

            if (!Enum.IsDefined(type, o)) throw new Exception("枚举类型\"" + type.ToString() + "\"中没有定义\"" + (o == null ? "null" : o.ToString()) + "\"");

            //处理可空枚举类型
            if (nullableType != null)
            {
                ConstructorInfo c = typeof(T).GetConstructor(new Type[] { nullableType });
                return (T)c.Invoke(new object[] { o });
            }
            return (T)o;
        }

        /// <summary>
        /// 从字符串转换为枚举类型
        /// </summary>
        /// <typeparam name="T">枚举类型(包括可空枚举)</typeparam>
        /// <param name="str">要转为枚举的字符串</param>
        /// <param name="defaultValue">转换失败时返回的默认值</param>
        /// <returns>转换结果</returns>
        public static T GetEnum<T>(string str, T defaultValue)
        {
            try
            {
                return GetEnum<T>(str);
            }
            catch
            {
                return defaultValue;
            }
        }

        /// <summary>
        /// 判断是否定义了FlagsAttribute属性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static bool HasFlagsAttribute(Type type)
        {
            object[] attributes = type.GetCustomAttributes(typeof(FlagsAttribute), true);
            return attributes != null && attributes.Length > 0;
        }

        /// <summary>
        /// 判断是否包含指定的值
        /// </summary>
        /// <param name="multValue"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsMarked(Enum multValue, Enum value)
        {
            return (Convert.ToInt32(multValue) & Convert.ToInt32(value)) == Convert.ToInt32(value);
        }

        /// <summary>
        /// 将指定的值拆分为一个枚举值的数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="values"></param>
        /// <returns></returns>
        public static T[] GetValues<T>(Enum values)
        {
            List<T> l = new List<T>();
            foreach (Enum v in Enum.GetValues(typeof(T)))
            {
                if (IsMarked(values, v))
                {
                    l.Add((T)((object)v));
                }
            }
            return l.ToArray();
        }

        /// <summary>
        /// 获取枚举值定义的属性
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static EnumAttribute GetAttribute(Enum value)
        {
            if (enumAttr.ContainsKey(value))
            {
                EnumAttribute ea = enumAttr[value];
                return ea;
            }
            else
            {
                FieldInfo field = value.GetType().GetField(value.ToString());
                if (field == null) return null;
                EnumAttribute ea = null;
                object[] attributes = field.GetCustomAttributes(typeof(EnumAttribute), true);
                if (attributes != null && attributes.Length > 0)
                {
                    ea = (EnumAttribute)attributes[0];
                }
                enumAttr[value] = ea;
                return ea;
            }
        }
    }

获取枚举的描述

EnumUtils.GetDescription(MsgType.text);

全部评论



提问