.NET无处不在的特性(Attribute)2-简单自定义

上篇文章“.NET无处不在的特性(Attribute)1-探究”讲了特性的探究, 这篇文章介绍自定义特性。

    在以前传统的开发中,后端也需要做很多的验证,比如不为空验证、长度验证、电话号码验证等,那么我们还是以上面的案例简单的自定义特性。

要创建自定义特性,首先我们了解一下特性的基本使用步骤:.
1、新建一个类,继承Attribute
这是创建特性的必要条件,必须继承Attribute了,或者父类是Attribute。
    public class RequiredAttribute : Attribute    {    }
2、配置特性的预定义特性
文件建好后可以配置预定义特性,也就是特性的特性,它是AttributeUsage特性。
    AttributeUsage描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型,AttributeUsage可以配置三个参数,第一个参AttributeTargets 规定特性可被放置的语言元素,比如类,属性,方法,参数等,具体可以查询;它是枚举器 AttributeTargets 的值的组合,默认值是 AttributeTargets.All,多个参数用“|”隔开。第二个参数是AllowMultiple,它是个布尔值,是否允许该特性多用,默认为false,是可选参数。第三个参数是inherited,是否可以被派生类继承,默认值是fasle,是可选参数。使用案例如下:
[AttributeUsage(AttributeTargets.All|AttributeTargets.Class//可以指定多个,AllowMultiple=true,Inherited =false)]
3、编写和实现验证方法
我们这里编写非空验证和字符串长度的验证。

​​​​

    public class RequiredAttribute : Attribute    {        //我们这里参数用object,便于通用性,但是如果是字符串需要特殊处理。        public bool Verification(object proValue)        {            if (proValue == null)            {                return false;            }            else                return true;        }    }     //这里为了传递字符的限制长度,可以用构造函数传入参数,使用特性也需要加上参数    public class StringLengthAttribute : Attribute    {        int _min = 0;int _max = 10;        //参数默认值防止参数报错        public StringLengthAttribute(int max = 10, int min = 0)        {            _min = min;_max = max;        }        public  bool Verification(object proValue)        {            if (proValue == null|| proValue.ToString().Length < this._min            || proValue.ToString().Length > this._max)                        return false                   else                return true;        }    }    //使用    
在属性中使用
        [Required]//非空验证        [StringLength(20,3)]//字数长度验证        public string name { get; set; }
4、通过反射来执行验证
上面方法实现了,怎么验证呢?也就是执行这个特性方法,上面有讲到可以用反射获取调用方法,然后运行判断,我们改造一下上面的方法,用扩展方法实现,实现如下:
 public static bool IsVerification(this object obj)        {            Type type = obj.GetType();//反射出类型            var pro = type.GetProperties();//获取属性            foreach (var property in pro)            {                //是否包含属性                if (property.IsDefined(typeof(RequiredAttribute), true))                {                    var propertyValue = property.GetValue(obj);//获取属性值                    //实例化验证类                    var attribute = property.GetCustomAttribute<RequiredAttribute>();                    //调用验证方法                    bool rel = attribute.Verification(propertyValue);                    if (!rel)                    {                        return false;                    }                }                if (property.IsDefined(typeof(StringLengthAttribute), true))                {                    var propertyValue = property.GetValue(obj);                    var attribute = property.GetCustomAttribute<StringLengthAttribute>();                    bool rel = attribute.Verification(propertyValue);                    if (!rel)                    {                        return false;                    }                }            }            return true;        }

调用如下代码:

         Customer customer = new Customer()            {                id = 2,                name = "张学友",                age = 52,                email = "zxy@net.cn"            };            var rel= customer.IsVerification();   //结果返回为true,如果name赋值为空则返回为false

5、代码重构和优化

重构特性验证类

    验证的特性可能有多个,我们可以把验证的特性抽象化,这样可以统一配置、可以在父类创立公共方法等,如下。

    /// <summary>    ///抽象类,可以配置特性的属性,    /// </summary>    [AttributeUsage(AttributeTargets.All,AllowMultiple=true,Inherited =false)]    public abstract class AbstractAttribute: Attribute    {        public abstract bool Verification(object proValue);    }    public class RequiredAttribute : AbstractAttribute    {        //我们这里参数用object,便于通用性,但是如果是字符串需要特殊处理。        public override bool Verification(object proValue)        {            if (proValue == null)            {                return false;            }            else                return true;        }        //这里为了传递字符的限制长度,可以用构造函数传入参数,使用特性也需要加上参数        public class StringLengthAttribute : AbstractAttribute        {            int _min = 0;            int _max = 10;            public StringLengthAttribute(int max = 10, int min = 0)            {                _min = min; _max = max;            }            public override bool Verification(object proValue)            {                if (proValue == null                || proValue.ToString().Length < this._min                || proValue.ToString().Length > this._max)                    return false;                else                    return true;
            }        }    }

优化调用的验证方法

    反射的验证方法如果有多个那么我们需要判断多次,还需要输入特性类名来反射方法,我们可以把验证方法改成通用的,而且通过反射来获取特性验证类的名称,如下:

    public static bool IsVerification(this object obj)    {        Type type = obj.GetType();//反射出类型        var pro = type.GetProperties();//获取属性        foreach (var property in pro)        {            var Attributess = property.GetCustomAttributes();            foreach (var item in Attributess)            {                if (property.IsDefined(item.GetType(), true))                {                    object[] propertyValues = { property.GetValue(obj) };                    var funcs = ((System.Reflection.TypeInfo)item.GetType()).DeclaredMethods;                    foreach (MethodInfo item2 in funcs)                    {                        var rel= item2.Invoke(item, propertyValues);                        if (!(bool)rel)                        { return (bool)rel; }                    }                }            }        }        return true;    }

上面的的重构和优化还有空间,比如反射效率问题,大家可以留言发表自己的重构优化建议。

源码:https://pan.baidu.com/s/1yC3uz8sMzq38oz_BwBNfFA?pwd=o113

提取码:o113

结语

这篇文章写了3天终于写完了,主要介绍了特性和自定义特性及优化。本文通过特性优化用到了oop的思想。写作水平有限,希望本文对大家学习和工作有一定参考价值,谢谢大家的支持。