.NET开发技术栈: 特性 Attributes

特性介绍

我们经常看到这样的代码

 

   [Serializable]
    class Test
    {

    }

 

这种在类,字段,属性,方法等编程元素上以[ ] 标记的东东就是特性(Attribute)。

写在[] 中的特性其实就是一个类, 该类除了继承自 Attribute类, 与其他的普通类没有什么区别, 有很多自带的特性, 我们也可以通过定义这样的类来定义自己的特性。

特性类约定俗称一般以Attribute结尾, 当以Attribute结尾时,标记的时候可以省略Attribute。

比如 CustomTagAttribute 标记时可以这么写[CustomTag], 而CustomAttributeTag 标记时不能写成 [CustomTag]。

标记了这玩意之后我们可以在程序运行时通过反射读取

  [ClassAttribute("类上的参数")]
   public class Hero
    {
        [PropAttribute("属性上的参数")]
        public string name { get; set; }

        [FieldAttribute("字段上的参数")]
        public int age;

        [MethodAttribute("方法上的参数")]
        public void Attack()
        {
            Console.WriteLine($"{name}发起攻击");
        }
    }

    /// <summary>
    /// 自定义特性
    /// AttributeUsage:在定义特性的时候,对特性的约束
    /// AllowMultiple =true:允许多次标记
    /// Inherited =true:约束当前特性是否可以继承
    /// AttributeTargets.All:当前特性可以标记在所有的元素上
    /// /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
    public abstract class MyAttribute : Attribute
    {
       public string param;
       public abstract void Show();
    }

    public class ClassAttribute : MyAttribute
    {
        public ClassAttribute(string param)
        {
            this.param = param;
        }

        public override void Show()
        {
            Console.WriteLine($"【调用方法】标记在类上的特性, 参数:{param}");
        }
    }

    public class FieldAttribute : MyAttribute
    {
        public FieldAttribute(string param)
        {
            this.param = param;
        }

        public override void Show()
        {
            Console.WriteLine($"【调用方法】标记在字段上的特性, 参数{param}");
        }
    }

    public class PropAttribute : MyAttribute
    {
        public PropAttribute(string param)
        {
            this.param = param;
        }

        public override void Show()
        {
            Console.WriteLine($"【调用方法】标记在属性上的特性, 参数{param}");
        }
    }

    public class MethodAttribute : MyAttribute
    {
        public MethodAttribute(string param)
        {
            this.param = param;
        }

        public override void Show()
        {
            Console.WriteLine($"【调用方法】标记在方法上的特性, 参数{param}");
        }
    }


        static void Main(string[] args)
        {
            var hero = new Hero();

            // 获取标记在类上的特性
            var type = hero.GetType();
            if (type.IsDefined(typeof(MyAttribute), true)) //获取其前先判断
            foreach (MyAttribute attr in type.GetCustomAttributes()) //把所有标记的特性都实例化了
            {
                Console.WriteLine($"【访问字段】传入的参数: {attr.param}"); //访问特性类中的属性,字段
                attr.Show(); //调用特性类中的方法
            }

            //获取标记在属性或字段上的特性
            foreach (PropertyInfo prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(MyAttribute), true))
                {
                    foreach (MyAttribute attr in prop.GetCustomAttributes()) 
                    {
                        Console.WriteLine($"【访问字段】传入的参数: {attr.param}"); 
                        attr.Show(); 
                    }
                }
            }

            //获取标记在方法上的特性
            foreach (MethodInfo method in type.GetMethods())
            {
                if (method.IsDefined(typeof(MyAttribute), true))
                {
                    foreach (MyAttribute attr in method.GetCustomAttributes()) 
                    {
                        Console.WriteLine($"【访问字段】传入的参数: {attr.param}");
                        attr.Show();
                    }
                }
            }
    }

 

特性目标 

可以通过在特性类上标记AttributeUsage特性来限制当前特性可以标记在什么地方, 防止胡乱调用, 该特性可以传一个枚举参数 AttributeTargets, 该枚举的定义如下:

 public enum AttributeTargets
 {
     Assembly    = 0x0001,
     Module      = 0x0002,
     Class       = 0x0004,
     Struct      = 0x0008,
     Enum        = 0x0010,
     Constructor = 0x0020,
     Method      = 0x0040,
    Property    = 0x0080,
    Field       = 0x0100,
    Event       = 0x0200,
    Interface   = 0x0400,
    Parameter   = 0x0800,
    Delegate    = 0x1000,
    All = Assembly │ Module │ Class │ Struct │ Enum │ Constructor │ 
        Method │ Property │ Field │ Event │ Interface │ Parameter │ 
        Delegate,

    ClassMembers  =  Class │ Struct │ Enum │ Constructor │ Method │ 
        Property │ Field │ Event │ Delegate │ Interface,
}

 

总结

特性必须结合反射使用,通过特性我们可以在执行正常的逻辑中额外加入一些信息或者功能,这样可以使程序设计得更灵活, 更具扩展性。

 

posted @ 2020-08-09 18:56  我是张志淼  阅读(165)  评论(0)    收藏  举报