引言
類型轉換經常遇到,最常用的應該是string類型轉換為其它基元類型,常見於http參數類型轉換。Convert靜態類的Convert.ChangeType()方法可以把實現IConvertible接口的類型轉換為其它也實現這個接口的類型,也等同於里面的ToInt32()、ToDecimal()等方法的功能,但不支持轉換到這類型的可空類型,因為Nullable<>類型並沒有實現IConvertible這個接口;此外JavaScriptSerializer.ConvertToType()這個方法相對強大,支持轉換到Nullable<>類型、字符串到枚舉,也支持IDictionary<string,object>類型轉換為對象,居於原因,是為了JavaScriptSerializer在反序列化JSON為動態類型后,能把這些類型進行轉換為常見類型,但JavaScriptSerializer.ConvertToType方法不能擴展。
神器來了
名稱:
今天主角轉換器叫Converter,這是花費我幾天時間才琢磨出來。
功能:
今它支持基礎類型、decimal、guid和枚舉相互轉換以及這些類型的可空類型和數組類型相互轉換,支持字典和DynamicObject轉換為對象以及字典和DynamicObject的數組轉換為對象數組。
擴展性:
可以增加其它類型轉換,或修改某種轉換規則,也就是說轉換器是支持功能部分重寫和功能擴展的,功能移除也支持,比如想干掉DynamicObject類型的轉換也可以。
神器使用方法
1、使用靜態方法,Converter.Cast(object value,Type targetType)或其泛型方法 :Converter.Cast<int?[]>(new[] {null, "2" }
2、實例化來調用,new Converter().Convert(new[] { null, "2" }, typeof(int?));
Converter的擴展
擴展是靈魂,如果沒有擴展功能,這東東和JavaScriptSerializer.ConvertToType()有啥區別,Converter在擴展方面下了一翻功夫。轉換器里包含多個轉換單元,這些單元可以增加、刪除、替換和調整順序,自定義的單元,可以從已有單元派生,也可以完整實現,然后插入到轉換器的轉換單元管理器中,轉換器就升級了。而轉換單元本身也能調用轉換器來轉換,這就形成一種可擴展的遞歸。
關系代碼如下:
/// <summary> /// 類型轉換 /// </summary> public Converter() { this.Items = new ContertItems() .AddLast<NoConvert>() .AddLast<NullConvert>() .AddLast<PrimitiveContert>() .AddLast<NullableConvert>() .AddLast<DictionaryConvert>() .AddLast<ArrayConvert>() .AddLast<DynamicObjectConvert>(); }
/// <summary> /// 定義類型轉換單元 /// </summary> public interface IConvert { /// <summary> /// 將value轉換為目標類型 /// 並將轉換所得的值放到result /// 如果不支持轉換,則返回false /// </summary> /// <param name="converter">轉換器實例</param> /// <param name="value">要轉換的值</param> /// <param name="targetType">轉換的目標類型</param> /// <param name="result">轉換結果</param> /// <returns>如果不支持轉換,則返回false</returns> bool Convert(Converter converter, object value, Type targetType, out object result); }
刪除轉換單元:
var converter = new Converter(); converter.Items.Remove<DynamicObjectConvert>();
增加轉換單元:
編寫一個單元,實現IConvert接口,比如 class JObjectConvert:IConvert{},然后就可以把這個轉換單元添加到轉換器實例中,
var converter = new Converter(); converter.Items.AddFrist<JObjectConvert>();
轉換單元重寫:
比如從string轉換為decimal類時,要求5位小數,我們可以寫一個單元,class MyPrimitiveContert:PrimitiveContert{},然后改寫里面的Convert方法,再把MyPrimitiveContert替換轉換器里的PrimitiveContert單元
var converter = new Converter(); converter.Items.Replace<PrimitiveContert, MyPrimitiveContert>();
后記
之所以寫這個東西,是因為我的NetworkSocket組件里websocket部分很需要一個靈活的類型轉換器,客戶端發送{api:"login",parameters:[{account:"admin",password:"123456"}]}這樣結構的Json給服務器,服務要解析這個json然后反射執行Login(Userinfo user)這個方法。
這里parameters數組里內容的類型是根據api的名稱而定的,而要解析到api的內容,又要知道整個json的結構,形成蛋和雞。只有將json解析為動態類型,才能滿足要求,而JavaScriptSerializer反序列化為動態類型時,實際上是json對象轉換為字典,json數組轉換為ArrayList,其它基本類型差不多一一對應,枚舉類型看情況;如果使用Json.Net解析,json對象轉換為JObject動態類型,json數組轉換為JArray,一些基本類型一般會對應JValue。
當把序列化和反序列化抽象或定義為接口的時候,JavaScriptSerializer.Converter方法就無力了,它沒法轉換JObject這些類型,也沒有擴展的入口點,還有就是在.net core上也用不了。而Converter只需要添加一個單元轉換單元,用來實現Json.Net里面幾個動態類型轉換為Api參數的類型,就可以達到完美的轉換效果。
