/// 获取配置文件中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); }转自:http://www.cnblogs.com/kongyiyun/archive/2010/12/03/1895764.html