如何在Attribute中使用自定义属性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Attribute中使用自定义属性相关的知识,希望对你有一定的参考价值。

参考技术A 使用属性可以在设计时对类、properties和方法添加说明,然後在运行时通过反射资讯来检查它们。本文为我们介绍了在开发应用时如何利用C#自定义属性。 本文可以从技术文章下载出获得,其中包含了一个使用定制属性的Visual Studio专案示例档。 属性类是设计时可应用於类、properties和方法的特殊文类。属性类提供描述元素某些方面属性的方式或决定依附於该元素的其他类的行为,进而在运行时可以访问和检验这些描述与行为。你可以将属性类看作为类成员添加特殊修改器的一种方式。 例如,如果你曾经写过Web服务,那肯定知道要使得方法在整个服务中是公开的,必须要使用WebMethod属性。这是一个演示属性应用的很好的例子,因为我们要用WebMethod属性扩展编程模型。C#中没有内建的方式来指定某个方法通过Web服务是可见的(因为内建有表明一个方法是私有的方式),因此需要添加WebMethod属性来满足这一需要。 设计自定义属性 设计自定义属性的过程十分简单,在设计属性前只需要考虑以下几个方面:使用属性的目的是什麼? 属性可以以很多方式使用。你需要定义属性到底要完成什麼功能并确保这些特定功能没有内建在.NET框架集中。使用.NET修改器要比使用属性好,因为这将简化同其他装配件的...

C# 自定义特性(Attribute)详解

什么是特性


特性的定义:公共语言运行时允许添加类似关键字的描述声明,叫做attribute,它对程序中的元素进行标注,如类型、字段、方法、和属性等。attribute和.NetFramework文件的元数据保存在一起,可以用来在运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

如何编写自定义特性


为了帮助大家理解自定义的特性,首先带大家了解一下编译器遇到代码中某个应用了自定义特性时,是如何处理的,以检验Model为例,假如声明一个C#属性,如下

    public class User
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 邮箱
        /// </summary>
        [EmailAttribute]
        public string Email { get; set; }

        /// <summary>
        /// 薪水
        /// </summary>
        [LenghtAttribute(10000, 100000)]
        public decimal Salary { get; set; }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = = false, Inherited = false)]
    public class LenghtAttribute
    {
        private int _minLenght = 0;
        private int _maxLenght = 0;
        public LenghtAttribute(int minLenght, int maxLenght)
        {
            _minLenght = minLenght;
            _maxLenght = maxLenght;
        }
    }

当编译器发现这个属性应用了一个FiedlName特性时,先会将字符串Attribute追加到这个名称上,形成一个组合名称FieldNameAttribute,然后搜索路径的所有名称空间中搜索有指定该名称的类,但是注意,如果编译器发现了有某个特性标记了数据项,并且该名称为Attribute结尾,编译器不会将该字符加到组合名称中,所以这就是我们用特性标注字段是编译器会默认将Attribute字符以浅蓝色字体显示的原因
之后编译器会找到含有该名称的类,且这个类直接或间接派生自System.Attribute。编译器还认为这个类包含控制特性用法的信息。特别是

  1. 特性可以应用到那些类型的程序元素上
  2. 它是否可以多次应用到同一个程序元素上
  3. 特性在应用到类或接口上时,是否由派生类类和接口继承
  4. 这个特性有哪些必选参数和可选参数

如果找不到指定的特性类,或者找到一个特性类,但使用特性的方式和特性类中的信息不匹配,编译器就会产生一个错误,比如特性类指定该特性只能应用于结构上,但我们应用在字段或者类上,就会产生编译错误


一、指定AttributeUsage类

首先,特性类本身是需要用一个特性--system.AttributeUsage特性来标记的,AttributeUsage用来表示我们的特性可以应用在那些类型的程序元素上,这个参数在AttributeUsage是必选参数,类型是枚举类型 AttributeTargets。具体如下


1.Assembly = 0x1,
2.Module = 0x2,
3.Class = 0x4,
4.Struct = 0x8,
5.Enum = 0x10,
6.Constructor = 0x20,
7.Method = 0x40,
8.Property = 0x80,
9.Field = 0x100,
10.Event = 0x200,
11.Interface = 0x400,
12.Parameter = 0x800,
13.Delegate = 0x1000,
14.ReturnValue = 0x2000,
15.GenericParameter = 0x4000,
16.All = 0x7FFF


如果大家想了解枚举对应的类型可以去System.AttributeTargets下看到详细介绍,这里就不给大家一一介绍了
但是大家需要注意,在上面的枚举类型中有两个枚举值不对应任何程序元素, Assembly和Module
特性可以应用到整个程序集或者模块中,而不是应用在代码中的某一个元素上,在这种情况下,这个特性可以放在源代码的任何地方,但需要使用Assembly和Module,写法如下

    public class User
    {
        [Assembly: FieldNameAttribute(Parameters)]
        [Module: FieldNameAttribute(Parameters)]
    }

另外还有两个属性AllowMultiple和Inherited
AllowMultiple表示该特性是否可以多次运用到同一项上,
Inherited表示应用到类或者接口上的特性可以自动应用到所有派生的类和接口上,如果应用到方法和属性上,也可以自动应用到该方法和属性的重写版本上,

二、指定特性参数

接下来给大家介绍如何自定义特性接受的参数。
编译器会检查传递给特性的参数,并查找该特性中带这些参数类型的构造函数,如果编译器找到了这样的构造函数,编译器就会将指定的元数据传递给程序集,反之就会生成一个编译错误。

三、指定特性的可选参数

在AttributeUsaege特性中,可以使用另外一种语法,把可选参数添加到特性中,这种语法指定可选参数的名称和值,它通过特性类中的公共属性和字段起作用。

四、完整代码

  public abstract class BaseAttribute : Attribute
    {
        /// <summary>
        /// 验证
        /// </summary>
        /// <param name="oValue"></param>
        /// <returns></returns>
        public abstract bool Validate(object oValue);
    }
    /// <summary>
    /// 特性扩展
    /// </summary>
    public static class AttributeExtend
    {
        /// <summary>
        /// 验证
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool Validate<T>(T t)
        {
            Type type = t.GetType();
            foreach (var prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(BaseAttribute), true))
                {
                    var oValue = prop.GetValue(t, null);

                    foreach (BaseAttribute item in prop.GetCustomAttributes(typeof(BaseAttribute), true))//获取字段所有的特性
                    {
                        if (!item.Validate(oValue))
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = = false, Inherited = false)]
    public class LenghtAttribute : BaseAttribute
    {
        private int _minLenght = 0;
        private int _maxLenght = 0;
        public LenghtAttribute(int minLenght, int maxLenght)
        {
            _minLenght = minLenght;
            _maxLenght = maxLenght;
        }
        public override bool Validate(object oValue)
        {
            decimal.TryParse(oValue.ToString(), out var minParam);
            decimal.TryParse(oValue.ToString(), out var maxParam);
            if (oValue != null)
            {
                return minParam >= _minLenght && maxParam <= _maxLenght;
            }
            return true;
        }
    }
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = = false, Inherited = false)]
    public class EmailAttribute : BaseAttribute
    {
        public override bool Validate(object oValue)
        {
            if (oValue != null)
            {
                Regex r = new Regex(@"^[A-Za-z0-9u4e00-u9fa5]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$");
                return r.IsMatch(oValue.ToString());
            }
            return true;

        }
    }
     static void Main(string[] args)
        {
            var user = new User { Name = "张三", Email = "1300000@qq.com", Salary = 60000.1M };
            var result = AttributeExtend.Validate<User>(user);
            Console.WriteLine("Hello World!");
            Console.ReadKey();
        }

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

以上是关于如何在Attribute中使用自定义属性的主要内容,如果未能解决你的问题,请参考以下文章

在Attribute Inspector 上显示自定义的控件的属性

Woocommerce在wc属性标签功能中获取属性自定义字段

C# 自定义特性(Attribute)详解

如何在 C# 中创建自定义属性

如何从 React 中的事件对象访问自定义属性?

如何将对象类和自定义属性添加到 LDIF 文件?