本文代碼基于 .NET Framework 實(shí)現(xiàn)。
本來只想進(jìn)行簡單的配置存儲的,不料發(fā)現(xiàn) .NET 的基本類型多達(dá)十多種。于是,如果寫成下面這樣,那代碼可就太多了哦:
// 注:`Configurator`是我的配置類,用于讀寫字符串的。
public static int GetInt32(this Configurator config, string key)
{
return int.Parse(config[key], CultureInfo.InvariantCulture);
}
public static void SetInt32(this Configurator config, string key, int value)
{
config[key] = value.ToString(CultureInfo.InvariantCulture);
}
public static bool GetBoolean(this Configurator config, string key)
{
return bool.Parse(config[key]);
}
// 還沒完,這才 1.5 種而已。
// ……
嘗試使用泛型
這些方法都比較相似,于是自然而然想到了泛型,所以寫出了這段代碼:
public static T GetValue<T>(this Configurator config, string key) where T : struct
{
var @string = config[key];
// T value = 某種通用的轉(zhuǎn)換(@string); // 問題來了,這里該怎么寫?
return value;
}
這里就郁悶了,因?yàn)殡m然方法內(nèi)部的實(shí)現(xiàn)都長得差不多,但他們之間除了名字相同之外(比如 Parse和ToString),并沒有什么聯(lián)系;這樣,便不能使用統(tǒng)一的接口或者抽象類等等來調(diào)用。
嘗試使用反射
既然名字類似,那自然又能想到反射??墒牵瑩碛?Parse 的類型并不多,ToString 中能傳入 CultureInfo.InvariantCulture 且參數(shù)順序保持一致的類型也少的可憐。于是,反射只能保證寫得出來代碼,卻并不能保證多種類型的支持。
另外想到一點(diǎn),Int32 類型的 TryParse 中有 out 關(guān)鍵字修飾的參數(shù),反射能否支持呢?StackOverflow 上找到了答案:
You invoke a method with an out parameter via reflection just like any other method. The difference is that the returned value will be copied back into the parameter array so you can access it from the calling function.
object[] args = new object[] { address, request }; _DownloadDataInternal.Invoke(this, args); request = (WebRequest)args[1];
意思是說,這在反射中不用作什么考慮,傳入的參數(shù)數(shù)組天然就已經(jīng)支持了 out 關(guān)鍵字。
嘗試尋找更通用的方案
在 Google 上繼續(xù)漫游,在 StackOverflow 上發(fā)現(xiàn)這篇討論:How to convert string to any type。
最高票答案給出的回復(fù)是:
using System.ComponentModel; TypeConverter typeConverter = TypeDescriptor.GetConverter(propType); object propValue = typeConverter.ConvertFromString(inputValue);
這可打開了思路,原來 .NET Framework 內(nèi)部已經(jīng)有了這種轉(zhuǎn)換機(jī)制和相關(guān)的方法。于是用這種方法修改我的方法,成了這樣子:
public static T GetValue<T>(this Configurator config, string key) where T : struct
{
var @string = config[key];
var td = TypeDescriptor.GetConverter(typeof (T));
return (T) td.ConvertFromInvariantString(@string);
}
public static void SetValue<T>(this Configurator config, string key, T value) where T : struct
{
var td = TypeDescriptor.GetConverter(typeof (T));
var @string = td.ConvertToInvariantString(value);
config[key] = @string;
}
編寫單元測試發(fā)現(xiàn),這種方法能夠支持的類型真的非常多,byte char short ushort int uint long ulong bool float double decimal DateTime Point Vector Size Rect Matrix Color……
看看代碼中 TypeDescriptor.GetConverter 的返回值發(fā)現(xiàn)是 TypeConverter 類型的,而我們在 WPF 的 xaml 中編寫自定義類型時,經(jīng)常需要執(zhí)行此類型的 TypeConverter。憑這一點(diǎn)可以大膽猜測,xaml 中能夠通過字符串編寫的類型均可以通過這種方式進(jìn)行轉(zhuǎn)換。然而,目前我還為對此進(jìn)行驗(yàn)證。
驗(yàn)證猜想
- 去 https://referencesource.microsoft.com/ 看
TypeDescriptor.GetConverter的源碼(點(diǎn)擊進(jìn)入)。 - 嘗試自定義一個類型,在類型上標(biāo)記
TypeConverter,并對此類進(jìn)行測試。