序言
本文分享一個用鏈式編程思想和泛型實現的擴展方法類,用於減少代碼量,並提供更為清晰的業務邏輯代碼表達。
概念
鏈式編程:將多個業務邏輯(方法)通過“.”(點號)串聯起來的一種代碼風格,形似鏈條,故稱鏈式編程。核心思想在於每個方法均返回自身實例。
泛型:可以理解為是一個類的“籃子“”,只要符合約束的類均可以放置在該“籃子”里面。
擴展方法:向現有類添加方法。
根據泛型和擴展方法的特點,泛型+擴展方法實現了向所有符合約束的“類”添加方法,可減少重復代碼量。
(.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委托。