【学习笔记】Linq和特性(使用自定义特性和反射来实现统一参数校验和获取枚举描述)

一、Linq

下面我们来看个示例,自定义一个Linq,实现Where条件筛选

using System;
using System.Collections.Generic;

namespace MyLinqAttribute
{
    /// 
    /// 含有yield的函数说明它是一个生成器,而不是普通的函数。
    /// 当程序运行到yield这一行时,该函数会返回值,并保存当前域的所有变量状态;
    /// 等到该函数下一次被调用时,会从上一次中断的地方开始执行,一直遇到下一个yield, 程序返回值, 并在此保存当前状态;如此反复,直到函数正常执行完成。
    /// 
    /// 迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,它是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。
    /// 简单来说,迭代器模式使得你能够获取到序列中的所有元素,而不用关心其类型是array,list,linked list或者是其他什么序列结构。
    /// 这一点使得能够非常高效的构建数据处理通道(data pipeline),即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是Linq的核心模式。
    /// 在.NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接 口,那么就能够被迭代;
    /// 调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,它是数据序列中的一个位置记录。
    /// 迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。
    /// 
    public static class ExtendMethod
    {
        /// 
        /// Linq是基于委托的封装,实现逻辑解耦、代码重用
        ///     1、把重复的代码写在这里,把变化的部分通过委托传递
        ///     2、泛型完成对不同类型的数据过滤
        ///     3、迭代器模式,完成了数据的按需获取,延迟加载
        /// 
        public static IEnumerable MyWhere(this IEnumerable resource, Funcbool> func)
        {
            foreach (var item in resource)
            {
                Console.WriteLine("进入数据检测"); //写好公共逻辑
                if (func.Invoke(item)) //可变部分交给委托实现逻辑解耦
                {
                    yield return item;//yield跟IEnumerable配对使用(迭代器模式)
                }
            }
        }
    }
}

二、特性

 特性的作用:提供额外的信息、提供额外的行为。

1、首先来看下AttributeUsage中的Inherited是什么意思,我们直接通过官方文档提供的Demo来理解

//官方文档地址:https://docs.microsoft.com/en-us/dotnet/api/system.attributeusageattribute.inherited?view=netframework-4.7.2

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method |
                AttributeTargets.Property | AttributeTargets.Field,
                Inherited = true)]
public class InheritedAttribute : Attribute
{ }

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method |
                AttributeTargets.Property | AttributeTargets.Field,
                Inherited = false)]
public class NotInheritedAttribute : Attribute
{ }

[InheritedAttribute]
public class BaseA
{
    [InheritedAttribute]
    public virtual void MethodA()
    { }
}

public class DerivedA : BaseA
{
    public override void MethodA()
    { }
}

[NotInheritedAttribute]
public class BaseB
{
    [NotInheritedAttribute]
    public virtual void MethodB()
    { }
}

public class DerivedB : BaseB
{
    public override void MethodB()
    { }
}

public class Example
{
    public static void Main()
    {
        Type typeA = typeof(DerivedA);
        Console.WriteLine($"DerivedA has Inherited attribute: {typeA.GetCustomAttributes(typeof(InheritedAttribute), true).Length > 0}");
        MethodInfo memberA = typeA.GetMethod(nameof(DerivedA.MethodA));
        Console.WriteLine($"DerivedA.MemberA has Inherited attribute: {memberA.GetCustomAttributes(typeof(InheritedAttribute), true).Length > 0}\n");

        Type typeB = typeof(DerivedB);
        Console.WriteLine($"DerivedB has NotInherited attribute: {typeB.GetCustomAttributes(typeof(NotInheritedAttribute), true).Length > 0}");
        MethodInfo memberB = typeB.GetMethod(nameof(DerivedB.MethodB));
        Console.WriteLine($"DerivedB.MemberB has NotInherited attribute: {memberB.GetCustomAttributes(typeof(NotInheritedAttribute), true).Length > 0}");
    }
}
// The example displays the following output:
//       DerivedA has Inherited attribute: True
//       DerivedA.MemberA has Inherited attribute: True
//       
//       DerivedB has NotInherited attribute: False
//       DerivedB.MemberB has NotInherited attribute: False

2、提供额外的行为(例如:使用自定义特性和反射来实现统一参数校验)

下面我们直接通过代码来看下如何实现:

首先自定义参数校验特性

/// 
/// Json消息码
/// 
[Serializable]
public class JsonCode
{
    /// 
    /// 状态
    /// 
    public int code { get; set; } = 0;

    /// 
    /// 消息
    /// 
    public string msg { get; set; }

    /// 
    /// 数据
    /// 
    public dynamic data { get; set; }
}
/// 
/// 抽象校验特性
/// 
public abstract class AbstractValidateAttribute : Attribute
{
    /// 
    /// 校验方法
    /// 
    /// 
    /// 需要校验的值
    /// 
    /// 
    /// 校验结果
    /// 
    public abstract JsonCode Validate(object value);
}

/// 
/// Email校验特性
/// 
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] //Inherited:修饰的特性子类是否生效
public class EmailAttribute : AbstractValidateAttribute
{
    /// 
    /// 自定义错误描述
    /// 
    private string _errorMsg;

    public EmailAttribute(string errorMsg = "")
    {
        _errorMsg = errorMsg;
    }

    /// 
    /// 实现校验逻辑
    /// 
    /// 
    /// 
    public override JsonCode Validate(object value)
    {
        var jsonCode = new JsonCode
        {
            code = 1,
            msg = "校验成功"
        };

        if (value == null || string.IsNullOrEmpty(value.ToString()))
        {
            return jsonCode;
        }

        var reg = new Regex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
        if (!reg.IsMatch(value.ToString()))
        {
            jsonCode.code = -1;
            jsonCode.msg = string.IsNullOrEmpty(_errorMsg) ? "Email格式错误" : _errorMsg;
        }

        return jsonCode;
    }
}

/// 
/// 手机号校验特性
/// 
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class MobileAttribute : AbstractValidateAttribute
{
    /// 
    /// 自定义错误描述
    /// 
    private string _errorMsg;

    public MobileAttribute(string errorMsg = "")
    {
        _errorMsg = errorMsg;
    }

    /// 
    /// 实现校验逻辑
    /// 
    /// 
    /// 
    public override JsonCode Validate(object value)
    {
        var jsonCode = new JsonCode
        {
            code = 1,
            msg = "校验成功"
        };

        if (value == null || string.IsNullOrEmpty(value.ToString()))
        {
            return jsonCode;
        }

        var reg = new Regex(@"^1[3456789]{1}\d{9}$");
        if (!reg.IsMatch(value.ToString()))
        {
            jsonCode.code = -1;
            jsonCode.msg = string.IsNullOrEmpty(_errorMsg) ? "手机号码格式错误" : _errorMsg;
        }

        return jsonCode;
    }
}

/// 
/// 非空校验特性
/// 
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class RequiredAttribute : AbstractValidateAttribute
{
    /// 
    /// 自定义错误描述
    /// 
    private string _errorMsg;

    public RequiredAttribute(string errorMsg = "")
    {
        _errorMsg = errorMsg;
    }

    /// 
    /// 实现校验逻辑
    /// 
    /// 
    /// 
    public override JsonCode Validate(object value)
    {
        var jsonCode = new JsonCode
        {
            code = 1,
            msg = "校验成功"
        };

        if (value == null || string.IsNullOrEmpty(value.ToString()))
        {
            jsonCode.code = -1;
            jsonCode.msg = string.IsNullOrEmpty(_errorMsg) ? "必填项不能为空" : _errorMsg;
            return jsonCode;
        }

        return jsonCode;
    }
}

接着封装用于参数校验的扩展方法

/// 
/// 特性扩展
/// 
public static class ValidateExtensions
{
    /// 
    /// 参数校验
    /// 
    public static JsonCode Validate(this T t)
    {
        Type type = t.GetType();
        foreach (var prop in type.GetProperties())
        {
            if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
            {
                object oValue = prop.GetValue(t);
                foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
                {
                    var result = attribute.Validate(oValue);
                    if (result.code != 1)
                    {
                        return result;
                    }
                }
            }
        }

        return new JsonCode()
        {
            code = 1,
            msg = "校验成功"
        };
    }

    /// 
    /// 参数校验
    /// 
    /// 
    /// 校验不通过的JsonCode消息集合
    /// 
    public static List ValidateAll(this T t)
    {
        List validates = new List();
        Type type = t.GetType();
        foreach (var prop in type.GetProperties())
        {
            if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
            {
                object oValue = prop.GetValue(t);
                foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
                {
                    var result = attribute.Validate(oValue);
                    if (result.code != 1)
                    {
                        validates.Add(result);
                    }
                }
            }
        }

        return validates;
    }
}

然后在需要校验的地方打上对应的特性标签

/// 
/// 实体基类
/// 
public class EntityBase
{
    [Required("Id不能为空")]
    public string Id { get; set; } = Guid.NewGuid().ToString("N");
}

/// 
/// 学生类
/// 
public class Student : EntityBase
{
    /// 
    /// 学号
    /// 
    [Required("学号不能为空")]
    public string SNo { get; set; }

    /// 
    /// 姓名
    /// 
    [Required("姓名不能为空")]
    public string Name { get; set; }

    /// 
    /// 手机号
    /// 
    [Mobile]
    [Required("手机号不能为空")]
    public string Mobile { get; set; }

    /// 
    /// 电子邮箱
    /// 
    [Email]
    [Required("电子邮箱不能为空")]
    public string Email { get; set; }
}

最后调用校验的扩展方法

using System;

namespace MyLinqAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            var stu = new Student
            {
                SNo = "123456",
                Name = "张三",
                Mobile = "111"
            };
            var jsonCode = stu.Validate();
            Console.WriteLine($"code:{jsonCode.code},msg:{jsonCode.msg}");
            Console.ReadKey();
        }
    }
}

来看下运行结果

【学习笔记】Linq和特性(使用自定义特性和反射来实现统一参数校验和获取枚举描述)_第1张图片

3、提供额外的信息(例如:使用自定义特性和反射来实现获取枚举描述)

首先自定义枚举描述特性

/// 
/// 自定义枚举描述特性
/// 
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
public class EnumDescriptionAttribute : Attribute
{
    /// 
    /// 描述
    /// 
    public string Desc { get; set; }

    /// 
    /// 构造函数
    /// 
    /// 描述
    public EnumDescriptionAttribute(string desc)
    {
        this.Desc = desc;
    }
}

然后封装枚举帮助类

/// 
/// 枚举帮助类
/// 
public static class EnumHelper
{
    #region 获取指定字段上的自定义特性标签的描述(基础方法)

    /// 
    /// 获取指定字段上的自定义特性标签的描述(基础方法)
    /// 
    /// 字段
    /// 分隔符
    /// 返回指定字段上的自定义特性标签的描述
    public static string GetDescription(this FieldInfo fi, char splitChar = ',')
    {
        var desc = string.Empty; //字段上特性标签的描述
        Type typeEnumDescription = typeof(EnumDescriptionAttribute); //自定义枚举描述特性
        if (fi == null || !fi.IsDefined(typeEnumDescription, false)) //是否定义了特性
        {
            return desc;
        }

        object[] objs = fi.GetCustomAttributes(typeEnumDescription, false);//获取指定特性
        if (objs == null || objs.Length <= 0)
        {
            return desc;
        }

        //获取私有或者公开的实例属性
        PropertyInfo pi = typeEnumDescription.GetProperty("Desc", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
        if (pi == null)
        {
            return desc;
        }

        EnumDescriptionAttribute[] arr = objs as EnumDescriptionAttribute[];
        foreach (EnumDescriptionAttribute item in arr) //遍历获取标签描述
        {
            desc += pi.GetValue(item, null) + splitChar.ToString();
        }
        desc = desc.TrimEnd(splitChar);

        return desc;
    }

    #endregion 获取指定字段上的自定义特性标签的描述(基础方法)END

    #region 获取枚举描述(基础方法TEnum)

    /// 
    /// 获取枚举描述(基础方法TEnum)
    /// 
    /// 枚举类型
    /// 枚举值,例如Gender.Male
    /// 分隔符
    /// 返回枚举描述
    public static string GetDescription(this TEnum enumValue, char splitChar = ',')
    {
        Type t = enumValue.GetType();
        FieldInfo fi = t.GetField(enumValue.ToString());

        return fi.GetDescription(splitChar);
    }

    #endregion 获取枚举描述(基础方法TEnum)END

    #region 获取枚举描述(long)

    /// 
    /// 获取枚举描述
    /// 
    /// 枚举类型
    /// 枚举值
    /// 分隔符
    /// 返回枚举描述
    public static string GetDescription(this long enumValue, char splitChar = ',')
    {
        TEnum e = (TEnum)Enum.Parse(typeof(TEnum), enumValue.ToString());
        return GetDescription(e, splitChar);
    }

    #endregion 获取枚举描述(long)END

    #region 获取枚举描述(int)

    /// 
    /// 获取枚举描述
    /// 
    /// 枚举类型
    /// 枚举值
    /// 分隔符
    /// 返回枚举描述
    public static string GetDescription(this int enumValue, char splitChar = ',')
    {
        TEnum e = (TEnum)Enum.Parse(typeof(TEnum), enumValue.ToString());
        return GetDescription(e, splitChar);
    }

    #endregion 获取枚举描述(int)END

    #region 获取枚举描述(Enum)

    /// 
    /// 获取枚举描述
    /// 
    /// 枚举值,例如Gender.Male
    /// 分隔符
    /// 返回枚举描述
    public static string GetDescription(this Enum enumValue, char splitChar = ',')
    {
        return GetDescription(enumValue, splitChar);
    }

    #endregion 获取枚举描述(Enum)END

    #region 获取枚举值+枚举项字典

    /// 
    /// 获取枚举值+枚举项字典
    /// 
    /// 枚举类型
    /// 返回枚举值+枚举项字典
    public static Dictionary<int, string> GetEnumValueItemDictionary()
    {
        var dicEnum = new Dictionary<int, string>(); //key:枚举值,value:枚举项
        Type t = typeof(TEnum);
        FieldInfo[] fis = t.GetFields(BindingFlags.Static | BindingFlags.Public); //所有枚举成员
        if (fis == null || fis.Length <= 0)
        {
            return dicEnum;
        }

        //遍历枚举所有成员获取枚举值及枚举项
        foreach (var fi in fis)
        {
            if (!fi.FieldType.IsEnum) //是否是枚举字段
            {
                continue;
            }

            var key = (int)fi.GetValue(null); //枚举值
            if (!dicEnum.ContainsKey(key))
            {
                dicEnum.Add(key, fi.Name);
            }
        }

        return dicEnum;
    }

    #endregion 获取枚举值+枚举项字典END

    #region 获取枚举值+描述字典

    /// 
    /// 获取枚举值+描述字典
    /// 
    /// 枚举类型
    /// 分隔符
    /// 返回枚举值+描述字典
    public static Dictionary<int, string> GetEnumValueDescDictionary(char splitChar = ',')
    {
        var dicEnum = new Dictionary<int, string>(); //key:枚举值,value:枚举描述
        Type t = typeof(TEnum);
        FieldInfo[] fis = t.GetFields(BindingFlags.Static | BindingFlags.Public); //所有枚举成员
        if (fis == null || fis.Length <= 0)
        {
            return dicEnum;
        }

        //遍历枚举所有成员获取枚举值及描述
        foreach (var fi in fis)
        {
            if (!fi.FieldType.IsEnum) //是否是枚举字段
            {
                continue;
            }

            var key = (int)fi.GetValue(null); //枚举值
            if (!dicEnum.ContainsKey(key))
            {
                //fi.GetDescription(splitChar):枚举描述
                dicEnum.Add(key, fi.GetDescription(splitChar));
            }
        }

        return dicEnum;
    }

    #endregion 获取枚举值+描述字典END

    #region 获取枚举项+描述字典

    /// 
    /// 获取枚举项+描述字典
    /// 
    /// 枚举类型
    /// 分隔符
    /// 返回枚举项+描述字典
    public static Dictionary<string, string> GetEnumItemDescDictionary(char splitChar = ',')
    {
        var dicEnum = new Dictionary<string, string>(); //key:枚举项,value:枚举描述
        Type t = typeof(TEnum);
        FieldInfo[] fis = t.GetFields(BindingFlags.Static | BindingFlags.Public); //所有枚举成员
        if (fis == null || fis.Length <= 0)
        {
            return dicEnum;
        }

        //遍历枚举所有成员获取枚举项及描述
        foreach (var fi in fis)
        {
            if (!fi.FieldType.IsEnum) //是否是枚举字段
            {
                continue;
            }

            if (!dicEnum.ContainsKey(fi.Name))
            {
                //fi.GetDescription(splitChar):枚举描述
                dicEnum.Add(fi.Name, fi.GetDescription(splitChar));
            }
        }

        return dicEnum;
    }

    #endregion 获取枚举项+描述字典END
}

具体的使用都是类似的,应该大家都懂,此处就不在演示了。

 

Demo源码:

链接:https://pan.baidu.com/s/1BxV-y88jRHx5HCeWHh7UsA 
提取码:ebjd

 

你可能感兴趣的:(【学习笔记】Linq和特性(使用自定义特性和反射来实现统一参数校验和获取枚举描述))