C#进阶之特性(Attribute)

相信只要是用过.net MVC的开发都用过特性,例如最基础的HttpMethodAttribute特性,用来给接口进行请求方式限定的,等等诸如此类的特性,数不胜数。

那么什么是特性?

特性就是一个类,直接或者间接继承自Attribute,特性本身没有任何作用

特性的使用方法有两种:

        [Range(0,10)]
        [Test]
        ///分开单独使用
        public int Hight { set; get; }
        [Test, Range(0, 10)]
        ///合并使用,通过逗号分隔
        public string Name { set; get; }

attribute的使用规则,一般通过在特性类上使用AttributeUsage特性来进行设置

这个特性有三个属性:

AllowMultiple:是否允许重复使用;

Inherited:是否可以被继承;

构造函数有AttributeTargets类型的参数,主要用来设置特性的使用场景(类、接口、属性、字段……)

特性的使用场景:

用来做标记,通过判断类、属性……是否有这个特性,来进行一些业务逻辑上的判断,比如:

具体是通过System.Reflection程序集提供的反射功能来进行特性获取,

譬如判断一个类型是否具有某个特性,使用的是IsDefined方法,

获取一个类的所有特性,使用的是GetCustomAttributes方法,获取的是一个object[],可以通过as进行强转为需要判断的类型;

1、一些预设的特性

ObsoleteAttribute:可以通过添加这个特性,对类、属性、接口等进行代码过期设置;

ConditionalAttribute:通过在方法上设置这个特性,可以屏蔽掉对该方法的所有调用(不过支持返回值是void的方法,毕竟只有这样才不会有在调用的时候有上下文);

经常看到错误日志里面有很详细的信息,具体到了某某文件多少行哪个方法出错等等,其实微软也提供了对应的特性,支持我们自己查看这些内容:

/// <summary>
        /// 
        /// </summary>
        /// <param name="filePath">调用的文件物理路径</param>
        /// <param name="num">被调用的行</param>
        /// <param name="name">调用的方法</param>
        public void Call2(string str,
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int num = 0,
            [CallerMemberName] string name = "")
        {
        }

2、接口的模型绑定属性验证

 可以通过继承ValidationAttribute,并重写IsValid方法即可实现自定义的属性绑定校验。

/// <summary>
    ///  
    /// </summary>
    [AttributeUsage(AttributeTargets.All,AllowMultiple = true,Inherited = true)]
    public class RangeAttribute : ValidationAttribute
    {
        public RangeAttribute(int minNum, int maxNum)
        {
            MaxNum = maxNum;
            MinNum = minNum;
        }

        public int? MaxNum { set; get; }
        public int? MinNum { set; get; }
        public override bool IsValid(object value)
        {
            var result = false;
            if (MaxNum != null)
                result = (int)value < MaxNum;
            if (MinNum != null)
                result = result && (int)value > MinNum;
            return result;
        }
    }

3、可以用来做属性的标记

举个例子,比如我想要对一个列表进行多个字段的检索,比如这个实体

/// <summary>
    /// 全局搜索查询demo实体
    /// </summary>
    public class SerachEntity
    {
        /// <summary>
        /// 姓名
        /// </summary>
        [GlobalSerach]
        [Export("姓名")]
        public string Name { set; get; }
        /// <summary>
        /// 中文名
        /// </summary>
        [GlobalSerach]
        [Export("中文名")]
        public string CName { set; get; }
        /// <summary>
        /// 英文名
        /// </summary>
        [GlobalSerach]
        [Export("英文名")]
        public string EName { set; get; }
        /// <summary>
        /// 地址
        /// </summary>
        [GlobalSerach]
        [Export("地址")]
        public string Address { set; get; }
        /// <summary>
        /// IP
        /// </summary>
        [Export("IP地址")]
        public string IP { set; get; }
    }
    /// <summary>
    /// 全局搜索的标记特性
    /// </summary>
    public class GlobalSerachAttribute : Attribute
    { 
        
    }

我想要对这些属性进行一个综合的查询

那么不可能来一个需求我就写一个where吧,这样并不符合封装的思想,所以我需要做的就是寻找共同点,进行封装

public static class BaseSerach 
    {
        private static List<BinaryExpression> exps = new List<BinaryExpression>();
        private static ParameterExpression m_Parameter = null;
        /// <summary>
        /// where全局查找语句
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="iEnumerables"></param>
        /// <param name="str"></param>
        /// <returns></returns>
        public static IEnumerable<T> WhereByGlobalSerach<T>(this IEnumerable<T> iEnumerables, string str)
            where T : class
        {
            m_Parameter = Expression.Parameter(typeof(T), "x");
            var propertys = typeof(T).GetProperties().Where(t => !(t.IsDefined(typeof(SerializableAttribute), true))).ToList();
            propertys.ForEach(t=> {
                MemberExpression member = Expression.PropertyOrField(m_Parameter, t.Name);
                Expression exp1 = Expression.NotEqual(member, Expression.Constant(null));//判断是否为null
                Expression exp2 = Expression.Call(member, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(str));
                exps.Add(Expression.AndAlso(exp1, exp2));
            });
            return iEnumerables.Where(GetLambda<T>());

        }
        /// <summary>
        /// 获取表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private static Func<T, bool> GetLambda<T>()
            where T : class
        {
            Expression whereExpr = null;
            foreach (var expr in exps)
            {
                if (whereExpr == null)
                    whereExpr = expr;
                else
                    whereExpr = Expression.Or(whereExpr, expr);
            }
            if (whereExpr == null)
                return null;
            return Expression.Lambda<Func<T, bool>>(whereExpr, m_Parameter).Compile();
        }
    }

这里的方法就可以用来进行泛型的普遍属性检索,

调用起来也很简单

            var beforeList = new List<SerachEntity>() { new SerachEntity() {Name="余成" },new SerachEntity() {Name="余承浩" } };
            var lastList = beforeList.WhereByGlobalSerach("");

我感觉这就是特性最好用的一点了,用于标记。

原文地址:https://www.cnblogs.com/yuchenghao/p/12210708.html