你正確的使用Out/Ref了嗎?
2010-12-03 19:19 空逸云 閱讀(734) 評論(6) 收藏 舉報最近在努力的學習MVC.并逐步的把Asp.net MVC應用到實際項目中去.先前.找了不少開源項目,心里一個高興啊.一邊摸索一邊借鑒(抄)著別人的代碼,并希望能從中吸收大牛們的思想.在CodePlex找到一個不錯的開源項目.借鑒著就著手開發了.一路上風平浪靜.抄代碼抄得起勁.不料出現了一個讓我措手不及的錯誤.該代碼實現如下.
/// <summary> /// 獲取配置文件中DappSettings節點下指定索引鍵的Int類型的的值 /// </summary> /// <param name="key">索引鍵</param> /// <param name="defaultValue">默認值</param> /// <returns>Int</returns> private static int getInt32(string key, int? defaultValue) { return getValue<int>(key, (v, pv) => int.TryParse(v, out pv), defaultValue); }
private static T getValue<T>(string key, Func<string, T, bool> parseValue, T? defaultValue) where T : struct { string value = appSettings[key]; if (value != null) { T parsedValue = default(T); if (parseValue(value, parsedValue)) return parsedValue; else throw new ApplicationException(string.Format("Settings '{0}' was not a valid {1}", key, typeof(T).FullName)); } if (!defaultValue.HasValue) throw new ApplicationException("在配置文件的appSettings節點結合中找不到key為" + key + "的子節點,且沒有指定默認值"); else return defaultValue.Value; }
該方法就是獲取配置文件里相關的AppSetting項,并將其轉換成Int32值.很不錯的實現.但是實際上運行的時候卻發現返回的值永遠就是0,這不對啊.于是仔細的分析了這兩個方法.主要就是利用了out關鍵字.
我們知道,利用Out/Ref關鍵字,能使值類型的參數達到引用類型的參數的效果.
瓦解黑盒子
仔細分析了這兩個方法.將核心實現重現如下
public class MyClass { static int parseValue(string str, int pv) { if (ValidParse(str, pv)) return pv; else return 0; } static bool ValidParse(string str, int input) { return Int32.TryParse(str, out input); } public static void Main() { string str = "100"; int num = 0; Console.WriteLine(parseValue(str, num)); } }
看得到.調用步驟如下Main->parseValue->ValidParse,Main得到了一個字符串變量,想把它轉換成Int類型,于是找到了parseValue方法.parseValue方法要求Main提供字符串和一個Int變量,它的做法是利用ValidParse方法,嘗試能否吧str轉換成Int,如果可以的話就返回Int,注意!此處的原意和TryParse相識.而ValidParse方法就是簡單的調用了TryParse.大體看下來.這個流程是沒什么問題的.而且應該得到正確的答案.但事實卻并非如此.圖解一下
可能圖畫得不太精確.但能詮釋這個過程就好了.調用的過程.
第一步.看到.str是引用過去了.而num并不是原值,而是做了一個副本拷貝.這樣.即使在parseValue中num的值發生改變.也只是副本數據發生了改變.
第二步.可以看到.str還是引用,即保持第一手數據.而num又做了一次副本拷貝.在validParse上調用了TryParse方法.此時,ValidParse上的num改變了.但是注意.此處也是副本修改.所以.
第三步(返回),num數據丟失.這也就解釋了為什么我們的結果總會是0了.
既然知道了原因.那就好辦了.我們嘗試著加上out
public class MyClass { static int parseValue(string str, int pv) { if (ValidParse(str, out pv)) return pv; else return 0; } static bool ValidParse(string str, out int input) { return Int32.TryParse(str, out input); } public static void Main() { string str = "100"; int num = 0; Console.WriteLine(parseValue(str, num)); } }
改動并不大.只是在第二步中調用加入out,但是恰是這個out.讓我們的結果正確了.原因就是第三步使用的num并不是步驟二的副本.而是步驟二的數據.所以它修改了原數據,沒發生數據丟失.
優化代碼
精簡代碼一向是我們所追崇的.特別是.Net3.5之后的Lambda.不用的話豈不浪費.廢話不多說.我們開工
delegate bool ParseFunc<T, S>(T T1, out S S1);
先聲明一個委托,注意,這里的委托中用到了out.
private static T getOutValue<T>(string key, ParseFunc<string,T> parseValue, T? defaultValue) where T : struct { string value = appSettings[key]; if (value != null) { T parsedValue = default(T); if (parseValue(value, out parsedValue)) return parsedValue; else throw new ApplicationException(string.Format("Settings '{0}' was not a valid {1}", key, typeof(T).FullName)); } if (!defaultValue.HasValue) throw new ApplicationException("在配置文件的appSettings節點結合中找不到key為" + key + "的子節點,且沒有指定默認值"); else return defaultValue.Value; }
再改造一下GetValue方法.此處必須注意的是,在傳參時必須使用Out,當然.你沒用out,編譯器也不會讓你通過.最后
public static int getInt32(string key, int? defaultValue) { return getOutValue<int>(key, (string v, out int pv) => int.TryParse(v, out pv), defaultValue); }
大功告成.這里主要是理解Lambda表達式的應用.相信你對Lambda的應用有了一個不一樣的理解了吧!
PS:可能這個標題和文章內容有些格格不入,甚至有標題黨的感覺.沒什么文采.大家就體諒體諒.
出處:http://kongyiyun.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
浙公網安備 33010602011771號