查看公司項目代碼時,存在這樣一個問題:winform界面上有很多信息填寫,提交后台服務器更新,但數據的合法驗證及值的轉換卻不太敢恭維,一堆的if判斷和轉換,便想着是否能擴展個方法出來,琢磨出個思路,記錄下來與大家共同探討,有不對的地方還請大家指正。
設計思路:
1. 由於大部分從TextBox控件中獲取數據值,可以擴展個泛型方法出來,直接根據轉換后的數據類型獲得值,類似這樣,
var value = this.txtSample.GetValue<int>();
2. 可以傳入一個委托用來處理轉換失敗的操作,並重載此方法,提供一個默認的操作。
好,下面開工:
1. 創建TextBox類型的擴展方法
引用MSDN的解釋:擴展方法使您能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。 對於用 C# 和 Visual Basic 編寫的客戶端代碼,調用擴展方法與調用在類型中實際定義的方法之間沒有明顯的差異。
擴展方法被定義為靜態方法,但它們是通過實例方法語法進行調用的。 它們的第一個參數指定該方法作用於哪個類型,並且該參數以 this 修飾符為前綴。 僅當您使用 using 指令將命名空間顯式導入到源代碼中之后,擴展方法才位於范圍中。
注意:擴展方法是在非嵌套、非泛型靜態類內部定義的
2. 由於轉換類型未知,但為值類型,故采用泛型方法設計,並加上strut泛型約束,由於允許自定義處理轉換失敗時的操作,故傳入一個Action委托來實現,如下:
public static TResult GetValue<TResult>(this TextBox textBox, Action<TextBox> failed) where TResult : struct { var type = typeof(TResult); var method = type.GetMethod("TryParse", new Type[] { typeof(string), type.MakeByRefType() }); var parameters = new object[] { textBox.Text, default(TResult) }; // 若轉換失敗,執行failed if (!(bool)method.Invoke(null, parameters)) { failed(textBox); throw new InvalidCastException("輸入值格式不正確,請檢查輸入值。"); } return (TResult)parameters[1]; }
這里采用反射機制來調用類型的T.TryParse(string param, out T value),例如Int32.TryParse(string param,out Int32 value)等,需要注意的是:
(1). GetMethod()方法,必須傳入合適的參數(要反射的方法的簽名)來確定方法唯一,例如碰到重載這種情況(比較常見),否則返回值為null,方法的簽名中,若參數帶有ref 或out 關鍵字,則Type類型需要加上.MakeByRefType(),如上。
(2). 得到唯一的方法實例后,可以傳入相應的參數,調用Invoke方法來實現方法的調用,MethodInfo.Invoke(object obj, object[] parameters)方法第一個參數為反射調用該方法的對象,如果為靜態方法(比如本例),可以傳入null,第二個參數為方法的參數,順序必須與方法簽名一致。
(3). 方法參數中帶有ref和out關鍵字,獲得該值通過參數數組來獲得。如本例中:parameters[1]
3. 定義轉換失敗操作的委托
C#內置封裝的委托有兩種,Action和Func委托,並有很多的重載版本,參數可以有十多個,所以不用擔心參數問題。其中Action委托無返回值,屬於Void類型,Func委托具有返回值,如Func<T,TResult>,在Linq操作中比較常見,在該例中,無返回值的必要,故采用Action委托,由於需要處理轉換失敗的操作,故將TextBox作為該委托的參數里進行處理,如代碼所示,當轉換失敗時進行處理:
// 若轉換失敗,執行failed
if (!(bool)method.Invoke(null, parameters))
failed(textBox);
在此簡單介紹下委托:委托其實是一個類型,通過反編譯工具可以看出來,當構造委托時傳入一個方法,其實會隱形的傳入兩個參數(target,methodPtr),target參數為調用該方法的實例,若靜態方法,則為null,methodPtr為傳入方法的內存地址(在元數據中存貯該信息),faild(textBox)表面看不太好理解,為什么一個對象后面帶一個參數,其實C#編譯器為我們做了很多工作,在這里實質為faild.Invoke(textBox),這樣看還好理解委托是個類型,通過faild的委托對象來調用該委托所注冊的方法。
4. 創建重載版本:
用lambda表達式定義默認的轉換失敗操作,如果轉換失敗,則提示信息,並全部選中和定位到該輸入框上。
public static TResult GetValue<TResult>(this TextBox textBox, bool isShowError) where TResult : struct { return GetValue<TResult>(textBox, p => { if (isShowError) { p.Focus(); p.SelectAll(); MessageBox.Show("輸入值格式不正確,請重新輸入!", "提示--值類型:" + typeof(TResult).Name, MessageBoxButtons.OK, MessageBoxIcon.Warning); } }); }
///默認版本,調用上個重載方法
public static TResult GetValue<TResult>(this TextBox textBox) where TResult : struct { return GetValue<TResult>(textBox, true); }
5. 實驗測試:
新建winform程序,界面如圖所示:
后台代碼:
private void btnConvert_Click(object sender, EventArgs e) { try { var intValue = txtInt.GetValue<int>(); var floatValue = txtFloat.GetValue<float>(); var dateTimeValue = txtDateTime.GetValue<DateTime>(); var doubleValue = txtDouble.GetValue<double>(); } catch (Exception) { } }
如果輸入值非法,則提示錯誤,如圖所示:
轉載請注明出處:http://www.cnblogs.com/gis-crazy/archive/2013/03/17/2964132.html