链式编程:泛型实现的扩展方法类
序言
本文分享一个用链式编程思想和泛型实现的扩展方法类,用于减少代码量,并提供更为清晰的业务逻辑代码表达。
概念
链式编程:将多个业务逻辑(方法)通过“.”(点号)串联起来的一种代码风格,形似链条,故称链式编程。核心思想在于每个方法均返回自身实例。
泛型:可以理解为是一个类的“篮子“”,只要符合约束的类均可以放置在该“篮子”里面。
扩展方法:向现有类添加方法。
根据泛型和扩展方法的特点,泛型+扩展方法实现了向所有符合约束的“类”添加方法,可减少重复代码量。
(.Net语言提供了这么优雅的特性,刚开始接触确实让我“惊为天人”!这也许是我对它深耕不辍的原因吧!)
背景
笔者在MES系统的多个模块中,经常要对用户或方法返回值做判断,比如以下判断:
用户输入是否为空或空值;
用户输入的是否为数字或整数;
用户输入的数字是否在正常范围内;
方法返回的是否为True或False;
......
所以便到处充斥着诸如以下的代码:
if (string.IsNullOrEmpty(txtFullScore.Text)) { MessageBox.Show("满分分数不能为空"); return; } if (string.IsNullOrEmpty(txtScore.Text)) { MessageBox.Show("分数不能为空"); return; }
作为一个自认为“自我修养”还过得去的程序猿,肯定不允许程序散发“重复代码的气味”,
所以实现了泛型+扩展方法实现的链式编程风格的类。
特点
该类具有以下优点:
- 该类采用了泛型,可适用于所用引用类型的扩展;
- 验证结果采用委托方法实现,可实现与平台的解耦;
- 各扩展方法都返回自身实例,可实现逻辑表达上更为清晰的链式调用
说明
该类主要分两部分:直接传值-扩展方法;委托-扩展方法。
直接传值-扩展方法
验证值的获取方式有两种:直接获取和调用方法获取
方法列表:
- T IsInt<T>(this T source,string value,Action action)
- T IsNullOrEmpty<T>(this T source, string value, Action action)
- T IsAllNullOrEmpty<T>(this T source, Action action, params string[] value)
- T IsNumeric <T>(this T source, string value, Action action)
- T IsTrue<T>(this T source, bool IsTrue, Action action)
- T OPResult<T>(this T source,bool oPResult, Action<bool> action)
- T Range<T>(this T source, int value, int MinValue, int MaxValue, Action action)
示例代码
方法描述:验证字符串是否为空或空值,验证失败后执行委托。
/// <summary> /// 验证是否为空或空值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">返回实例</param> /// <param name="value">验证字符串</param> /// <param name="action">验证失败后执行该委托</param> /// <returns></returns> public static T IsNullOrEmpty<T>(this T source, string value, Action action) where T : class { if (source == null) return null; if (!string.IsNullOrEmpty(value)) return source; action(); return null; }
委托-扩展方法
先判断扩展方法类是否为NULL后才执行委托,避免由于返回的是NULL却仍然执行委托产生报错的情况。
方法列表
- T IsInt<T>(this T source, Func<T, string > value,Action action)
- T IsNullOrEmpty<T>(this T source, Func<T, string > value, Action action)
- T IsNumeric <T>(this T source, Func<T, string > value, Action action)
- T IsTrue<T>(this T source, Func<T, bool > value IsTrue, Action action)
- T OPResult<T>(this T source, Func<T, bool > value oPResult, Action<bool> action)
- T Range<T>(this T source, Func<T,int> value, int MinValue, int MaxValue, Action action)
示例代码
方法描述:验证委托的返回值是否为空或空值,验证失败后执行委托。
/// <summary> /// 验证是否为空或空值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">返回实例</param> /// <param name="value">需验证委托</param> /// <param name="action">验证失败后执行该委托</param> /// <returns></returns> public static T IsNullOrEmpty<T>(this T source, Func<T, string> value, Action action) where T : class { if (source == null) return null; if (!string.IsNullOrEmpty(value(source))) return source; action(); return null; }
调用示例
现有一个需求判断分数是否及格,采用WinForm实现。具体需求如下:
满分不能小于60大于100;分数不能小于等于0;当结果大于等于1时,提示PASS,当结果小于1时,提示NG;当输入错误时,需要提示用户。
窗体设计如下图所示:
需求分析:
该需求采用如下验证流程:
→满分不能为空
→分数不能为空
→满分必须为整数
→分数必须为整数
→满分必须在60-100之间
→分数不能小于等于0
→计算结果,结果大于等于0.6,提示PASS,否则提示NG。
一般代码写法
if (string.IsNullOrEmpty(txtFullScore.Text)) { MessageBox.Show("满分分数不能为空"); return; } if (string.IsNullOrEmpty(txtScore.Text)) { MessageBox.Show("分数不能为空"); return; } int temp = 0; if (!int.TryParse(txtFullScore.Text, out temp)) { MessageBox.Show("满分必须为整数"); return; } if (!int.TryParse(txtScore.Text, out temp)) { MessageBox.Show("分数必须为整数"); return; } if (Convert.ToInt32(txtFullScore.Text)<60||Convert.ToInt32(txtScore.Text)>100) { MessageBox.Show("满分必须在60-100范围内"); return; } if (Convert.ToInt32(txtScore.Text) < 0) { MessageBox.Show("分数不能小于等于0"); return; } if (Calc(Convert.ToInt32(txtFullScore.Text), Convert.ToInt32(txtScore.Text))) { MessageBox.Show("PASS"); } else { MessageBox.Show("NG"); }
泛型+扩展方法的链式风格写法
this.IsNullOrEmpty(txtFullScore.Text,()=> MessageBox.Show("满分不能为空")) .IsNullOrEmpty(txtScore.Text,()=>MessageBox.Show("分数不能为空")) .IsInt(txtFullScore.Text,()=>MessageBox.Show("满分必须为整数")) .IsInt(txtScore.Text,()=>MessageBox.Show("分数必须为整数")) .Range(t=>Convert.ToInt32(txtFullScore.Text),60,100,()=>MessageBox.Show("满分必须在60-100范围内")) .IsTrue(t=>Convert.ToInt32(txtScore.Text)>0,()=>MessageBox.Show("分数不能小于等于0")) .OPResult(t=>t.Calc(Convert.ToInt32(txtScore.Text),Convert.ToInt32(txtFullScore.Text)), (result)=>{if(result) MessageBox.Show("PASS");else MessageBox.Show("NG");});
计算结果Calc的代码如下
public bool Calc(int Score, int FullScore) { return (float)Score / FullScore >= 0.6; }
结论
链式风格写法从代码逻辑上看完全与需求流程一致,逻辑表达上更为清晰。
建议
使用Action委托可以自行设定验证通过/不通过时所要进行的操作,如MessageShow,也可写入文件或数据库,或者其他方法,实现解耦。
建议采用工厂方法实现ActionFactory管理所有的Action委托。