利用 TypeConverter,轉換字符串和各種類型只需寫一個函數


本文代碼基於 .NET Framework 實現。

本來只想進行簡單的配置存儲的,不料發現 .NET 的基本類型多達十多種。於是,如果寫成下面這樣,那代碼可就太多了哦:

// 注:`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 = 某種通用的轉換(@string); // 問題來了,這里該怎么寫? return value; }

這里就郁悶了,因為雖然方法內部的實現都長得差不多,但他們之間除了名字相同之外(比如 ParseToString),並沒有什么聯系;這樣,便不能使用統一的接口或者抽象類等等來調用。

嘗試使用反射

既然名字類似,那自然又能想到反射。可是,擁有 Parse 的類型並不多,ToString 中能傳入 CultureInfo.InvariantCulture 且參數順序保持一致的類型也少的可憐。於是,反射只能保證寫得出來代碼,卻並不能保證多種類型的支持。

另外想到一點,Int32 類型的 TryParse 中有 out 關鍵字修飾的參數,反射能否支持呢?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];

意思是說,這在反射中不用作什么考慮,傳入的參數數組天然就已經支持了 out 關鍵字。

嘗試尋找更通用的方案

在 Google 上繼續漫游,在 StackOverflow 上發現這篇討論:How to convert string to any type

最高票答案給出的回復是:

using System.ComponentModel;

TypeConverter typeConverter = TypeDescriptor.GetConverter(propType); object propValue = typeConverter.ConvertFromString(inputValue);

這可打開了思路,原來 .NET Framework 內部已經有了這種轉換機制和相關的方法。於是用這種方法修改我的方法,成了這樣子:

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; }

編寫單元測試發現,這種方法能夠支持的類型真的非常多,byte char short ushort int uint long ulong bool float double decimal DateTime Point Vector Size Rect Matrix Color……

看看代碼中 TypeDescriptor.GetConverter 的返回值發現是 TypeConverter 類型的,而我們在 WPF 的 xaml 中編寫自定義類型時,經常需要執行此類型的 TypeConverter。憑這一點可以大膽猜測,xaml 中能夠通過字符串編寫的類型均可以通過這種方式進行轉換。然而,目前我還為對此進行驗證。

驗證猜想

  1. 去 https://referencesource.microsoft.com/ 看 TypeDescriptor.GetConverter 的源碼(點擊進入)。
  2. 嘗試自定義一個類型,在類型上標記 TypeConverter,並對此類進行測試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM