之前曾經處理一個需求,需要把日期轉成民國日期、把數字轉成中文數字,那時是採用函式的方式來處理這樣的需求,
不過,那時總覺得使用起來並不方便,一直想把他做成像格式化字串一樣的處理方式。
之前曾經處理一個需求,需要把日期轉成民國日期、把數字轉成中文數字,那時是採用函式的方式來處理這樣的需求,
不過,那時總覺得使用起來並不方便,一直想把他做成像格式化字串一樣的處理方式。
最近參考了MSDN,找到了一個解法,使用IFormatProvider物件來提供自訂義的格式化物件,
這個實作上需要使用到兩個介面,一個是IFormatProvider,另一個則是ICustomFormatter介面,
簡單的實作一下,就可以看到自訂義的格式化效果,
接下來就考量到了一個問題,string.Format()這個函式的運作方式是可以一次格式化多種不同型別的物件,
當我同時需要使用民國日期以及中文數字的時候,我需要讓我的自訂義的格式化物件可以處理這兩種型別的物件,
我一開始想到了一個方法來處理這樣的需求,使用Runtime時期的函式多載,
這個方法需要定義各種版本的函式來處理不同型別的物件,然後使用Reflection來選擇所要使用的函式版本,
程式碼會像這樣,
Type obj = this.GetType();
MethodInfo method = obj.GetMethod("FormatA", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(string), arg.GetType(), formatProvider.GetType() }, null);
if (method != null)
return (string)method.Invoke(this, new[] { format, arg, formatProvider });
這樣的話,只要我有定義,就可以選擇到正確的函式來執行,不過萬一沒有定義,就會出錯。
這裡我嘗試使用泛型函式來處理,本來的想法是我多加一個多載版本,這個多載版本是使用泛型函式來做,
這樣的話沒有定義到的型別就會選到這個版本來執行,不過,天不從人願,程式出錯了。
原因在於當我使用上面那樣的方式來找尋時,是找不到泛型函式的,另外一點是,泛型函式其實是不能直接執行的,
需要使用MethodInfo.MakeGenericMethod()來產生特定版本的函式才能執行,
這裡我試了好久,也Google了不少文章,沒想到當泛型和反射搞在一起的時候會這麼困難,
最後實作的程式碼解決了問題,
Type obj = this.GetType();
MethodInfo method = obj.GetMethod("FormatA", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(string), arg.GetType(), formatProvider.GetType() }, null);
if (method != null)
return (string)method.Invoke(this, new[] { format, arg, formatProvider });
else
{
method = (
from m in obj.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
where m.IsGenericMethod == true && m.Name == "FormatA"
select m).ToList<MethodInfo>()[0].MakeGenericMethod(arg.GetType());
return (string)method.Invoke(this, new[] { format, arg, formatProvider });
}
最後想想,其實並不用這麼麻煩就可以解決了,直接使用if(arg is xxx)這樣的寫法來判斷型別,然後執行指定的轉換就可以了,
這樣的話也不用使用到反射,以免產生效能上的疑慮,
結果遶了一圈,使用的方法卻沒用上以上的解決方案,不過,倒是認識到了一些新東西。