時間:2017-11-08 來源:互聯網 瀏覽量:
在《.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也不能跨平台共享,它為開發者帶來的一個最大的問題就是:很難編寫能夠在各個目標框架複用的代碼。比較極端的場景就是:當我們需要為一個現有的桌麵應用提供針對移動設備的支持時,我們不得不從頭到尾開發一個全新的應用,現有的代碼難以被新的應用所複用用。 “代碼複用”是軟件設計一項最為根本的目標,在不考慮跨平台的前提下,我們可以應用相應的設計模式和編程技巧來實現代碼的重用,但是平台之間的差異導致了跨平台代碼重用確實具有不小的困難。雖然作得不算非常的理想,但是微軟在這方麵確實做出了很多嚐試,我們不妨先來聊聊目前我們都有哪些跨平台代碼複用的解決方案。
一、源代碼複用對於包括Mono在內的各個.NET Framework平台的BCL來說,雖然在API定義層麵上存在一些共同之處,但是由於它們定義在不同的程序集之中,所以在PCL(Portal Class Library)推出之前,針對程序集的共享是不可能實現的,我們隻能在源代碼層麵實現共享。源代碼的共享通過在不同項目之間共享源文件的方式來實現,至於具體采用的方式,我們有三種不同的方案供你選擇。
源文件共享
對於一個能夠多個針對不同目標框架的項目共享的源文件,定義其中的代碼也有不少是針對具體某個目標框架的。對於這種代碼,我們需要按照如下的方式進行編寫,相應的項目以添加編譯的方式選擇與自身平台相匹配的代碼編譯道生成的程序集中。
#if WINDOWS
<<針對Windows Desktop>>
#elif SILVERLIGHT
<<針對 Silverlight>>
#elif WINDOWS_PHONE
<<針對Windows Phone>>
#else
<<針對其他平台>>
#endif
如果多個針對不同.NET Framework平台的項目文件存在於同一個物理目錄下,存在於相同目錄下的源文件可以同時包含到這些項目中以實現共享的目的。如下圖所示,兩個分別針對Silverlight和WPF的項目共享相同的目錄,與兩個項目文件同在一個目錄下的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”)來說,公鑰令牌也一並包含其中。
.assembly extern Foo
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 1:0:0:0
}
.assembly extern Bar
{
.ver 1:0:0:0
}
包含在當前程序集清單文件中針對引用程序集的元數據是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)所在程序集的全名。
class Program
{
static void Main()
{
Console.WriteLine(typeof(int).Assembly.FullName);
Console.WriteLine(typeof(XmlDocument).Assembly.FullName);
Console.WriteLine(typeof(DataSet).Assembly.FullName);
}
}
直接運行這段程序使之在默認版本的CLR(2.0)下運行會在控製台上輸出如下的結果,我們會發現上述三個基本類型所在程序集的版本都是2.0.0.0。也就說在這種情況下,運行時加載的程序集和編譯時引用的程序集是一致的。
現在我們在目錄“\bin\debug”直接找到以Debug模式編譯生成的程序集App.exe,並按照如下的形式修改對應的配置文件(App.exe.config),該配置的目的在於將啟動應用時采用的運行時(CLR)版本從默認的2.0切換到4.0。
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
</startup>
</configuration>
或者:
<configuration>
<startup>
<requiredRuntime version="v4.0"/>
</startup>
</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程序集,進而確定針對該程序集的加載策略,即采用針對文件名、版本和公鑰令牌的完全匹配策略,還是采用隻針對文件名的降級匹配策略。
針對普通程序集的引用:
.assembly extern Foobar
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 1:0:0:0
}
針對Retargetable程序集的引用:
.assembly extern retargetable Foobar
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 1:0:0:0
}
類型的轉移
在進行框架或者產品升級過程,我們經常會遇到針對程序集的合並和拆分的場景,比如在新版本中需要對現有的API進行從新規劃,可能會將定義在程序集A中定義的類型轉移到程序集B中。但是即使發生了這樣的情況,我們依然需要為新框架或者產品提供向後兼容的能力,這就需要使用到所謂“類型轉移(Type Forwarding)”的特性。
為了讓讀者朋友們對類型轉移這個重要的特性具有一個大體的認識,我們來作一個簡單的實例演示。我們利用Visual Studio創建一個針對.NET Framework 3.5的控製台應用App,並在作為程序入口的Main方法中編寫了如下兩行代碼將兩個常用的類型(String和Func<>)所在的程序集名打印出來。程序編譯之後會在 “\bin\Debug” 目錄下生成可執行文件App.exe和對應的配置文件App.exe.config。從如下給出的配置文件內容可以看出.NET Framework 3.5采用的運行時(CLR)版本為 “v2.0.50727” 。
class Program
{
static void Main()
{
Console.WriteLine(typeof(string).Assembly.FullName);
Console.WriteLine(typeof(Func<>).Assembly.FullName);
}
}
App.exe.config
<configuration>
<startup>
<supportedRuntime version="v2.0.50727"/></startup>
</startup>
</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。
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
</startup>
</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)的程序集引用。下麵的代碼片段展示了針對這兩個程序集引用的元數據的定義。
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 2:0:0:0
}
.assembly extern System.Core
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 3:5:0:0
}
當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” 指令。
.class extern forwarder System.Func`1
{
.assembly extern mscorlib
}
不同於上麵介紹的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\目錄下。
class Program
{
static void Main()
{
Console.WriteLine(typeof(Foobar).Assembly.FullName);
}
}
接下來我們采用命令行的形式來運行控製台程序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”指令。
.assembly extern Lib.Core
{
.ver 1:0:0:0
}
.class extern forwarder Lib.Foobar
{
.assembly extern Lib.Core
}
…
三、可移植類庫(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跨平台的奧秘[下篇]:全新的布局》。
作者:蔣金楠
原文://www.cnblogs.com/artech/p/how-to-cross-platform-02.html