新框架正在逐步完善,可喜可賀的是基礎服務部分初具模樣了,給大家分享一下
由於基礎服務涉及面太廣,也沒開發完,這篇只介紹其中的類型轉化部分,命名為類型轉化基礎服務,其實就是基礎服務模塊的類型轉化子模塊
說到類型轉化必須要弄清楚.net的類型,類型都不清楚何來類型轉化
1、Primitive類型
1.1 這個概念估計很多人都沒聽說過,Primitive不是一個新類型,而是.net類型中最基本的一種分類,是基元類型的意思
MS將類型分為三類:Primitive(基元類型)、Complex(復合類型) 和 Collection(集合類型),Type 提供了IsPrimitive 屬性
1.2 哪些類型屬於Primitive呢?
參考鏈接:http://msdn.microsoft.com/en-us/library/system.type.isprimitive(v=vs.110).aspx
”The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.“
1.3 看到這里還是挺失望的,我們常用的類型Decimal和String並不含在內
有的同學可能說,別說那兩個了,里面就除了Char和Double其他都沒用過;老實說MS真的把.net程序員徹底慣壞了
其實Boolean就是我們常用的bool,Int32就是int,Int64就是long,Single就是float



1.4 Primitive類型有什么用呢?
Primitive是基元類型,可以這樣理解,其他類型都是由Primitive類型構成的或者衍生的,.net對Primitive支持最好,"有些地方"對於非Primitive類型支持不好,可能造成"靈異"事件(對於一般的項目是很難碰到的,如果你能碰到,說明你對.net鑽研的比較深了,普通的工程師就更不要嚷嚷說.net類型有缺陷不敢用之說,最多底層框架開發者或者架構師小心一點)
這里就不展開了
2、IConvertible類型
IConvertible並不是新維度的類型類別,IConvertible是MS定義的一個重要接口,我這里講的IConvertible類型特指Ms定義的基本類型中繼承了該接口的類型

我來稍作解析一下,繼承了IConvertible的類型非常強大,可以方便的轉化為常用的這15種類型,也正是這15種類型繼承了IConvertible接口
也就是這15種類型可以方便的相互轉化(其實不一定的,有些轉化是會出錯的)
IConvertible類型轉化具有高度的擴展性,可以傳一個格式化的參數(IFormatProvider)
注:在我看來IFormatProvider主要是作用是字符串(string)類型和其他14種類型相互轉化之用,string被Ms定義成了"通用類型",我認為是數據形態之一(我把數據分成幾個形態,強類型,弱類型(object),string,二進制(byte[]),數據流(Stream),這個會在序列化基礎服務中再詳細講解)
3、IFormattable類型
IFormattable和前面IConvertible類似,也是Ms定義的一個重要接口,繼承這個接口的類型非常多,這個接口主要是把對象轉化為特殊(格式化)的字符串。

基本類型中的byte\DateTime\Decimal\Double\short\int\long\sbyte\float\ushort\uint\ulong等都繼承,除此之外還有很多系統類型也都繼承IConvertible(這里不再枚舉),我們自定義類型也可以方便的實現IConvertible接口
鑒於字符串(string)的重要性,該接口相關的服務對應用開發非常有用(但是很多人都不清楚)
4、"基本"類型
這里是我定義的”基本類型“,我認為有這些類型就足夠開發使用,框架對此之外的類型不予"支持",或者這些類型優先支持
包含類型列表:bool、byte、char、decimal、double、float、int、long、string、DateTime、byte[]
注1:在現在內存這么便宜的時代,我認為短數據類型的作用不大,短數據類型都使用int就好了
注2:我也不喜歡使用無符號類型,所以又省略了一大批類型,對於數字類型Ms使用==0表示"邏輯空",我使用<=0表示"邏輯空",后面還有相關例子
注3:這樣做也是情非得已,類型太多了,其排列組合更多,好在本框架是可擴展框架,使用本框架的時候可以自己擴展支持的類型,當然也可以開發一個類型擴展的組件(類庫),需要的時候自己注冊進去
現在開始演示類型轉化基礎服務
一、基本類型轉化
1、類型太多,先測試字符串轉int


2、不同數字類型轉化為Int


這里我要多嘮叨一句,我前面說過對”基本類型“以外的不予支持,其實是不"支持"轉化為這些類型,這些類型轉化為”基本類型“是沒有問題的
上面例子里面有小數(浮點)轉整數(int)的例子,Ms有自己的一套“四舍五入”的規則,如果不滿意可以自己擴展注冊進去(一般情況下調用的地方並不需要修改)
另外,會嘗試IConvertible的類型相互轉化,如果Ms支持的很好的IConvertible轉化.這里也沒道理不支持的
3、object轉化為int


以上例子可以看出,基本類型轉化完全沒有問題,其實在主框架,我也不打算默認實現復雜自定義類型的轉化,這個留給擴展實現
有人可能會說你上面的這些功能So easy,實現過類似功能的多如牛毛,不過是多造個輪子而已。確實,基本類型轉化實現簡單,但是這里要強調,我做的是可擴展框架,可擴展是亮點,而且非常容易擴展
二、自定義類型轉化為基本類型
1、先看自定義類型代碼

非常簡單還是解讀一下,定義了兩個類型,customObj明顯是個模型(Model)類,customConverter有一個方法傳入一個customObj對象返回一個int值
2、再看對象轉化代碼


首先我們看結果,是我們預期的效果,和customConverter的Get方法的效果一致,但是我們沒有直接調用customConverter.Get,甚至好像和customConverter類型都沒什么關系
這里有幾個關鍵因素,其一是GlobalServices.Convert方法是怎么運行的;其二是GlobalServices.CreateContainer()是什么鬼
三、源碼解析
1、GlobalServices.Convert方法解析




這個類型轉化還是挺復雜的,挑主要的來解讀
1.1 TryConvert是嘗試類型轉化,如果轉化失敗可以換一種方法再做
這種方式在本框架中有大量應用,其實就就形成一個”策略鏈“,每個”策略“判斷一個這個問題自己是否是自己可以處理的類型,不能處理,下一個策略繼續,這樣非常便於擴展
在TryConvert中先調用個接口IEntityConvert,轉化失敗再調用IEntityAccess用來轉化
這里有一個特別重要的事情,就是這個接口的對象從哪里來,這里是來自一個容器對象,這里很清楚的看到本框架的一個擴展點,只要往容器里面添加"策略"就可以增強本框架的類型轉化功能,這是我為什么說主框架不打算實現復雜的類型轉化,真的非常容易擴展
1.2 再看Convert主方法
A:先按當前類型嘗試轉化
如果策略庫(容器注冊)里面有當前兩種類型轉化的策略,性能是最好的,優先執行
B:轉化失敗判斷當前對象是否為空
對象為null,放棄轉化,直接返回默認值
C:Transform.TryConvertByType
這個簡單就是強制類型轉化,如果T是S的基類,這個時候就可以直接轉化過去,也是非常安全的
D:再嘗試IConvertible轉化
前面說到系統的IConvertible定義了15種基本類型的相互排列組合轉化,而且還可以使用IFormatProvider自定義轉化,這就是一個強大的轉化機器,不能不試
E:再檢測返回類型是否為string,如果是string直接調用對象的ToString()
F:嘗試通用類型轉化(IConvert)
這個地方可以把第三方的Mapper工具封裝為IConvert接口注冊進來(后面還有講解)
G:最后嘗試把s對象轉化為字符串,然后把字符串轉化為目標類型(T)對象
其實這里把字符串作為基本數據格式,相當於與對s對象序列化為字符串,然后把字符串反序列化為T類型對象,也不怪我怎么用,.net所有類型都有一個ToString()方法,所以string是個不錯的中間類型
功能是不是非常強大,也非常有別於很多類型轉化工具,上來先反射,獲取類型元數據,然后調用屬性和字段,現在還沒做測試,我這種方式可能會有明顯的性能優勢,但是性能現在不是我最想考慮的問題,我現在考慮的是怎么可以非常簡單的擴展
2、GlobalServices.CreateContainer()是什么鬼
CreateContainer定義

A:從上面可以看出GlobalServices.CreateContainer就是一個再普通不過的容器,默認實例的容器名是GlobalServices
B:前面有提到容器是可以擴展的,只要我們使用的容器支持(比如Unity、Spring.net等),我們完全可以使用配置文件來擴展類型轉化,還可以使用非常炫的IOC和AOP等特性
C:說到這里,現在"業界"有幾個類型轉化工作可以做到?我稍微看了一個現在非常流行的Automapper,自定義映射確實沒問題,但是映射過程還可以通過配置文件擴展或者IOC和AOP的我沒見識過,如果有人看到過請告知,我要好好學習學習
D:早期看過這篇文章的可能發現,變化很大,我把GlobalServices由靜態類修改可以實例化的形式,這樣就允許我們創建多套服務配置管理對象(使用子容器技術,繼承默認服務並覆蓋少數服務),進一步提高可擴展性
3、其實GlobalServices容器和其他容器還是有點區別的,繼續深挖源碼

這里有一個不起眼的地方加了一行代碼,給GlobalServices容器”吃小灶“
不挖不知道,一挖嚇一跳,有一種”柳暗花明又一村“的感覺
哈哈,我就說我要對每個容器做包裝,”居心不良“吧;哈哈,這樣說太難聽了,一句話"還是為了更好的擴展"。
4、沒辦法了,還得繼續挖GlobalServices.CheckServices

這里可以看到框架支持的"基本類型",把這些轉化策略都注冊到容器中,主要是擔心容器注冊的服務不夠用,把不足的基本類型轉化服務都注冊上
當然如果容器中已經存在同類型的"服務",這里的注冊是會忽略的
同樣,就算是這里注冊的服務,如果覺得不好用,也可以在外面重新注冊並覆蓋
還有一點,前面截圖可以明顯看出"基礎服務模塊"的基本樣子,但是除了”類型轉化子模塊“外,其他子模塊都還沒開發出來
四、拆分使用
1、字符串轉時間測試



以上兩個日期字符串我們用來轉化為時間,一個成功一個失敗
這個例子我想說明兩個問題
其一、每個對象轉化服務都可以單獨使用而且性能最好,省去了復雜的的策略邏輯、容器操作,而且執行邏輯明確
其二、使用特定的服務不便於擴展,如果這樣的代碼有bug需要替換,每個地方去改,很容易導致遺留問題(沒改全)
其三、通用服務都是默認實現,總有局限性,有些特殊問題無法解決(以上調用邏輯和使用全局服務的邏輯一致,也會出現異常),總有特事特辦的,只要控制數量還是沒有問題的
2、對特殊字符串轉轉時間測試


以上順利轉化成功,可喜可賀,事實上有的時候特事特辦能做到更高的性能或者更好的效果
這里還有一個我要說的就是“面向對象編程”,在我看來很多人都是在做"面向類"編程,因為他們的類型都只有一個實例或者都只能簡單的new()來構造,我特別強調類和對象是一對多的關系,一個類可以初始化多個對象,通過不同的字段和屬性能達到不同的效果。這樣才可以把類和對象用到極致。不是有句名言“程序等於算法加數據結構”。對我而言,每個對象就是一個獨立的程序,字段(屬性)就是數據結構,定義的方法(函數)就是算法。好的對象是可以獨立遷移的。如果使用容器配置管理,我要遷移程序(組件對象)就只要復制對應的配置節點和dll即可。
當然不是說一定非要硬編碼,這樣的“特事特辦”也可以在容器中配置特殊的節點來實現
3、從容器中獲取特殊服務對特殊字符串轉轉時間測試


效果還不錯吧,而且也是面向接口的,要擴展也是非常簡單
以下是容器配置:

以上是Unity容器配置,能實現類似配置的容器很多,選擇也很多,再強調一次邏輯代碼沒有必要強依賴任何固定的容器
五、IFormattable轉化為字符串服務
1、時間格式化的例子


效果不錯吧
有人說使用DateTime的ToString()方法傳一個字符串參數嗎?確實,底層調用的是同一個邏輯。
我們知道DateTime的ToString()可以傳一個格式字符串,那這個格式字符串硬編碼寫死不太好吧,如果哪天我們要的字符串格式使用ToString()無法實現怎么辦。ToString(format)是一種轉化字符串的方式,但不是唯一的方式。 這就是面向接口的好處,我們依賴的是把一個把DateTime轉化為string的服務(接口),甚至是一個把object轉化為string的服務。這樣我們的服務就更強大,更容易擴展了。
2、使用容器再做一個上面的例子


效果和前面一樣
以下是容器配置:

當然還可以注冊為全局服務(使用GlobalServices.Convert就可以調用到,覆蓋默認的ToString()),這樣就更方便了(但是要慎重,全局格式一定定義的足夠通用)
六、擴展性演示
1、邏輯空(int轉bool)


前面有提到,我是把>0為true的,<=0為false,我一直用的很方便,我叫做"邏輯空",但是ms並不是這樣處理的,如果有的團隊還是希望按ms的規則怎么辦,擴展啊
2、把上例中的注釋打開就切換為ms模式(==0為false)的擴展

上面的結果就是ms模式了
3、現在來看一下是怎么擴展的


以上代碼很清楚,擴展很簡單,獲取服務配置的容器,注入服務並覆蓋原服務,就完成了擴展
當然如果自己想擴展基礎服務中的任何功能都不是問題,只需要實現對應服務接口,如后注冊到服務容器中去就可以了
七、IConvertible轉化功能
1、IConvertible轉化int的例子


A:這里演示的就是IConvertible(15種類型)轉化為int的部分例子,完全沒有問題,調用也很方便
B:有的人可能會說,Ms提供了IConvertible搞定15中類型相互轉化,你寫那些類型轉化完全是重復造輪子,這是沒有理解這個框架的思想
注:在這里使用Convert<IConvertible, int>轉化short類型的值(123)是沒有使用Convert<short, int>性能好的,不僅僅是類型轉化的問題,完全不是一套邏輯
C:這個框架(可擴展)不只是提供某種功能,他要提供高度可擴展性,以便對任意的細分"領域"都可以擴展優化效果和性能
D:特別是性能這塊,框架優先獲取最細分領域的服務來處理問題,我有一個開發思想,通用工具解決大部分(60%到90%)的問題,有特殊性能或業務需求的單獨寫代碼來支持,這個框架就是對這種思想的強有力支持
注:這個框架和其他框架有個明顯的區別,一般框架是越擴展(打補丁)性能越差,這個框架是相反的,一般情況下是越擴展性能越好(除非擴展進來的服務太差,那還叫擴展嗎)
八、自定義類型轉化
1、繼承類型轉化試試



木有問題,轉化成功
2、取消繼承再測



“哦,NO!框架出bug了,這么簡單的事情也出錯”
不是的,大家好好看這個錯誤信息的內容,可擴展框架提醒你,需要擴展
3、按提示的擴展IEntityConvert再測



又轉化成功了,說明可擴展此言不虛
有人說你這不就是硬編碼嗎?類型轉化這等小事,還要一個個硬編碼,還讓不讓人活了。我還要重復一遍,對於特別性能需要的地方,硬編碼是有必要的
而誒硬編碼類型轉化代碼不需要你直接調用,是注入到框架中的,調用的地方沒有區別(不知道是調用硬編碼實現還是默認實現)
有人說你要我每個類型都硬編碼就是不對,現在沒有這么干的!確實沒有必要,90%以上的類型轉化都不需要硬編碼,再按提示做另一種擴展
4、擴展IConvert再測



再次轉化成功,可以使用這種方式擴展自定義類型的默認轉化
有人要挑錯了,你以上代碼哪是任意類型嘛,明明就只能A轉B,其他都轉成null了。以上只是個示例,現在開源項目中能實現這樣轉化的工具很多,我真沒必要開發到主框架中,拿來用就可以了
我完全可以拿Automapper(或者EmitMapper,LiteMapper等)封裝一下,繼承IConvert接口,然后注冊到框架中來,這才是本框架的精神(集成和整合),還是那句話,如果特定兩個類型轉化需要高性能,直接硬編碼注冊進來,所有調用的地方不用改就達到優化的目的
我也不用再糾結哪個Mapper更好,所有Mapper都封裝,愛用哪個就注冊哪個(當然不建議同一個應用程序使用兩個以上的Mapper)
類型轉化基礎服務基本介紹完了,還有遺漏的我再補(大塊應該都差不多了),后面的精力將轉為其他模塊的開發和介紹。有什么建議和意見請回復,謝謝。
