自訂格式化

之前曾經處理一個需求,需要把日期轉成民國日期、把數字轉成中文數字,那時是採用函式的方式來處理這樣的需求,

不過,那時總覺得使用起來並不方便,一直想把他做成像格式化字串一樣的處理方式。

之前曾經處理一個需求,需要把日期轉成民國日期、把數字轉成中文數字,那時是採用函式的方式來處理這樣的需求,

不過,那時總覺得使用起來並不方便,一直想把他做成像格式化字串一樣的處理方式。

 

最近參考了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)這樣的寫法來判斷型別,然後執行指定的轉換就可以了,

這樣的話也不用使用到反射,以免產生效能上的疑慮,

結果遶了一圈,使用的方法卻沒用上以上的解決方案,不過,倒是認識到了一些新東西。