本系列文章將介紹一些對初學者有幫助的輔助類,這些輔助類本身並沒有什么稀奇之處,如何能發現需要封裝它們可能更加重要,所謂授之以魚不如授之以漁,掌握封裝公共操作類的技巧才是關鍵,我會詳細說明創建這些類的動機和思考過程,以幫助初學者發現和封裝自己需要的東西。創建公共操作類的技巧,大家可以參考我的這篇文章——應用程序框架實戰十二:公共操作類開發技巧(初學者必讀)。
封裝公共操作類,不僅要把技術上困難的封裝進來,還需要不斷觀察自己的代碼,以找出哪些部分可以更加簡化。本文將介紹一個容易被大家所忽視的東西——數據類型轉換。
數據類型轉換可以把某個源類型轉換為目標類型,比如把字符串轉換為整型。一種選擇是,你可以使用System.Convert類進行轉換。
string input = "1"; int result = System.Convert.ToInt32( input);
這樣看起來好像沒什么問題,不過input很明顯不一定是個常量,如果你還在使用ASP.NET Web Form這樣的技術,你需要在后置代碼中從控件讀取值。
string input = TextBox1.Text; int result = System.Convert.ToInt32( input);
當客戶輸入整數時,不會有什么問題,但他如果輸入一個字母或漢字,上面代碼會拋出一個異常,“System.FormatException: 輸入字符串的格式不正確。”,這可能不是你想要的。當然,你可以在客戶端進行JS驗證,不過客戶也可以繞過你的頁面,直接POST到你的服務器,所以你在服務端必須處理這個問題。
引發異常,並且你沒有進行任何處理,可能導致一個黃頁,讓你的客戶一驚。為了避免顯示黃頁,初學者大多直接在代碼上加一個try-catch進行捕獲,並給客戶一個友情提示。
try { string input = TextBox1.Text; int result = System.Convert.ToInt32( input ); } catch( Exception ex ) { //彈出消息框提示客戶輸入正確數據 }
一旦吃到甜頭,初學者發現這段代碼可以實現他要的功能,不會過多考慮可維護性,於是會把這個結構向整個表現層復制,最終導致一個混亂的局面。
除了表現層以外,數據訪問層也經常需要進行數據類型轉換。如果你還在使用原始的Ado.Net,從DataReader獲取值。
IDataReader reader = cmd.ExecuteReader(); int result = System.Convert.ToInt32( reader["字段名"] );
同樣,為了不引發異常,初學者根據之前的經驗,會在調用代碼時添加try-catch進行異常捕獲。
try { IDataReader reader = cmd.ExecuteReader(); int result = System.Convert.ToInt32( reader["字段名"] ); } catch( Exception ex ) { //有些人會在這里記錄錯誤日志,還有些懶人直接留空,啥也不干
}
所以最終的結果是,只要進行數據類型轉換的操作,初學者為了一定的系統健壯性會大量添加異常處理結構,從而導致代碼混亂。
另一個選擇是,.Net提供了一個不引發異常的類型轉換方法,比如,
string input = "1"; int result; if( int.TryParse( input, out result ) == false ){ //處理錯誤
}
TryParse會返回一個bool值,指示轉換是否成功,如果轉換失敗,你可以記錄日志,並顯示一個錯誤。不過大部分人都可能會偷懶,不會在這里進行任何處理。
string input = "1"; int result; int.TryParse( input, out result );
如果你不想引發異常,更不想用try-catch結構來捕獲異常,這確實是一個更好的選擇,但需要額外定義一個變量,作為out參數來獲取值,會造成額外的工作量。
當然,.Net技術也一直處於持續改進中,表現層技術進入到MVC和WPF時代,而數據訪問技術也進入到了Entity Framework時代。MVC提供了一個叫做模型綁定的功能,用於將界面上傳回的數據映射到控制器操作的參數中,並且這些參數可以支持實體,這是一個非常強大的功能,這樣就不需要手工進行賦值了,更不需要類型轉換。WPF通過雙向數據綁定,Entity Framework通過映射器,都解決了類似問題。
但是,並不是說數據類型轉換就毫無用武之地了,考慮一個使用MVC的場景。你在界面上有一個表格,表格的每行都有一個checkbox,你可以打勾以選中某些行,然后你會把每行的實體標識傳到控制器。如果實體標識的數據類型使用Guid,你可以用一個IList<Guid>來接收。
public ActionResult 方法名( IList<Guid> ids ) { }
這一般都行得通。但是,你並不總是可以這樣干,出於某些特殊原因,有時候你需要自己在js中用逗號拼接一些Id,或者用一個Hidden把用逗號拼接的Id保存起來,然后作為一個字符串傳到控制器。比如” 06d8dcdd-cfad-433c-aec0-87bf613b9457, 684c0c02-93ec-4047-8f16-57e36b50d703”。
public ActionResult 方法名( string ids ) { }
由於沒有自動轉換支持,你只好自己動手,這很簡單,使用逗號把輸入字符串打散成字符串數組,然后遍歷每個數組元素,轉換成Guid類型,再添加到一個結果集合保存起來就OK了。
public ActionResult 方法名( string ids ) { List<Guid> result = new List<Guid>(); string[] list = ids.Split( ',' ); foreach( var each in list ) { result.Add( Guid.Parse( each ) ); } //在這里把result傳到業務層
}
你發現這段代碼不僅可以處理帶有逗號的字符串,像” 06d8dcdd-cfad-433c-aec0-87bf613b9457, 684c0c02-93ec-4047-8f16-57e36b50d703”,甚至還能處理不帶逗號的,比如“06d8dcdd-cfad-433c-aec0-87bf613b9457 “。你非常滿意,然后把這一段代碼復制到所有需要將拼接字符串轉換成集合的地方。
沒過多久,你在一個頁面發現了Bug,以前測試的時候,都是操作多個Id,這次沒進行操作,傳過來的字符串是””,你的代碼沒有進行任何健壯性檢測,所以失敗了,拋出一個異常“無法識別的 GUID 格式。“。
你安慰自己“這並不算什么技術問題,只是一時疏忽,看我加一個判斷,一招將它搞定“。不過在你的下意識里,已經感覺到進行邊界測試才是健壯性的關鍵。
public ActionResult 方法名( string ids ) { List<Guid> result = new List<Guid>(); if ( !string.IsNullOrWhiteSpace( ids ) ) { string[] list = ids.Split( ',' ); foreach( var each in list ) { result.Add( Guid.Parse( each ) ); } } //在這里把result傳到業務層
}
你現在准備測試一下,傳了一個””過來,果然有效,你大贊自己處理BUG的速度驚人,為了給其它地方也添加如此健壯的特性,你打開所有可能用到的頁面,找到這幾行代碼,進行修改,雖然你感覺這確實有點無聊,但還是只有硬起頭皮改了。
從上面可以看到,雖然是司空見慣的數據類型轉換,也還是有很多值得我們改進的地方。
下一篇我將使用TDD方式把數據類型轉換公共操作類開發出來。由於TDD並不是本系列介紹的重點,所以我不會在本文中詳細介紹TDD的要點,請大家查看相關資料,如果有空,我會專門寫一篇文章來分享我在使用TDD所碰到的障礙以及心得。
.Net應用程序框架交流QQ群: 386092459,歡迎有興趣的朋友加入討論。
謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/xiadao521/