一、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, Func bool> 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(); } } }
来看下运行结果
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