在 Visual Studio 2008 之前,編寫面向不同版本的 Microsoft .NET Framework 的應用程序需要安裝不同版本的 Visual Studio 開發環境。每個版本的 Visual Studio 都提供了不同的開發人員體驗,並會占用大量磁盤空間。而且,每個版本的 Visual Studio 的項目文件格式也各不相同。結果就是當您開發面向不同版本的 .NET Framework 的項目組件時,您會得到多個版本的項目文件或解決方案。
Visual Studio 2008 是第一個在單個 IDE 中完全支持多重目標的版本,允許開發人員使用一個版本的 Visual Studio 編寫面向不同版本的 .NET Framework(2.0、3.0 和 3.5)的應用程序。結果如何?開發人員可獲得一致的使用體驗,同時減少磁盤空間需求。
Visual Studio 2008 能夠實現多重目標是因為每個可用的框架都使用了相同的基礎 CLR 2.0。而且,每個版本的框架都構建在 .NET Framework 2.0 的基礎上,並通過使用被引用程序集來提供其他功能。最終,所有框架都使用 .NET Framework 3.5 命令行 Visual Basic 編譯器 (vbc.exe)。
本文將討論 3.5 和 4 編譯器,即分別包含在 .NET Framework 3.5 和 4 中的編譯器。3.5 編譯器是隨 Visual Studio 2008 和 Visual Basic 9 提供的版本,而 4 編譯器是隨 Visual Studio 2010 和 Visual Basic 10 提供的版本。
讓我們看一下當前多重目標在 Visual Studio 中的工作原理,並說明應該如何在項目中實現多重目標。
Visual Studio 中的多重目標
在 Visual Studio 2008 中,更改所需的目標框架很簡單,只需從項目屬性中的下拉列表選擇目標即可,如圖 1 所示。這可以添加或刪除每個框架版本所需的特定引用,並輕松地更改框架。
圖 1 在 Visual Studio 2008 中更改所需的目標框架
對於命令行編譯,只需更改所用的引用程序集即可。
但在 Visual Studio 2010 中有了一些重大變更。新的 .NET Framework 4 引入了新版本的 CLR。這意味著 Visual Studio 2008 中采用的方法不能用於 Visual Studio 2010。因此,Visual Studio 2010 使用版本 4 的編譯器用於所有的多重目標,即使目標是舊版本的 .NET Framework。這就允許在向下定向時使用很多新的語言功能,並提供大為簡化的開發體驗。
但是,可以針對低級目標使用 Visual Studio 2010 功能的一個缺點是,如果用於之前版本的 Visual Studio,源文件可能不是設計時兼容的。如果您要共享使用不同版本的 Visual Studio 構建且面向不同版本的 .NET Framework 的項目的源代碼,就會遇到問題。
如果項目始終保持在 Visual Studio 2010 中,用於所有設計時工作,則您將獲得更好的體驗。您只需使用 Visual Studio 2010 和 .NET Framework 3.5 SP1,即可生成可在 .NET Framework 2.0 及以後版本中使用的程序集。
設計時兼容性
現在讓我們來看一個設計時兼容性陷阱的示例。圖 2 中的代碼使用了隱式行繼續符和自動實現的屬性功能,這兩者都是在 Visual Studio 2010 中引入的。使用 Visual Studio 2010 進行編譯時,這段代碼可以編譯為面向所有 2.0 以上版本的框架。因此生成的程序集是運行時兼容的。
圖 2 使用可在低級目標中工作的新語言功能
但是,同樣是這個源代碼文件,使用版本 3.5 或 2.0 的編譯器進行編譯後,您將得到圖 3 中所示的錯誤。
圖 3 來自 Visual Studio 2010 的源代碼與 Visual Studio 2008 不是設計時兼容的
發生這個問題是由於早期版本的編譯器完全不了解這些功能,會將其視為無效代碼。因此源文件不是設計時兼容的。若要使其成為設計時兼容的,您必須只使用在 3.5 編譯器中可用的功能。
設計時兼容性對於 Web 項目同樣有意義。很多 Web 項目的編譯發生在服務器上,而服務器肯定會使用其上安裝的目標框架編譯器來編譯頁面。如果您的 Web 頁面是在 Visual Basic 中編寫且面向 3.5 編譯器的,則頁面將在服務器上使用版本 3.5 的 Visual Basic 編譯器 (vbc.exe) 進行編譯。使用新的 Visual Studio 2010 語言功能必將導致失敗,因為 3.5 編譯器對它們一無所知。
對於在 Visual Studio 2010 中開發且使用版本 4 編譯器的代碼,您需要通過某種方法在編譯時識別此要求,以防止 Web 頁面部署到服務器時發生意外錯誤。為此您可以使用 /langversion 開關。此開關在開發 Web 項目時使用,可以生成有關新版語言語法功能與早期框架編譯器不兼容的錯誤。構建 ASP.NET 項目時,如果您的代碼使用新的 Visual Studio 2010 功能但您面向的是早期版本的框架,則在內部使用此開關可以生成錯誤。
盡管在默認情況下,/langversion 開關不會用於其他類型的項目,但如果您要驗證源代碼與舊版本的 Visual Studio 是否是設計時兼容的,則此開關會很有用。
Visual Studio 2010 IDE 中的多重目標
Visual Studio 2010 IDE 中的多重目標用戶體驗幾乎與 Visual Studio 2008 中的完全一致,它仍然從項目屬性內部進行控制,但在默認安裝中,您可能不會看到早期目標框架。為了減小安裝大小,Visual Studio 團隊決定在 Visual Studio 2010 的默認安裝中不提供 3.5 框架。這一變化意味著您在“目標框架”下拉列表或“新建項目”對話框中看不到這些框架選項。
若要添加這些框架,您需要安裝 .NET Framework 3.5 SP1。您可以從 IDE 執行此操作。在“新建項目”對話框的上部,您將看到下拉菜單,可以選擇目標框架。如果只安裝了 .NET Framework 4,則此菜單包含一個鏈接,用於下載更多框架。但如果您安裝了其他版本,則在下拉菜單中只能看到 .NET Framework 3.5 SP1,因為 Visual Studio 在這裡只能識別已安裝的 .NET Framework 3.5 SP1。
另一項更改與客戶端配置文件有關。這些配置文件在 .NET Framework 3.5 SP1 中引入,可讓應用程序使用精簡版的框架,從而能夠改進部署,因為不再要求包括服務器端的框架組件,例如 ASP.NET。這些配置文件在 3.5 和 4 框架目標中都可用。
因此,各種項目類型的默認配置文件都已更改。客戶端項目類型(Windows、Console、Office 和 Windows Presentation Foundation (WPF) 應用程序)默認情況下都使用客戶端配置文件。但是,對於 Web 應用程序來說,默認的配置文件是“完全”配置文件,因為要引用未使用客戶端配置文件部署的庫,例如 System.Web。
類庫默認情況下也采用完全配置文件,但如果您只依賴使用客戶端配置文件部署的引用,則可以很輕松地更改回客戶端配置文件。如果您的類庫設置為完全配置文件,然後在使用客戶端配置文件的項目中使用,只要該庫並不依賴不屬於客戶端框架程序集的引用,該類庫就可以正常工作。
默認情況下,添加到類庫項目類型的引用都不要求完全配置文件。但是,由於它們隨應用程序一起部署,因此應用程序部署配置文件是確保應用程序正常運行的重要設置。如果您的庫依賴客戶端范圍之外的引用,則庫和使用該庫的應用程序都需要使用完全配置文件。
使用命令行編譯器實現多重目標
版本 4 編譯器有許多命令行開關,但遺憾的是,沒有哪個開關可以控制目標框架,因此有必要稍稍了解這些開關以及它們的工作原理。
如果安裝了 .NET Framework 4,則可以使用 vbc.exe 構建面向早期版本的框架的應用程序,而不需要在構建計算機上安裝 Visual Studio。直接調用命令行編譯器的構建腳本常用於大型開發環境。如果您從命令行定向到早期版本,則要求使用您面向的早期框架版本安裝文件,因此最佳方案是在計算機上同時安裝 .NET Framework 3.5 SP1 和 .NET Framework 4。
考慮到這一點,圖 4 詳細介紹了一些用於多重目標的潛在開關。
圖 4 用於控制多重目標的命令行構建開關
開關 說明 langversion 為使用不符合特定語言版本的功能的源代碼提供錯誤。(9.0 與 .NET Framework 3.5 及以上版本的目標相關;10 與 .NET Framework 4 目標相關。)這實際上並不確定所用的目標框架或 CLR,但允許 Web 項目識別在向下定向方案中使用的 Visual Studio 2010 功能。 vbruntime 盡管有不同版本的 Microsoft.VisualBasic 可用於 .NET Framework 4,但簡單地嘗試指定版本 2.0 的 Microsoft.VisualBasic.dll 不能解決問題,而且會導致程序集依賴於版本 4 的 NetFX。 nostdlib 阻止向程序集中添加對 system.dll 的標准引用。盡管可以在 2.0 框架中使用此選項和對 system.dll 版本的引用,但結果仍是版本 4 的程序集。 sdkpath 如果 vbruntime 開關沒有指定使用 MSCorLib.dll 還是 Microsoft.VisualBasic.dll,則此選項就是一個關鍵選項,可指定使用哪個版本的 MSCorLib.dll 和 Microsoft.VisualBasic.dll。但是,這並不是您通常在引用列表中看到的顯式引用。相反,編譯器將其包含在標准引用中。當您需要版本 2.0 而不是版本 4 的 MSCorLib 時,需要在解決方案中添加它以現實多重目標。 noconfig 使編譯器避免加入 vbc.rsp 文件中包含的默認引用、導入和開關,否則就將使用這些內容。
這個表格簡要介紹了各個開關,但為了實際創建向下定向的編譯,您將需要組合使用多個開關,因為沒有一個單獨的多重目標開關。對於版本 3.5 的目標,最重要的開關是 sdkpath,您可以用它來指定版本 2.0 的 MSCorlib。然後確保您的引用指向正確版本的 System.dll、System.core.dll 和其他早期目標框架程序集。(這些文件可以在 %programfiles%\Reference Assemblies\Microsoft\Framework\v3.5 文件夾中找到。)
您需要指定 noconfig 開關以避免使用版本 4 的 vbc.rsp(包含了默認的編譯設置)中的默認開關。如果不添加這個重要的開關,編譯程序將添加那些默認的版本 4 引用、導入等等。
多重目標命令行編譯可通過示例進行最好的演示。在這裡,我只是面向 .NET Framework 3.5 編譯一個簡單的源文件 test.vb:
vbc.exe /noconfig /sdkpath:D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 /r:"D:\Program Files\ReferenceAssemblies\Microsoft\Framework\v3.5\System.Core.dll" d:\school\test.vb /out:\school\test.exe
當您了解編譯 3.5 程序集的開關後,面向 2.0 只需要刪除一些 3.5 框架所需的引用,例如 system.core.dll。sdkpath 和 noconfig 開關保持不變:
vbc.exe /noconfig /sdkpath:D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 d:\school\test.vb /out:\school\test.exe
編譯二進制文件之後,您可以使用 MSIL Disassembler (Ildasm.exe) 或 .NET Reflector 等工具來查看所使用的引用的版本。這可以讓您確定可執行文件是否在所需的目標框架上運行。
客戶端配置文件和多重目標解決方案
在前文中,我提到客戶端配置文件是大多數項目類型的默認設置。部署這樣的應用程序時,將安裝更精簡的框架。在部署期間,您將看到還會部署命令行編譯器。該編譯器的版本與使用完全框架部署時相同,但是嘗試使用客戶端配置文件來編譯簡單應用程序時,可能會遇到陷阱。
在客戶端框架計算機上使用此命令可能會失敗,因為客戶端框架並未隨附包含默認引用的 vbc.rsp:
vbc.exe test.vb /out: test.exe
您必須指定 vbc.rsp 文件中通常會包含的所有引用和導入語句,或者自行創建這些語句。
在客戶端框架上編譯 Visual Basic 應用程序最少需要以下開關:
/r:System.dll
/imports:System
/imports:Microsoft.VisualBasic
加入這些開關您就可以編譯基本的 Visual Basic 應用程序。但是,您應該在安裝了完全框架的計算機上編譯應用程序。
多重目標解決方案(使用 .NET Framework 3.5 構建的類庫用在面向 .NET Framework 4 的客戶端應用程序中)是受支持的,但有一些需要注意的地方。在 Visual Studio 2010 IDE 中,如果您使用項目引用並且熟悉這種體驗,則您仍將獲得這種體驗,前提是項目引用中的目標框架使用相同版本的 MSCorlib。圖 5 顯示了 MSCorlib 版本和受支持的框架版本。
圖 5 MSCorlib 和框架版本兼容性
MSCorlib 版本 支持的框架 配置文件 2.0 2.0、3.0、3.5 客戶端和完全配置文件 4.0 4 客戶端和完全配置文件
如果您在 .NET Framework 3.5 應用程序中使用面向 MSCorlib 2.0 的類庫,您仍可以使用項目引用。同樣,由 .NET Framework 4 客戶端配置文件 Windows 應用程序引用的 .NET Framework 4 完全配置文件類庫可以擁有對該庫的項目引用。
但是,如果您使用項目到項目的引用,而這些項目中使用的 MSCorlib 版本不同,則項目引用將被轉換為文件引用。這意味著在您更正錯誤時需要手動重建解決方案。如果您曾經處理過擁有多個分別用 C# 和 Visual Basic 編寫的引用項目的解決方案,則會熟悉這種經驗。您會失去一些項目引用所具備的方便功能,例如在項目間重命名以及自動的後台編譯。
在編譯期間,IDE 在一定程度上為您屏蔽了後台執行的操作,但並不是每個人都從 IDE 構建程序。如果對目標框架進行更改,使兩個引用所使用的框架具有同一版本的 MSCorlib,則文件引用將自動變回項目引用,因此這並不是真正的文件引用。
如果您在版本 4 應用程序中使用向下定向的 (3.5) 類庫,會發生什麼呢?類庫實際將使用 .NET Framework 4。需要大量測試以確保此方案運行時不出現問題。但是,在 3.5 框架應用程序中使用 4.0 框架類庫不受支持,並會造成使用 Visual Studio 或 MSBuild 構建時發生編譯時錯誤。若要在面向 3.5 框架的應用程序中使用 4.0 框架類庫,您可能需要將類庫向下定向到 .NET Framework 3.5。
請記住,由於可以在向下定向的方案中使用 Visual Studio 2010 語言功能,因此將類庫定向到 .NET Framework 3.5 應該不是大問題。圖 6 總結了您可以在向下定向的項目中使用的新功能。
圖 6 向下定向方案中的新 Visual Studio 功能
語言功能 是否能用在向下定向方案中 集合初始值設定項 能 數組初始值設定項 能 自動實現的屬性 能 隱式行繼續符 能 語句 Lambda 能 無 PIA 不能 動態互操作 不能 協變/逆變 部分支持PIA 和互操作
若要使用 .NET Framework 對 Microsoft Office 對象模型進行編程,需要使用主互操作程序集 (PIA)。PIA 必須部署到用戶計算機上,而且通常會很大,因此部署它們是一項費力的工作。
新的類型嵌入功能使得用戶計算機上沒有 PIA 也可部署這些應用程序。做到這一點的方式是通過生成嵌入式互操作類型,來執行對 COM 庫的直接互操作調用。編譯器會對這些類型做出注釋,以便 CLR 平等對待所有嵌入式互操作類型示例。編譯器不會將 PIA 中的所有類型都復制到您的程序集中,而只是復制您實際使用的類型。
此功能在低於 .NET Framework 4 的向下定向方案中不受支持。在使用 Visual Studio IDE 時,這可能不像在向下定向的方案中將引用嵌入式互操作屬性設置為 true 以便使用普通引用那麼明顯。IDE 功能的用戶體驗是程序集將繼續構建,但會還原為標准引用的行為,這將要求部署 PIA。
在命令行中,通常使用 /reference 開關來添加引用。對於嵌入,則要使用 /link 開關。在向下定向的方案中嘗試使用 /link 開關會導致編譯錯誤。
以下是來自 Word 互操作程序集的命令行嵌入式類型:
D:\Windows\Microsoft.NET\Framework\v4.0.30128\Vbc.exe /imports:Microsoft.VisualBasic,System /link:"D:\Program Files\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Word.dll" /reference:"D:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\System.Core.dll","D:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\System.dll" /out:ConsoleApplication16.exe /target:exe Module1.vb
這種行為很重要,因為在默認情況下,添加到 Visual Studio 2010 項目中的 COM 引用會將嵌入互操作屬性設置為 true。因此更改目標框架不應該導致其他錯誤,而會在可能時提供嵌入式互操作類型的優勢。
向下定向方案中不受支持的另一項 Visual Studio 2010 新功能是動態互操作,因為在 Visual Studio 2010 之前,動態語言運行時 (DLR) 還不存在。
其他問題
支持協變和逆變,是為了在用戶定義的接口中使用。但是,針對低級目標的基類庫 (BCL) 接口並未改變,因此不支持使用這些基類所提供的功能。
如果您在 Visual Studio 2010 中打開在早期版本的 Visual Studio 中創建的項目或解決方案,您將看到標准的升級對話框。Visual Studio 會對項目或解決方案文件做出必要的更改,以便在 Visual Studio 2010 中使用。但是,升級文件時的兩項操作會影響多重目標。
如果您已安裝 .NET Framework 3.5 SP1,升級對話框將允許項目或解決方案文件升級到 Visual Studio 2010,但為項目指定的目標框架保持不變。因此如果您升級面向 .NET Framework 3.5 的應用程序,升級之後該程序應該仍舊面向 3.5 框架。
如果您未安裝 .NET Framework 3.5 SP1,則無法正確構建多重目標,因為您需要版本 2.0 的 MSCorlib 和引用程序集。對話框將提供選項,讓您選擇更改為面向版本 4 框架,或者不升級項目。這種情況下的最佳選擇是取消升級,安裝 .NET Framework 3.5 SP1,然後重復該過程以再次升級項目。
通過深入了解 Visual Studio 2010 中 Visual Basic 多重目標的實現細節,您應該可以編寫代碼,使用 IDE 或命令行生成可以部署到舊版本框架的程序集,同時充分利用 Visual Studio 2010 的一些新功能。盡管多重目標有一些需要注意的地方,您還是可以開發和部署不能立即升級到使用 .NET Framework 4 的應用程序。