日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Core跨平台的奥秘[中篇]:复用之殇

發布時間:2023/12/4 asp.net 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Core跨平台的奥秘[中篇]:复用之殇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在《.NET Core跨平臺的奧秘[上篇]:歷史的枷鎖》中我們談到:由于.NET是建立在CLI這一標準的規范之上,所以它天生就具有了“跨平臺”的基因。在微軟發布了第一個針對桌面和服務器平臺的.NET Framework之后,它開始 “樂此不疲” 地對這個完整版的.NET Framework進行不同范圍和層次的 “閹割” ,進而造就了像Windows Phone、Windows Store、Silverlight和.NET Micro Framework的壓縮版的.NET Framework。從這個意義上講,Mono和它們并沒有本質的區別,唯一不同的是Mono真正突破了Windows平臺的藩籬。包括Mono在內的這些分支促成了.NET的繁榮,但我們都知道這僅僅是一種虛假的繁榮而已。雖然都是.NET Framework的子集,但是由于它們采用完全獨立的運行時和基礎類庫,這使我們很難開發一個支持多種設備的“可移植(Portable)”應用,這些分支反而成為制約.NET發展的一道道枷鎖。至于為什么“可移植(Portable)”.NET應用的開發如此繁瑣呢?

所謂由于目標框架的獨立性,意味著不僅僅是作為虛擬機的Runtime是根據具體平臺特性設計的,作為編程基礎的BCL也不能跨平臺共享,它為開發者帶來的一個最大的問題就是:很難編寫能夠在各個目標框架復用的代碼。比較極端的場景就是:當我們需要為一個現有的桌面應用提供針對移動設備的支持時,我們不得不從頭到尾開發一個全新的應用,現有的代碼難以被新的應用所復用用。 “代碼復用”是軟件設計一項最為根本的目標,在不考慮跨平臺的前提下,我們可以應用相應的設計模式和編程技巧來實現代碼的重用,但是平臺之間的差異導致了跨平臺代碼重用確實具有不小的困難。雖然作得不算非常的理想,但是微軟在這方面確實做出了很多嘗試,我們不妨先來聊聊目前我們都有哪些跨平臺代碼復用的解決方案。

目錄
一、源代碼復用
??? 源文件共享
??? 文件鏈接
??? 共享項目
二、程序集復用
??? 程序集一致性
??? Retargetable程序集
??? 類型的轉移
三、可移植類庫(PCL)

?

一、源代碼復用

對于包括Mono在內的各個.NET Framework平臺的BCL來說,雖然在API定義層面上存在一些共同之處,但是由于它們定義在不同的程序集之中,所以在PCL(Portal Class Library)推出之前,針對程序集的共享是不可能實現的,我們只能在源代碼層面實現共享。源代碼的共享通過在不同項目之間共享源文件的方式來實現,至于具體采用的方式,我們有三種不同的方案供你選擇。

源文件共享

對于一個能夠多個針對不同目標框架的項目共享的源文件,定義其中的代碼也有不少是針對具體某個目標框架的。對于這種代碼,我們需要按照如下的方式進行編寫,相應的項目以添加編譯的方式選擇與自身平臺相匹配的代碼編譯道生成的程序集中。

? 1: #if WINDOWS ? 2:???? <<針對Windows Desktop>> ? 3: #elif SILVERLIGHT ? 4:???? <<針對 Silverlight>> ? 5: #elif WINDOWS_PHONE ? 6:???? <<針對Windows Phone>> ? 7: #else ? 8:???? <<針對其他平臺>> ? 9: #endif

如果多個針對不同.NET Framework平臺的項目文件存在于同一個物理目錄下,存在于相同目錄下的源文件可以同時包含到這些項目中以實現共享的目的。如下圖所示,兩個分別針對SilverlightWPF的項目共享相同的目錄,與兩個項目文件同在一個目錄下的C#文件Shared.cs可以同時被包含到這兩個項目之中。

?

文件鏈接

當我們采用默認的方式將一個現有的文件添加到當前項目之中的時候,Visual Studio會將目標文件拷貝到項目本地的目錄下,所以根本起不到共享的目的。但是針對現有文件的添加支持一種叫做“鏈接”的方式使添加到項目中的文件指向的依然是原來的地址,我們可以為多個項目添加針對同一個文件的鏈接以實現源文件跨項目共享。同樣還是上面演示分別針對Silverlight和WPF的兩個項目,不論項目文件和需要被共享的文件存在于哪個目錄下面,我們都可以采用如下圖所示的添加文件鏈接的方式分享這個Shared.cs文件。

共享項目(Shared Project)

普通項目的目的都是組織源文件和其他相關資源并將它們最終編譯成一個可被部署的程序集。但是Shared Project這種項目類型則比較特別,它只有對源文件進行組織的功能,卻不能通過編譯生成程序集,它存在的目的就是為了實現源文件的共享。對于上面我們介紹的兩種源代碼的共享方式來說,它們都是針對某個單一文件的共享,而Shared Project則可以對多個源文件進行打包以實現批量共享。

如上圖所示,我們可以創建一個Shared Project類型的項目Shared.shproj,并將需要共享的三個C#文件(Foo.cs、Bar.cs和Baz.cs)添加進來。我們將針對這個項目的引用同時添加到一個Silverlight項目(SilverlightApp.csproj)和Windows Phone項目(WinPhoneApp.csproj)之中,當我們對這兩個項目實施編譯的時候,包含在項目Shared.shproj中的三個C#文件會自動作為當前項目的源文件參與編譯。??

二、程序集復用

我們采用C#、VB.NET這樣的編程語言編寫的源文件經過編譯會生成有IL代碼和元數據構成的托管模塊,一個或者多個托管模塊合并生成一個程序集。程序集的文件名、版本、語言文化和簽名的公鑰令牌共同組成了它的唯一標識,我們將該標識稱為程序集有效名稱(Assembly Qualified Name)。除了包含必要的托管模塊之外,我們還可以將其他文件作為資源內嵌到程序集中,程序集的文件構成一個“清單(Manifest)”文件來描述,這個清單文件包含在某個托管模塊中。

除了作為描述程序集文件構造清單之外,描述程序集的元數據也包含在這個清單文件中。程序集使程序集成為一個自描述性(Self-Describing)的部署單元,除了描述定義在本程序集中所有類型之外,這些元數據還包括對引用自外部程序集的描述。包含在元數據中針對外部程序集的描述是由編譯時引用的程序集決定的,引用程序集的名稱(包含文件名、版本和簽名的公鑰令牌)會直接體現在當前程序集的元數據中。針對程序集引用的元數據采用如下的形式(“.assembly extern”)被記錄在清單文件中,我們可以看出被記錄下來的不僅包含被引用的程序集文件名(“Foo”和“Bar”),還包括程序集的版本,對于簽名的程序集(“Foo”)來說,公鑰令牌也一并包含其中。

? 1: .assembly extern Foo ? 2: { ? 3:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 4:?? .ver 1:0:0:0 ? 5: } ? 6: .assembly extern Bar ? 7: { ? 8:?? .ver 1:0:0:0 ? 9: }

包含在當前程序集清單文件中針對引用程序集的元數據是CLR加載目標程序集的依據。在默認的情況下,CLR要求加載與程序集引用元數據完全一致的程序集。具體來說,如果引用的是一個未簽名的程序集(“Bar”),那么只要求被加載的程序集具有一致的文件名和版本;如果引用的是一個經過簽名的程序集,那么還要求被加載的程序集具有一致的公鑰令牌。

在回到《.NET Core跨平臺的奧秘[上篇]:歷史的枷鎖》關于.NET多目標框架獨立性的問題。雖然不同的目標框架的BCL在API層面具有很多交集,但是這些API實際上被定義在不同的程序集中,這就導致了在不同的目標框架下共享同一個程序集幾乎成了不可能的事情。如果要使跨目標平臺程序集復用成為現實,就必須要求CLR在加載程序集時放寬“完全匹配”的限制,因為針對當前程序集清單文件中描述的某個引用程序集來說,在不同的目標框架下可能指向不同的程序集。實際上確實存在這樣的一些機制或者策略讓CLR加載一個與引用元數據的描述不一致的程序集,我們現在就來聊聊這些策略。

程序集一致性

我們都知道.NET Framework是向后兼容的,也就是說原來針對低版本.NET Framework編譯生成的程序集是可以直接在高版本CLR下運行的。我們試想一下這么一個問題:就一個針對.NET Framework 2.0編譯生成的程序集自身來說,所有引用的基礎程序集的版本在元數據描述中都應該是2.0,如果這個程序集在NET Framework 4.0環境下執行,CLR在決定加載它所依賴程序集的時候,應該選擇2.0還是4.0呢?

我們不妨通過實驗來獲得這個問題的答案。我們利用Visual Studio創建一個針對.NET Framework 2.0的控制臺應用(命名為App),并在作為程序入口的Main方法上編寫如下一段代碼。如下面代碼片斷所示,我們在控制臺上輸出了三個基本類型(Int32、XmlDocument和DataSet)所在程序集的全名。

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(typeof(int).Assembly.FullName); ? 6:???????? Console.WriteLine(typeof(XmlDocument).Assembly.FullName); ? 7:???????? Console.WriteLine(typeof(DataSet).Assembly.FullName); ? 8:???? } ? 9: }

直接運行這段程序使之在默認版本的CLR(2.0)下運行會在控制臺上輸出如下的結果,我們會發現上述三個基本類型所在程序集的版本都是2.0.0.0。也就說在這種情況下,運行時加載的程序集和編譯時引用的程序集是一致的。

現在我們在目錄“\bin\debug”直接找到以Debug模式編譯生成的程序集App.exe,并按照如下的形式修改對應的配置文件(App.exe.config),該配置的目的在于將啟動應用時采用的運行時(CLR)版本從默認的2.0切換到4.0。

? 1: <configuration> ? 2:?? <startup> ? 3:???? <supportedRuntime&nbsp;version="v4.0"/> ? 4:?? </startup> ? 5: </configuration>

或者:

? 1: <configuration> ? 2:?? <startup> ? 3:???? <requiredRuntime&nbsp;version="v4.0"/> ? 4:?? </startup> ? 5: </configuration>

無需重新編譯(確保運行的依然是同一個程序集)直接運行App.exe,我們會在控制臺上得到如下圖所示的輸出結果,可以看到三個程序集的版本全部變成了4.0.0.0,也就說真正被CLR加載的這些基礎程序集是與當前CLR的版本相匹配的。

這個簡單的實例體現了這么一個特征:運行過程中加載的.NET Framework程序集(承載FCL的程序集)是由當前運行時(CLR)決定的,這些程序集的版本總是與CLR的版本相匹配。包含在元數據中的程序集信息提供目標程序集的名稱,而版本則由當前運行的CLR來決定,我們將這個重要的機制稱為“程序集一致性(Assembly Unification)”,下圖很清晰地揭示了這個特性。

?

Retargetable程序集

在默認情況下,如果某個程序集引用了另一個具有強簽名的程序集,CLR在執行的時候總是會根據程序集文件名、版本和公鑰令牌去定位目標程序集。如果無法找到一個與之完全匹配的程序集,一般情況下會拋出一個FileNotFoundException類型的異常。如果當前引用的是一個Retargetable程序集,則意味著CLR在定位目標程序集的時候可以 “放寬” 匹配的要求,即指要求目標程序集具有相同的文件名即可。

如下圖所示,我們的應用程序(App)引用了具有強簽名的程序集“Foobar, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”,所以對于編譯后生成的程序集App.exe來說,對應的程序集引用將包含目標程序集的文件名、版本和公鑰令牌。如果在運行的時候只提供了一個有效名稱為“Foobar, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d7fg7asdf7asd7aer”的程序集,除了文件名,后者的版本號和公鑰令牌都與程序集引用元數據描述的都不一樣。在默認情況下,系統此時總是會拋出一個FileNotFoundException類型的異常,倘若Foobar是一個Retargetable程序集,我們提供的將作為目標程序集被加載并使用。

除了定義程序集的元數據多了如下一個retargetable標記之外,Retargetable程序集與普通程序集并沒有本質區別。

普通程序集:??

.assembly Foobar

Retargetable程序集:?

.assembly retargetable Foobar

這樣一個retargetable標記可以通過按照如下所示的方式在程序集上應用AssemblyFlagsAttribute特性來添加。不過這樣的重定向僅僅是針對.NET Framework自身提供的基礎程序集有效,雖然我們也可以通過使用AssemblyFlagsAttribute特性為自定義的程序集添加這樣一個retargetable標記,但是CLR并不會賦予它重定向的能力。

[assembly:AssemblyFlags(AssemblyNameFlags.Retargetable)]

如果某個程序集引用了一個Retargetable程序集,自身清單文件針對該程序集的引用元數據同樣具有如下所示的retargetable標記。CLR正式利用這個標記確定它引用的是否是一個Retargetable程序集,進而確定針對該程序集的加載策略,即采用針對文件名、版本和公鑰令牌的完全匹配策略,還是采用只針對文件名的降級匹配策略。

針對普通程序集的引用:

? 1: 針對普通程序集的引用 ? 2: .assembly extern Foobar ? 3: { ? 4:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 5:?? .ver 1:0:0:0 ? 6: }

針對Retargetable程序集的引用:

? 1: .assembly extern?retargetable Foobar ? 2: { ? 3:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89)???????????????????????? ? 4:?? .ver 1:0:0:0 ? 5: }

類型的轉移

在進行框架或者產品升級過程,我們經常會遇到針對程序集的合并和拆分的場景,比如在新版本中需要對現有的API進行從新規劃,可能會將定義在程序集A中定義的類型轉移到程序集B中。但是即使發生了這樣的情況,我們依然需要為新框架或者產品提供向后兼容的能力,這就需要使用到所謂“類型轉移(Type Forwarding)”的特性。

為了讓讀者朋友們對類型轉移這個重要的特性具有一個大體的認識,我們來作一個簡單的實例演示。我們利用Visual Studio創建一個針對.NET Framework 3.5的控制臺應用App,并在作為程序入口的Main方法中編寫了如下兩行代碼將兩個常用的類型(StringFunc<>)所在的程序集名打印出來。程序編譯之后會在 “\bin\Debug” 目錄下生成可執行文件App.exe和對應的配置文件App.exe.config。從如下給出的配置文件內容可以看出.NET Framework 3.5采用的運行時(CLR)版本為 “v2.0.50727” 。

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(typeof(string).Assembly.FullName); ? 6:???????? Console.WriteLine(typeof(Func<>).Assembly.FullName); ? 7:???? } ? 8: }

App.exe.config

? 1: <configuration> ? 2:?? <startup> ? 3:???? <supportedRuntime&nbsp;version="v2.0.50727"/></startup> ? 4:?? </startup> ? 5: </configuration>

現在我們直接以命令行的執行執行編譯生成的App.exe后會在控制臺上得到如下圖所示的輸出結果。可以看出對于我們給出的這兩個基礎類型(String和Func<>),只有String類型被定義在程序集mscorlib.dll之中,而類型Func<>其實被定義在另一個叫做System.Core.dll的程序集之中。其實Framework 2.0、3.0和3.5不僅僅共享相同的運行時(CLR 2.0),對于提供基礎類型的核心程序集mscorlib.dll也是共享的,下圖輸出的版本信息已經說明了這一點。也就是說,.NET Framework 2.0發布時提供的程序集mscorlib.dll在.NET Framework 3.x時代就沒有升級過。Func<>類型是在.NET Framework 3.5發布時提供的一個基礎類型,所以不得不將它定義在一個另一個程序集中,微軟將這個程序集命令為System.Core.dll

?

現在我們看看.NET Framework 4.0(CLR 4.0)環境下運行同一個應用程序(App.exe)是否會有不同的輸出結果。為此我們在不對項目做重新編譯情況下直接修改配置文件App.exe.config,并按照如下所示的方式將運行時版本設置為4.0。

? 1: <configuration> ? 2:?? <startup> ? 3:???? <supportedRuntime&nbsp;version="v4.0"/> ? 4:?? </startup> ? 5: </configuration>

下圖是同一個App.exe在.NET Framework 4.0環境下的輸出結果,可以看出我們提供的兩個基礎類型所在的程序集都是mscorlib.dll。也就是當.NET Framework升級到4.0之后,不僅僅運行時升級到了全新的CLR 4.0,微軟同時也對承載基礎類型的mscorelib.dll程序集進行了重新規劃,所以定義在System.Core.dll程序集中的基礎類型也基本上又重新回到了mscorlib.dll這個本應該屬于它的程序集中。

我們來繼續分析上面演示的這個程序。由于App.exe這個程序集最初是針對目標框架.NET Framework 3.5編譯生成的,所以它的清單文件將包含針對mscorlib.dll(2.0.0.0)和System.Core.dll(3.5.0.0)的程序集引用。下面的代碼片段展示了針對這兩個程序集引用的元數據的定義。

? 1: .assembly extern mscorlib ? 2: { ? 3:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 4:?? .ver 2:0:0:0 ? 5: } ? 6: .assembly extern System.Core ? 7: { ? 8:?? .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )???????????????????????? ? 9:?? .ver 3:5:0:0 ?10: }

當App.exe在.NET Framework 4.0環境中運行時,由于它的元數據提供的是針對System.Core.dll程序集的引用,所以CLR總是試圖加載該程序集并從中定位目標類型(比如我們演示實例中的類型Func<>)。如果當前運行環境無法提供這個程序集,那么毫無疑問,一個FileNotFoundException類型的異常會被拋出來。也就是,雖然類型Func<>在.NET Framework 4.0中已經轉移到了新的程序集mscorlib.dll中,當前環境依然會提供一個文件名為System.Core.dll的程序集。

System.Core.dll存在的目的是告訴CLR它需要加載的類型已經發生轉移,并將該類型所在的新的程序集名稱告訴它,那么.NET Framework 4.0環境中的System.Core.dll是如何描述類型Func<>已經轉移到程序集mscorelib.dll之中了呢?如果分析程序集System.Core.dll中的元數據,我們可以看到如下一段于此相關的代碼。在程序集的清單文件中,每一個被轉移的類型都對應這個這么一個 “.class extern forwarder” 指令。

? 1: .class extern forwarder System.Func`1 ? 2: { ? 3:?? .assembly extern mscorlib ? 4: }

不同于上面介紹的Retargetable程序集,類型的轉移并不是只針對.NET Framework提供的基礎程序集,如果我們自己開發的項目也需要提供類似的向后兼容性,也可以使用這個特性。針對類型轉移類型的編程只涉及到一個類型為TypeForwardedToAttribute的特性,接下來我們通過一個簡單的實例來演示一下如何利用這個特性將某個類型轉移到一個新的程序集中。

我們利用Visual Studio創建了如下圖所示的解決方案,它演示了這樣一個場景:控制臺應用使用到了V1版本的類庫Lib(v1\Lib),其中涉及到一個核心類型Foobar。該類庫升級到V2版本時,我們選擇將所有的核心類型統一定義在新的程序集Lib.Core中,所以類型Foobar需要轉移到Lib.Core中。作為類庫的發布者,我們希望使用到V1版本的應用能夠直接升級到V2版本,也就是升級的應用不需要在引用新的Lib.Core程序集情況下對源代碼進行重新編譯,而是直接部署V2版本的兩個程序集(Lib.dll和Lib.Core)就可以了。

上圖中的虛線箭頭和實線箭頭分別代表項目之間的引用關系,我們從中可以看出v2目錄下的Lib項目具有對Lib.Core項目的引用,因為它需要引用轉移到Lib.Core項目中的類型。為了完成針對類型Foobar的轉移,我們只需要在v2\Lib中定義如下一行簡單的代碼就可以了,我們將這行代碼定義在AssemblyInfo.cs文件中。

? [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Lib.Foobar))]

為了檢驗針對Foobar類型的轉移是否成功,我們在控制臺應用App中定義了如下一段程序,它負責將Foobar類型當前所在程序集的名稱輸出到控制臺上。接下來我們只需要編譯(以Debug模式)整個解決方案,那么V2版本的兩個程序集(Lib.dll和Lib.Core.dll)將保存到\v2\lib\bin\debug\目錄下。

? 1: class Program ? 2: { ? 3:???? static?void Main() ? 4:???? { ? 5:???????? Console.WriteLine(typeof(Foobar).Assembly.FullName); ? 6:???? } ? 7: }

接下來我們采用命令行的形式來運行控制臺程序App.exe。如下圖所示,我們將當前目錄切換到App.exe所在的目錄(\app\bin\debug)下并執行App.exe,輸出的結果表明Foobar類型當前所在的程序集為Lib.dll。接下來我們將針對V2版本的兩個程序集拷貝進來后再次執行App.exe,我們發現此時的Foobar類型已經是從新的程序集Lib.Core.dll中加載的了。

我們順便來查看一下V2版本程序集Lib.dll的清單文件的內容。如下面的代碼片段所示,在源代碼中通過使用TypeForwardedToAttribute特性定義的類型轉移在編譯之后被轉換成了一個“.class extern forwarder”指令。

? 1: .assembly extern Lib.Core ? 2: { ? 3:?? .ver 1:0:0:0 ? 4: } ? 5: .class?extern forwarder Lib.Foobar ? 6: { ? 7:?? .assembly extern Lib.Core ? 8: } ? 9: …

三、可移植類庫(PCL)

在.NET Framework的時代,創建可移植類庫(PCL:Portable Class Library)是實現跨多個目標框架程序集共享的唯一途徑。上面介紹的內容都是在為PCL做鋪墊,只有充分理解了Retargetable程序集類型轉移的前提下才可能了解PCL的實現原理有正確的理解。考慮到很多讀者朋友并沒有使用PCL的經歷,所以我們先來介紹一下如何創建一個PCL項目。 當我們采用Visualization Studio的Class Library(Portal)項目模板創建一個PCL項目的時候,需要在如下圖所示的對話框中選擇支持的目標框架及其版本。Visual Studio會為新建的項目添加一個名為 “.NET” 的引用,這個引用指向一個由選定目標框架決定的程序集列表。由于這些程序集提供的API能夠兼容所有選擇的平臺,我們在此基礎編寫的程序自然也具有平臺兼容性。

?

如果查看這個特殊的.NET引用所在的地址,我們會發現它指向目錄“%ProgramFiles%\Reference Assemblies\Microsoft\Framework\.NETPortable\{version}\Profile\ProfileX”。如果查看 “%ProgramFiles%\Reference Assemblies\Microsoft\Framework\.NETPortable” 目錄,我們會發現它具有如下圖所示的結構。

?

如上圖所示,本機所在目錄“%ProgramFiles%\Reference Assemblies\Microsoft\Framework\.NETPortable”下具有三個代表.NET Framework版本的子目錄(v4.0、v4.5和v4.6)。具體到針對某個.NET Framework版本的目錄(比如v4.6),其子目錄Profile下具有一系列以 “Profile” + “數字” (比如Profile31、Profile32和Profile44等)命名的子目錄,實際上PCL項目引用的就是存儲在這些目錄下的程序集

對于兩個不同平臺的.NET Framework來說,它們的BCL在API的定義上存在交集,從理論上來說,建立在這個交集基礎上的程序是可以被這兩個平臺中共享的。如下圖所示,如果我們編寫的代碼需要分別對Windows Desktop/Phone、Windows Phone/Store和Windows Store/Desktop平臺提供支持,那么這樣的代碼依賴的部分僅限于兩兩的交集A+B、A+C和A+D。如果要求這部分代碼能夠運行在Windows Desktop/Phone/Store三個平臺上,那么它們只能建立在三者之間的交集A上。??

針對所有可能的目標框架(包括版本)的組合,微軟會將作為兩者交集的API提取出來并定義在相應的程序集中。比如說所有的目標框架都包含一個核心的程序集mscorlib.dll,雖然定義其中的類型及其成員在各個目標框架不盡相同,但是它們之間肯定存在交集,微軟針對不同的目標框架組合將這些交集提取出來并定義在一系列同名程序集中,并同樣命名為mscorlib.dll。 微軟按照這樣的方式創建了其他針對不同.NET Framework平臺組合的基礎程序集,這些針對某個組合的所有程序集構成一系列的Profile,并定義在上面我們提到過的目錄下。值得一提的是,所有這些針對某個Profile的程序集均為Retargetable程序集。

當我們創建一個PCL項目的時候,第一個必需的步驟是選擇兼容的目標框架(和版本),Visual Studio會根據我們的選擇確定一個具體的Profile,并為創建的項目添加針對該Profile的程序集引用。由于所有引用的程序集是根據我們選擇的目標框架組合 “度身定制” 的,所以定義在PCL項目的代碼才具有可移植的能力。

上面我們僅僅從開發的角度解釋了定義在PCL項目的代碼本身為什么能夠確保是與目標.NET Framework平臺兼容的,但是在運行的角度來看這個問題,卻存在額外兩個問題:

  • 元數據描述的引用程序集與真實加載的程序集不一致,比如我們創建一個兼容.NET Framework 4.5和Silverlight 5.0的PCL項目,被引用的程序集mscorlib.dll的版本為2.0.5.0,但是Silverlight 5.0運行時環境中的程序集mscorlib.dll的版本則為5.0.5.0

  • 元數據描述的引用程序集的類型定義與運行時加載程序集類型定義不一致,比如引用程序集中的某個類型被轉移到了另一個程序集中。

由于PCL項目在編譯時引用的均為Retargetable程序集,所以程序集的重定向機制幫助我們解決了第一個問題。因為在CLR在加載某個Retargetable程序集的時候,如果找不到一個與引用程序集在文件名、版本、語言文化和公鑰令牌完全匹配的程序集,則會只考慮文件名的一致性。至于第二個問題,自然可以通過上面我們介紹的類型轉移機制來解決。

?

綜上所述,雖然微軟在針對多個目標框架的代碼復用上面為我們提供了一些解決方案。在源代碼共享方面,我們可以采用共享項目,雖然共享項目能夠做到將一組源文件進行打包復用,但是我個人基本上不怎么用它,因為如果我們在其中定義一些公有類型,那么引用該共享項目的項目之間會造成命名沖突。從另一方面講,我們真正需要的是程序集層面的復用,但是在這方面微軟只為我們提供了PCL。PCL這種采用提取目標框架API交集的方式注定了只能是一種臨時的解決方案,試著想一下:如果目標框架由10種,每種有3個版本,我們需要為多少種組合創建相應的Profile。對于開發者來說,如果目標框架(包括版本),我們在創建PCL項目進行兼容框架的選擇都會成問題。所以我們針對希望的是能夠提供給全平臺支持的BCL,你可以已經知道了,這就是Net Standard,那么Net Standard是如何能夠在多個目標框架中復用的呢?請求關注本系列終結篇《.NET Core跨平臺的奧秘[下篇]:全新的布局》。

原文地址:http://www.cnblogs.com/artech/p/how-to-cross-platform-02.html


.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com

總結

以上是生活随笔為你收集整理的.NET Core跨平台的奥秘[中篇]:复用之殇的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。